001 /*
002 Copyright (C) 2003 Adam Olsen
003
004 This program is free software; you can redistribute it and/or modify
005 it under the terms of the GNU General Public License as published by
006 the Free Software Foundation; either version 1, or (at your option)
007 any later version.
008
009 This program is distributed in the hope that it will be useful,
010 but WITHOUT ANY WARRANTY; without even the implied warranty of
011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 GNU General Public License for more details.
013
014 You should have received a copy of the GNU General Public License
015 along with this program; if not, write to the Free Software
016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
017 */
018
019 package com.valhalla.jbother;
020
021 import java.awt.*;
022 import java.awt.event.*;
023 import java.beans.PropertyChangeEvent;
024 import java.beans.PropertyChangeListener;
025 import java.util.Hashtable;
026 import java.util.Locale;
027 import java.util.ResourceBundle;
028
029 import javax.swing.border.*;
030 import javax.swing.*;
031
032 import org.jivesoftware.smack.packet.Presence;
033 import org.jivesoftware.smackx.muc.*;
034
035 import com.valhalla.gui.DialogTracker;
036 import com.valhalla.gui.Standard;
037 import com.valhalla.jbother.groupchat.ChatRoomPanel;
038 import com.valhalla.jbother.groupchat.GroupChatBookmarks;
039 import com.valhalla.jbother.plugins.events.ExitingEvent;
040 import com.valhalla.jbother.jabber.BuddyStatus;
041 import com.valhalla.pluginmanager.PluginChain;
042 import com.valhalla.settings.Settings;
043 import net.infonode.tabbedpanel.*;
044 import net.infonode.tabbedpanel.titledtab.*;
045 import net.infonode.util.*;
046 import net.infonode.tabbedpanel.theme.*;
047 import net.infonode.gui.colorprovider.*;
048 import net.infonode.gui.hover.*;
049
050 /**
051 * Contains all of the groupchat windows in tabs
052 *
053 * @author Adam Olsen
054 * @author Andrey Zakirov
055 * @version 1.1
056 */
057 public class TabFrame extends JFrame {
058 private ResourceBundle resources = ResourceBundle.getBundle(
059 "JBotherBundle", Locale.getDefault());
060
061 private JPanel container = new JPanel(new BorderLayout());
062
063 private TabbedPanel tabPane = new TabbedPanel();
064
065 private TabListener tabListener = null;
066
067 private JMenuBar menuBar = new JMenuBar();
068
069 private JMenu optionMenu = new JMenu(resources.getString("options"));
070
071 private JMenuItem newItem = new JMenuItem(resources.getString("joinRoom")),
072 leaveItem = new JMenuItem(resources.getString("leaveAll")),
073 closeItem = new JMenuItem(resources.getString("closeButton"));
074
075 private Hashtable queueCounts = new Hashtable();
076
077 private GCTabHandler switchListener = new GCTabHandler();
078
079 private WindowAdapter windowListener = null;
080
081 private TabFocusListener tabFocusListener = null;
082
083 private CloseMenu close = new CloseMenu();
084
085 private JSplitPane pane;
086
087 private static boolean tabListenerAdded = false;
088
089 private MyFocusListener focusListener = new MyFocusListener();
090 private javax.swing.Timer focusTimer = new javax.swing.Timer( 50, focusListener );
091 //private ShapedGradientTheme theme = new ShapedGradientTheme();
092 //private SmallFlatTheme theme = new SmallFlatTheme();
093 private ShapedGradientTheme theme = new ShapedGradientTheme(0f, 0f, new FixedColorProvider(new Color(150, 150, 150)),null);
094
095 /**
096 * Constructor sets the frame up and adds a listener to the JTabPane so that
097 * if a tab is changed the title of this frame reflects the topic and the
098 * name of the room in the tab
099 */
100 public TabFrame() {
101 super("JBother");
102
103 setIconImage(Standard.getImage("frameicon.png"));
104
105 setContentPane(container);
106 container.add(tabPane, BorderLayout.CENTER);
107
108 // add the tab switch listener so the title of the frame reflects the
109 // current tab
110 tabListener = new TabAdapter() {
111 public void tabSelected(TabStateChangedEvent e) {
112 if( tabPane.getSelectedTab() == null) return;
113 if (tabPane.getSelectedTab().getContentComponent() != null) {
114 final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
115 setTitle(panel.getWindowTitle());
116 focusComponent( panel.getInputComponent() );
117 clearTab(panel);
118 }
119 }
120 };
121
122 tabFocusListener = new TabFocusListener();
123
124 optionMenu.add(newItem);
125 optionMenu.add(leaveItem);
126
127 addListeners();
128
129 menuBar.add(optionMenu);
130 windowListener = new WindowAdapter() {
131 public void windowClosing(WindowEvent e) {
132 saveStates();
133 closeHandler();
134 }
135 };
136
137 // if they press the close button, we wanna handle leaving of the rooms
138 addWindowListener(windowListener);
139
140 setPreferredLocation();
141 pack();
142
143 String stringWidth = Settings.getInstance().getProperty(
144 "chatFrameWidth");
145 String stringHeight = Settings.getInstance().getProperty(
146 "chatFrameHeight");
147
148 if (stringWidth == null)
149 stringWidth = "635";
150 if (stringHeight == null)
151 stringHeight = "450";
152
153 setSize(new Dimension(Integer.parseInt(stringWidth), Integer
154 .parseInt(stringHeight)));
155 DialogTracker.addDialog(this, true, false);
156
157 addComponentListener(new ComponentAdapter() {
158 public void componentResized(ComponentEvent e) {
159 saveStates();
160 }
161 });
162
163 com.valhalla.Logger.debug("TabFrame is being created");
164
165 addComponentListener(new MoveListener());
166 addTabListeners();
167 }
168
169 /**
170 * When the focus is gained on a tab, this class focuses the input
171 * component for this tab
172 */
173 class TabFocusListener implements FocusListener {
174 public void focusLost(FocusEvent e) {
175 }
176
177 public void focusGained(FocusEvent e) {
178 final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
179 if (panel != null)
180 {
181 focusComponent( panel.getInputComponent() );
182 }
183 if (panel instanceof ConversationPanel)
184 {
185 (((ConversationPanel) panel).getBuddy()).sendNotDisplayedID();
186 }
187 }
188 }
189
190 /**
191 * Focuses the input component on a tab
192 */
193 private void focusComponent( Component comp )
194 {
195 focusListener.setComponent( comp );
196 if( !focusTimer.isRunning() ) focusTimer.start();
197 else focusTimer.restart();
198 }
199
200 /**
201 * Waits a few seconds to focus the new component when a tab is selected
202 */
203 class MyFocusListener implements ActionListener
204 {
205 private Component comp = null;
206 public void setComponent( Component comp ) { this.comp = comp; }
207 public void actionPerformed( ActionEvent e )
208 {
209 SwingUtilities.invokeLater( new Runnable()
210 {
211 public void run() { if( comp != null ) comp.requestFocus(); comp = null; }
212 } );
213
214 //comp = null;
215
216 focusTimer.stop();
217 }
218 }
219
220 /**
221 * Adds event listeners to this tab frame
222 */
223 private void addTabListeners() {
224 tabPane.addTabListener(tabListener);
225 tabPane.addFocusListener(tabFocusListener);
226 KeyboardFocusManager.getCurrentKeyboardFocusManager()
227 .addKeyEventPostProcessor(switchListener);
228
229
230 Direction d = Direction.DOWN;
231 String dSetting = Settings.getInstance().getProperty( "tabOrientation", "Down" );
232 if( dSetting.equals( "Up" ) ) d = Direction.UP;
233 else if( dSetting.equals( "Right" ) ) d = Direction.RIGHT;
234 else if( dSetting.equals( "Left" ) ) d = Direction.LEFT;
235
236 tabPane.getProperties().setTabAreaOrientation(d);
237 tabPane.getProperties().setAutoSelectTab(true);
238 tabPane.getProperties().setTabReorderEnabled(true);
239 tabPane.getProperties().setTabLayoutPolicy(TabLayoutPolicy.COMPRESSION);
240 tabPane.getProperties().setTabDropDownListVisiblePolicy(TabDropDownListVisiblePolicy.MORE_THAN_ONE_TAB);
241 tabPane.getProperties().addSuperObject(theme.getTabbedPanelProperties());
242 }
243
244 /**
245 * Removes all event listeners from the TabbedPanel
246 */
247 public void removeTabListeners() {
248 tabPane.removeTabListener(tabListener);
249 tabPane.removeFocusListener(tabFocusListener);
250
251 KeyboardFocusManager.getCurrentKeyboardFocusManager()
252 .removeKeyEventPostProcessor(switchListener);
253 }
254
255 /**
256 * Docks the BuddyList to this frame
257 * @param list The buddy list to dock
258 */
259 public void dockBuddyList(final BuddyList list) {
260 container.remove(tabPane);
261 pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
262 pane.setResizeWeight(.7);
263
264 String dockWhere = Settings.getInstance().getProperty("dockOption", "Left");
265 if( dockWhere.equals( "Left" ) )
266 {
267 pane.add(list);
268 pane.add(tabPane);
269 }
270 else {
271 pane.add(tabPane);
272 pane.add(list);
273 }
274
275 container.add(pane, BorderLayout.CENTER);
276 removeWindowListener(windowListener);
277 windowListener = new WindowAdapter() {
278 public void windowClosing(WindowEvent e) {
279 saveStates();
280
281 ExitingEvent event = new ExitingEvent(list);
282 PluginChain.fireEvent(event);
283 if (event.getExit()) {
284 closeHandler();
285 list.quitHandler();
286 }
287 }
288 };
289
290 addWindowListener(windowListener);
291
292 String divLocString = Settings.getInstance().getProperty(
293 "dockedBuddyListDivLocation");
294 int divLoc = 150;
295
296 try {
297 if (divLocString != null) {
298 divLoc = Integer.parseInt(divLocString);
299 }
300
301 } catch (NumberFormatException ex) {
302 }
303
304 addComponentListener(new MoveListener());
305
306 pane.setDividerLocation(divLoc);
307 pane.addPropertyChangeListener("lastDividerLocation",
308 new DividerListener());
309
310 validate();
311 }
312
313 /**
314 * Listens for movement in the tab frame, and saves the new position
315 */
316 class MoveListener extends ComponentAdapter {
317 public void componentMoved(ComponentEvent e) {
318 saveStates();
319 }
320 }
321
322 /**
323 * Undocks the BuddyList from this tab frame
324 */
325 public void undock() {
326 container.remove(tabPane);
327 removeTabListeners();
328 TabFrame frame = new TabFrame();
329 frame.setTabPane(tabPane);
330 BuddyList.getInstance().setTabFrame(frame);
331 frame.setVisible(true);
332 dispose();
333 if (tabPane.getTabCount() > 0 && tabPane.getTabAt(0) != null)
334 tabPane.setSelectedTab(tabPane.getTabAt(0));
335 }
336
337 /**
338 * Sets the tab pane to be used for this frame
339 * This is done because unless a new tab pane is created when docking
340 * windows, weird things happen
341 * @param pane the new TabbedPanel
342 */
343 private void setTabPane(TabbedPanel pane) {
344 try {
345 remove(this.tabPane);
346 } catch (Exception e) {
347 }
348 this.tabPane = pane;
349 com.valhalla.Logger.debug("Setting tab pane");
350
351 addTabListeners();
352 getContentPane().add(pane, BorderLayout.CENTER);
353 validate();
354 }
355
356 /**
357 * Listens for the user to move the divider, and saves it's location
358 *
359 * @author Adam Olsen
360 * @version 1.0
361 */
362 private class DividerListener implements PropertyChangeListener {
363 public void propertyChange(PropertyChangeEvent e) {
364 Settings.getInstance().setProperty("dockedBuddyListDivLocation",
365 e.getOldValue().toString());
366 }
367 }
368
369 /**
370 * Marks a tab for a TabFramePanel if it's not already selected
371 *
372 * @param panel
373 * the panel to mark
374 */
375 public void markTab(TabFramePanel panel, boolean messageToMe) {
376 if( tabPane.getSelectedTab() == null ) return;
377 if (tabPane.getSelectedTab().getContentComponent() != panel) {
378 Integer i = (Integer) queueCounts.get(panel);
379 if (i == null)
380 i = new Integer(1);
381
382 int index = tabPane.getTabIndex(panel.getTab());
383 if (index == -1) return;
384
385 Icon icon = Standard.getIcon("images/newmessage.png");
386 if( messageToMe ) icon = Standard.getIcon("images/newhighlight.png");
387
388 TitledTab tab = panel.getTab();
389 String tip = panel.getPanelToolTip();
390 tip = htmlPad("<b>" + tip + "</b><br>" + i + " new messages");
391 tab.setToolTipText(tip);
392 tab.setIcon(icon);
393
394 queueCounts.put(panel, new Integer(i.intValue() + 1));
395 }
396 }
397
398 /**
399 * Returns an html enclosed and 3 pixel padding string for tab tooltips
400 * @return html for tab tooltips
401 */
402 private String htmlPad(String html)
403 {
404 return "<html><div style='padding: 3px;'>" + html + "</div></html>";
405 }
406
407 /**
408 * Clears the tab message queue count
409 */
410 public void clearTab(TabFramePanel panel) {
411
412 String name = panel.getPanelName();
413 TitledTab tab = panel.getTab();
414 if( tab == null ) return;
415
416 tab.setToolTipText(htmlPad(panel.getPanelToolTip()));
417
418 tab.setIcon(Standard.getIcon("images/nomessage.png"));
419 queueCounts.remove(panel);
420 if (panel instanceof ChatRoomPanel) {
421 ((ChatRoomPanel) panel).resetMessageToMe();
422 }
423 }
424
425 /**
426 * Saves the size of the chat frame
427 */
428 public void saveStates() {
429 if (isVisible()) {
430 Point location = new Point(getLocationOnScreen());
431 Settings.getInstance().setProperty("tabFrameX",
432 new Double(location.getX()).toString());
433 Settings.getInstance().setProperty("tabFrameY",
434 new Double(location.getY()).toString());
435 } else {
436 com.valhalla.Logger.debug("TabFrame is not visible");
437 }
438
439 Dimension size = getSize();
440 Integer width = new Integer((int) size.getWidth());
441 Integer height = new Integer((int) size.getHeight());
442 Settings.getInstance().setProperty("chatFrameWidth", width.toString());
443 Settings.getInstance()
444 .setProperty("chatFrameHeight", height.toString());
445 }
446
447 /**
448 * Switches the current tab in the tab frame
449 */
450 public void switchTab(TabbedPanel tabPane) {
451 com.valhalla.Logger.debug("Switching the tab");
452 int current = tabPane.getTabIndex(tabPane.getSelectedTab());
453 current++;
454 if (current >= tabPane.getTabCount())
455 current = 0;
456 tabPane.setSelectedTab(tabPane.getTabAt(current));
457 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(current).getContentComponent();
458 if (panel != null)
459 focusComponent( panel.getInputComponent() );
460 }
461
462 /**
463 * Loads the saved settings from any previous settings
464 */
465 private void setPreferredLocation() {
466 //load the settings from the settings file
467 String xString = Settings.getInstance().getProperty("tabFrameX");
468 String yString = Settings.getInstance().getProperty("tabFrameY");
469
470 if (yString == null)
471 yString = "100";
472 if (xString == null)
473 xString = "100";
474
475 double x = 100;
476 double y = 100;
477
478 try {
479 x = Double.parseDouble(xString);
480 y = Double.parseDouble(yString);
481 } catch (NumberFormatException e) {
482 com.valhalla.Logger.logException(e);
483 }
484
485 if (x < -50.0)
486 x = 100.0;
487 if (y < -50.0)
488 y = 100.0;
489
490 setLocation((int) x, (int) y);
491 }
492
493 /**
494 * Adds the various event listeners
495 *
496 * @author Adam Olsen
497 * @version 1.0
498 */
499 private void addListeners() {
500 MenuItemListener listener = new MenuItemListener();
501 newItem.addActionListener(listener);
502 leaveItem.addActionListener(listener);
503 }
504
505 public void addFrameListener (BuddyStatus buddy)
506 {
507 final BuddyStatus buddy2=buddy;
508 addWindowFocusListener ( new WindowFocusListener() {
509 public void windowGainedFocus (WindowEvent e) {
510 SwingUtilities.invokeLater(new Runnable() {
511 public void run() {
512 buddy2.sendNotDisplayedID();
513 }
514 });
515 }
516 public void windowLostFocus(WindowEvent e) {
517 }
518 });
519
520 }
521
522 /**
523 * Listens for a menu item to be clicked
524 *
525 * @author Adam Olsen
526 * @version 1.0
527 */
528 private class MenuItemListener implements ActionListener {
529 public void actionPerformed(ActionEvent e) {
530 if (e.getSource() == newItem)
531 new GroupChatBookmarks(TabFrame.this).setVisible(true);
532 if (e.getSource() == leaveItem)
533 closeHandler();
534 }
535 }
536
537 /**
538 * Updates the font in all the chat conversationareas
539 *
540 * @param font
541 * the font to update to
542 */
543 public void updateStyles(Font font) {
544 for (int i = 0; i < tabPane.getTabCount(); i++) {
545 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
546 panel.updateStyle(font);
547 }
548 }
549
550 /**
551 * Set the status in all the rooms
552 *
553 * @param mode
554 * the presence mode
555 * @param status
556 * the status string
557 */
558 public void setStatus(Presence.Mode mode, String status) {
559 for (int i = 0; i < tabPane.getTabCount(); i++) {
560 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
561 if (panel instanceof ChatRoomPanel) {
562 ChatRoomPanel window = (ChatRoomPanel) panel;
563 MultiUserChat chat = window.getChat();
564 if( chat == null || !chat.isJoined() ) continue;
565
566 //set up a packet to be sent to my user in every groupchat
567 Presence presence = new Presence(Presence.Type.AVAILABLE,
568 status, 0, mode);
569 presence.setTo(window.getRoomName() + '/'
570 + window.getNickname());
571
572 if (!BuddyList.getInstance().checkConnection()) {
573 BuddyList.getInstance().connectionError();
574 return;
575 }
576
577 BuddyList.getInstance().getConnection().sendPacket(presence);
578 }
579 }
580 }
581
582 /**
583 * This not only closes the window, but it leaves all the rooms like it
584 * should
585 */
586 public void closeHandler() {
587 removeTabListeners();
588 leaveAll();
589 }
590
591 /**
592 * Since there is no way to check to see if a message is from someone in a
593 * chat room, we check to see if the message is coming from the same server
594 * as a chatroom we are in.
595 *
596 * @param server
597 * the server to check
598 */
599 public boolean isRoomOpen(String server) {
600 for (int i = 0; i < tabPane.getTabCount(); i++) {
601 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
602 if (panel instanceof ChatRoomPanel) {
603 ChatRoomPanel window = (ChatRoomPanel) panel;
604 if (server.toLowerCase().equals(
605 window.getRoomName().toLowerCase()))
606 return true;
607 }
608 }
609
610 return false;
611 }
612
613 /**
614 * If there is a chatroom open in this frame with a server name, this
615 * returns the ChatRoomPanel that contains it
616 *
617 * @param server
618 * the name of the room to get the ChatRoomPanel for
619 * @return the ChatRoomPanel requested, or <tt>null</tt> if it could not
620 * be found
621 */
622 public ChatRoomPanel getChatPanel(String server) {
623 for (int i = 0; i < tabPane.getTabCount(); i++) {
624 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
625 if (panel instanceof ChatRoomPanel) {
626 ChatRoomPanel window = (ChatRoomPanel) panel;
627 if (server.toLowerCase().equals(
628 window.getRoomName().toLowerCase()))
629 return window;
630 }
631 }
632
633 return null;
634 }
635
636 /**
637 * This leaves a chatroom and removes the associated ChatRoomPanel from the
638 * TabPane
639 *
640 * @param window
641 * the room to leave
642 */
643 public void removePanel(TabFramePanel panel) {
644 queueCounts.remove(panel);
645 TitledTab tab = panel.getTab();
646 tab.setHighlightedStateTitleComponent(null);
647 tab.setNormalStateTitleComponent(null);
648 tab.setDisabledStateTitleComponent(null);
649 tab.getProperties().setHoverListener(null);
650
651 tabPane.removeTab(tab);
652 tabPane.validate();
653 if(panel instanceof ConversationPanel) MessageDelegator.getInstance().removePanel((ConversationPanel)panel);
654
655 if (panel instanceof ChatRoomPanel) {
656 ((ChatRoomPanel) panel).leave();
657 ((ChatRoomPanel) panel).removed();
658 panel = null;
659 }
660
661 try {
662 panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
663 }
664 catch( NullPointerException npe ) { panel = null; }
665
666 if (panel != null) {
667 setTitle(panel.getWindowTitle());
668 TitledTab t = panel.getTab();
669 t.setText(panel.getPanelName());
670 } else {
671 setTitle("JBother");
672 }
673
674 BuddyList.getInstance().stopTabFrame();
675 }
676
677 /**
678 * Sets the subject of a ChatRoomPanel based on a message that was received
679 * from the GroupChat server with <subject> in it
680 *
681 * @param window
682 * the window to set the subject for
683 */
684 public void setSubject(ChatRoomPanel window) {
685 if( tabPane.getSelectedTab() == null) return;
686 if (!(tabPane.getSelectedTab().getContentComponent() instanceof ChatRoomPanel))
687 return;
688 if ((ChatRoomPanel) tabPane.getSelectedTab().getContentComponent() == window) {
689 setTitle(resources.getString("groupChat") + ": "
690 + window.getRoomName());
691 validate();
692 }
693 }
694
695 /**
696 * @param panel
697 * the panel to check
698 * @return true if the tab panel is currently displayed in the tab frame
699 */
700 public boolean contains(TabFramePanel panel) {
701 for (int i = 0; i < tabPane.getTabCount(); i++) {
702 TabFramePanel p = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
703 if (p == panel)
704 return true;
705 }
706
707 return false;
708 }
709
710 /**
711 * Returns the number of rooms currently open in the frame
712 *
713 * @return the number of rooms still open
714 */
715 public int tabsLeft() {
716 return tabPane.getTabCount();
717 }
718
719 /**
720 * Adds a chat room to the frame
721 *
722 * @param window
723 * the room to add
724 */
725 public void addPanel(final TabFramePanel panel) {
726 panel.setListenersAdded(true);
727
728 String name = panel.getPanelName();
729 String orient = Settings.getInstance().getProperty("tabOrientation", "Down");
730
731 final TitledTab tab = new TitledTab( name, Standard.getIcon("images/nomessage.png"),
732 (JComponent) panel, null );
733 tab.setToolTipText(htmlPad(panel.getPanelToolTip()));
734
735 tab.getProperties().addSuperObject(theme.getTitledTabProperties());
736
737 CloseButton b = new CloseButton(tab);
738 final CloseButton temp = b;
739 tab.setHighlightedStateTitleComponent(b);
740
741 if( !Settings.getInstance().getBoolean("closeButtonOnAll")) b = null;
742 tab.setNormalStateTitleComponent(b);
743 tab.setDisabledStateTitleComponent(b);
744 tab.addMouseListener(new MouseAdapter()
745 {
746 public void mousePressed(MouseEvent e)
747 {
748 if(e.isPopupTrigger())
749 {
750 close.setTab(tab);
751 close.show(tab, e.getX(), e.getY());
752 }
753
754 }
755
756 } );
757
758 tab.getProperties().setHoverListener( new HoverListener()
759 {
760 public void mouseEntered(HoverEvent e)
761 {
762 if(Settings.getInstance().getBoolean("closeButtonOnAll")) return;
763 tab.setNormalStateTitleComponent(temp);
764 tab.setDisabledStateTitleComponent(temp);
765 tab.validate();
766 }
767
768 public void mouseExited(HoverEvent e)
769 {
770 if(Settings.getInstance().getBoolean("closeButtonOnAll")) return;
771 tab.setNormalStateTitleComponent(null);
772 tab.setDisabledStateTitleComponent(null);
773 tab.validate();
774 }
775 } );
776
777
778
779 tabPane.addTab( tab );
780 panel.setTab( tab );
781 if (panel instanceof ChatRoomPanel)
782 tabPane.setSelectedTab(tab);
783 }
784
785 /**
786 * Revalidates the close button on a tab
787 * @param selected whether or not this tab is selected
788 */
789 public void resetCloseButtons(boolean selected)
790 {
791 for( int i = 0; i < tabPane.getTabCount(); i++ )
792 {
793 TitledTab tab = (TitledTab)tabPane.getTabAt(i);
794 CloseButton button = new CloseButton(tab);
795 tab.setHighlightedStateTitleComponent(button);
796 if( !selected) button = null;
797
798 tab.setNormalStateTitleComponent(button);
799 tab.setDisabledStateTitleComponent(button);
800 tab.validate();
801 }
802 }
803
804
805 /**
806 * Switches the tab based on CTRL+n keys
807 */
808 class GCTabHandler implements KeyEventPostProcessor {
809 boolean first = false;
810
811 public boolean postProcessKeyEvent(KeyEvent e) {
812 Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager()
813 .getFocusedWindow();
814
815 if (!(w instanceof TabFrame))
816 return false;
817 TabbedPanel tabPane = ((TabFrame) w).getTabPane();
818
819 // get the ASCII character code for the character typed
820 int numPressed = (int) e.getKeyChar();
821
822 int mask = KeyEvent.CTRL_MASK;
823 if (System.getProperty("mrj.version") != null) {
824 mask = KeyEvent.META_DOWN_MASK;
825 }
826
827 // the integer characters start at ASCII table number 49, so we
828 // subtract 49
829 numPressed -= 49;
830
831 // if the new ASCII value is between 0 and 8, then the
832 // key pressed was 1 through 9 - which is what we want
833 // also check that the CTRL key was being held down
834 if ((numPressed >= 0 && numPressed <= 8)
835 && (e.getModifiers() & mask) == Toolkit.getDefaultToolkit()
836 .getMenuShortcutKeyMask()) {
837 e.consume();
838
839 if (tabPane.getTabCount() >= numPressed)
840 tabPane.setSelectedTab(tabPane.getTabAt(numPressed));
841 } else if (e.getKeyCode() == KeyEvent.VK_TAB
842 && (e.getModifiers() & mask) == Toolkit.getDefaultToolkit()
843 .getMenuShortcutKeyMask()) {
844 if (first == false) {
845 first = true;
846 } else {
847 switchTab(tabPane);
848 first = false;
849
850 final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
851 focusComponent( panel.getInputComponent() );
852
853 }
854 }
855
856 return true;
857 }
858 }
859
860 /**
861 * Leaves all chatrooms (for if they close the window)
862 */
863 public void leaveAll() {
864 com.valhalla.Logger.debug("There are " + tabPane.getTabCount()
865 + " rooms");
866
867 int tabCount = tabPane.getTabCount();
868 for (int i = 0; i < tabCount; i++) {
869 TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(0).getContentComponent();
870
871 if (panel instanceof ChatRoomPanel) {
872 ChatRoomPanel window = (ChatRoomPanel) panel;
873 //if this frame is closed as a result of connection loss and we
874 // try to leave
875 //the channel, it will not work, so we need to catch it.
876 window.removed();
877 try {
878 window.leave();
879
880 } catch (IllegalStateException e) {
881 com.valhalla.Logger
882 .debug("Caught Illegal State Exception when leaving window: "
883 + window.toString());
884 }
885
886 BuddyList.getInstance().removeTabPanel(panel);
887 } else {
888 ((ConversationPanel) panel).checkCloseHandler();
889 }
890 }
891
892 BuddyList.getInstance().stopTabFrame();
893 }
894
895 /**
896 * @return Returns the tabPane.
897 */
898 public TabbedPanel getTabPane() {
899 return tabPane;
900 }
901
902 class CloseMenu extends JPopupMenu
903 {
904 JMenuItem closeItem = new JMenuItem(resources.getString("closeButton"));
905 JMenuItem closeAll = new JMenuItem(resources.getString("closeAllButton"));
906 private TitledTab tab;
907 public CloseMenu()
908 {
909 add(closeItem);
910 add(closeAll);
911
912 closeItem.addActionListener(new ActionListener()
913 {
914 public void actionPerformed(ActionEvent e)
915 {
916 if( tab == null ) return;
917 TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
918
919 if (panel instanceof ConversationPanel) {
920 ((ConversationPanel) panel).checkCloseHandler();
921 } else {
922 removePanel(panel);
923 }
924 }
925
926 } );
927
928 closeAll.addActionListener(new ActionListener()
929 {
930 public void actionPerformed(ActionEvent e)
931 {
932 int count = tabPane.getTabCount();
933 for( int i = 0; i < count; i++ )
934 {
935 TitledTab tab = (TitledTab)tabPane.getTabAt(0);
936 TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
937
938 if (panel instanceof ConversationPanel) {
939 ((ConversationPanel) panel).checkCloseHandler();
940 } else {
941 removePanel(panel);
942 }
943 }
944 }
945
946 } );
947 }
948
949 public void setTab(TitledTab tab)
950 {
951 this.tab = tab;
952 }
953 }
954
955
956 class CloseButton extends JLabel
957 {
958 TitledTab tab;
959 public CloseButton( final TitledTab tab )
960 {
961 super(Standard.getIcon("images/buttons/close.png"));
962 setPreferredSize( new Dimension( 15, 8 ) );
963 this.tab = tab;
964 setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
965 setToolTipText( resources.getString( "closeButton" ) );
966
967 addMouseListener( new MouseAdapter()
968 {
969 public void mouseEntered( MouseEvent e )
970 {
971 setBorder(BorderFactory.createEtchedBorder());
972 validate();
973 }
974
975 public void mouseExited( MouseEvent e )
976 {
977 setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
978 validate();
979 }
980
981 public void mouseClicked( MouseEvent e )
982 {
983 TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
984
985 if (panel instanceof ConversationPanel) {
986 ((ConversationPanel) panel).checkCloseHandler();
987 } else {
988 removePanel(panel);
989 }
990
991 }
992
993 } );
994 }
995
996 }
997 }