001    /*
002     *  Copyright (C) 2003 Adam Olsen
003     *  This program is free software; you can redistribute it and/or modify
004     *  it under the terms of the GNU General Public License as published by
005     *  the Free Software Foundation; either version 1, or (at your option)
006     *  any later version.
007     *  This program is distributed in the hope that it will be useful,
008     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
009     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
010     *  GNU General Public License for more details.
011     *  You should have received a copy of the GNU General Public License
012     *  along with this program; if not, write to the Free Software
013     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
014     */
015    package com.valhalla.jbother;
016    
017    import java.awt.*;
018    import java.awt.event.*;
019    import java.io.BufferedReader;
020    import java.io.File;
021    import java.io.FileReader;
022    import java.io.IOException;
023    import java.util.*;
024    import java.lang.reflect.Array;
025    
026    import javax.swing.*;
027    import com.valhalla.jbother.actions.*;
028    
029    import org.jivesoftware.smack.XMPPConnection;
030    import org.jivesoftware.smack.packet.*;
031    
032    import com.valhalla.gui.DialogTracker;
033    import com.valhalla.gui.Standard;
034    import com.valhalla.jbother.groupchat.ChatRoomPanel;
035    import com.valhalla.jbother.jabber.BuddyStatus;
036    import com.valhalla.jbother.jabber.ParsedBuddyInfo;
037    import com.valhalla.jbother.jabber.smack.SecureExtension;
038    import com.valhalla.jbother.menus.BuddyListBuddiesMenu;
039    import com.valhalla.jbother.menus.SetStatusMenu;
040    import com.valhalla.jbother.plugins.events.ConnectEvent;
041    import com.valhalla.jbother.plugins.events.StatusChangedEvent;
042    import com.valhalla.misc.GnuPG;
043    import com.valhalla.pluginmanager.PluginChain;
044    import com.valhalla.settings.*;
045    import net.infonode.tabbedpanel.*;
046    import net.infonode.tabbedpanel.titledtab.*;
047    import net.infonode.util.*;
048    
049    /**
050     *  BuddyList is the main controller for the buddy list, as as the buddy list is
051     *  the main component of the IM application it performs most of the work once
052     *  it's been initialized.
053     *
054     *@author     Adam Olsen
055     *@author     Andrey Zakirov
056     *@created    October 26, 2004
057     *@update     April 10, 2005
058     *@version    1.0
059     */
060    public class BuddyList extends JPanel
061    {
062        private static BuddyList singleton = null;
063    
064        private static JFrame frame;
065    
066        private ResourceBundle resources = ResourceBundle.getBundle(
067                "JBotherBundle", Locale.getDefault() );
068    
069        private XMPPConnection connection;
070    
071        //the connection to the jabber server
072        private BuddyListTree buddyListTree;
073    
074        private boolean signoff = false;
075    
076        private TabFrame tabFrame;
077    
078        private Hashtable buddyStatuses = null;
079    
080        private Presence.Mode currentMode = null;
081    
082        private String currentStatusString = resources.getString( "available" );
083    
084        private AwayHandler awayHandler = new AwayHandler();
085    
086        private javax.swing.Timer awayTimer = null;
087    
088       // private javax.swing.Timer pingTimer = new javax.swing.Timer( 300000,
089        //    new PingHandler() );
090    
091        private boolean idleAway = false;
092    
093        private Hashtable blockedUsers = new Hashtable();
094    
095        private static BuddyList buddyList = null;
096    
097        private boolean docked = false;
098    
099        private String gnupgPassword = null;
100    
101        private boolean encrypting = false;
102    
103        private Hashtable notDeliveredHash = new Hashtable();
104    
105        private Hashtable notDisplayedHash = new Hashtable();
106    
107        private Hashtable notOfflineHash = new Hashtable();
108    
109        private JButton jbButton = new JButton();
110    
111        private JButton statusButton = new JButton();
112    
113        private BuddyListBuddiesMenu jbMenu = new BuddyListBuddiesMenu( this );
114    
115        private SetStatusMenu statusMenu = new SetStatusMenu( this, true, statusButton );
116    
117        private long date = new Date().getTime();
118        private JToggleButton showOffline = new JToggleButton( StatusIconCache.getStatusIcon( (Presence.Mode)null ) );
119        private JToggleButton showAway = new JToggleButton( StatusIconCache.getStatusIcon( Presence.Mode.AWAY ) );
120        private JToggleButton sound = new JToggleButton(Standard.getIcon("images/buttons/speaker.png"));
121        private boolean added = false;
122    
123    
124        /**
125         *  Constructor for the buddy list. BuddyList is a singleton, so it's
126         *  constructor is private
127         */
128        private BuddyList()
129        {
130            initComponents();
131        }
132    
133    
134        /**
135         *  Gets the containerFrame attribute of the BuddyList object
136         *
137         *@return    The containerFrame value
138         */
139        public JFrame getContainerFrame()
140        {
141            return frame;
142        }
143    
144    
145        /**
146         *  Gets the BuddyList singleton
147         *
148         *@return        the BuddyList singleton
149         *@deprecated    Use getInstance() instead
150         */
151        public static BuddyList getSingleton()
152        {
153            return getInstance();
154        }
155    
156    
157        /**
158         *  Gets the BuddyList singleton
159         *
160         *@return    the BuddyList singleton
161         */
162        public static BuddyList getInstance()
163        {
164            if( singleton == null )
165            {
166                singleton = new BuddyList();
167            }
168            return singleton;
169        }
170    
171        /*class PingHandler implements ActionListener
172        {
173            public void actionPerformed( ActionEvent e )
174            {
175                if( !checkConnection() ) return;
176    
177                if( idleAway )
178                {
179                    BuddyList.getInstance().setStatus(Presence.Mode.AWAY,
180                            resources.getString("autoAway"), false);
181                }
182                else {
183                    BuddyList.getInstance().setStatus(currentMode,currentStatusString,false);
184                }
185            }
186        }*/
187    
188        public void updateButtons( String mode )
189        {
190            ImageIcon icon = Standard.getIcon("imagethemes/statusicons/"
191                        + mode + "/offline.png");
192    
193            showOffline.setIcon( icon );
194    
195            icon = Standard.getIcon("imagethemes/statusicons/"
196                        + mode + "/away.png");
197    
198            showAway.setIcon( icon );
199            showOffline.validate();
200            showAway.validate();
201        }
202    
203        public void resetAwayTimer()
204        {
205            int aTime = 0;
206    
207            try {
208                aTime = Integer.parseInt(Settings.getInstance().getProperty("autoAwayMinutes", "15"));
209            }
210            catch( Exception e ) { }
211    
212            resetAwayTimer( aTime );
213        }
214    
215        /**
216         *  Starts the away timer
217         */
218        public void resetAwayTimer( int aTime )
219        {
220            com.valhalla.Logger.debug( "Away timer is set for " + ( aTime * 60 * 1000 ) );
221            if( aTime > 0 )
222            {
223                if( awayTimer != null ) awayTimer.stop();
224                awayTimer = new javax.swing.Timer(aTime * 60 * 1000, awayHandler);
225                awayTimer.start();
226                com.valhalla.Logger.debug( "Starting away timer " + aTime);
227            }
228            else if( awayTimer != null )
229            {
230                awayTimer.stop();
231                awayTimer = null;
232            }
233    
234            if( !added )
235            {
236                added = true;
237                AWTEventListener listener = new MyAWTEventListener();
238                Toolkit.getDefaultToolkit().addAWTEventListener( listener,
239                        AWTEvent.KEY_EVENT_MASK );
240            }
241        }
242    
243    
244        /**
245         *  Loads the blocked users information
246         */
247        protected void loadBlockedUsers()
248        {
249            File file = new File( JBother.profileDir + File.separatorChar
250                     + "blocked" );
251            String line = "";
252    
253            try
254            {
255                FileReader fr = new FileReader( file );
256                BufferedReader in = new BufferedReader( fr );
257    
258                while( ( line = in.readLine() ) != null )
259                {
260                    blockedUsers.put( line, "blocked" );
261                    com.valhalla.Logger.debug( "Blocked> " + line );
262                }
263    
264                fr.close();
265            }
266            catch( IOException e )
267            {
268                com.valhalla.Logger.debug( "Blocked users file was not found or could not be read." );
269                return;
270            }
271        }
272    
273    
274        /**
275         *  Description of the Method
276         */
277        private void loadNotDeliveredMessages()
278        {
279            final String notDeliveredMessages = Settings.getInstance().getProperty( "notDeliveredMessages" );
280            String[] notDeliveredMessage;
281            String[] notDeliveredMessageLast;
282            String[] info = new String[2];
283            if( notDeliveredMessages != null )
284            {
285                notDeliveredMessage = notDeliveredMessages.split( ";" );
286                if( notDeliveredMessage.length > 0 )
287                {
288                    for( int i = 0; i < notDeliveredMessage.length; i++ )
289                    {
290                        notDeliveredMessageLast = notDeliveredMessage[i].split( "," );
291                        if( notDeliveredMessageLast.length == 3 )
292                        {
293                            info[0] = notDeliveredMessageLast[1];
294                            info[1] = notDeliveredMessageLast[2];
295                            notDeliveredHash.put( notDeliveredMessageLast[0], info );
296                        }
297                    }
298                }
299            }
300        }
301    
302    
303        /**
304         *  Gets the eventMessage attribute of the BuddyList object
305         *
306         *@param  messageId  Description of the Parameter
307         *@param  type       Description of the Parameter
308         *@return            The eventMessage value
309         */
310        public String[] getEventMessage( String messageId, int type )
311        {
312            Hashtable tempHash;
313            switch ( type )
314            {
315                case 1:
316                    tempHash = notDeliveredHash;
317                    break;
318                case 2:
319                    tempHash = notDisplayedHash;
320                    break;
321                case 3:
322                    tempHash = notOfflineHash;
323                    break;
324                default:
325                    return null;
326            }
327            String[] eventMessage = (String[])tempHash.get( messageId );
328            tempHash.remove( messageId );
329            return eventMessage;
330        }
331    
332    
333        /**
334         *  Description of the Method
335         *
336         *@param  messageId  Description of the Parameter
337         *@param  userJID    Description of the Parameter
338         *@param  timeStamp  Description of the Parameter
339         *@param  type       Description of the Parameter
340         *@return            Description of the Return Value
341         */
342        public boolean putEventMessage( String messageId, String userJID, String timeStamp, int type )
343        {
344            Hashtable tempHash;
345            switch ( type )
346            {
347                case 1:
348                    tempHash = notDeliveredHash;
349                    break;
350                case 2:
351                    tempHash = notDisplayedHash;
352                    break;
353                case 3:
354                    tempHash = notOfflineHash;
355                    break;
356                default:
357                    return false;
358            }
359            if( ( messageId != null ) && ( userJID != null ) && ( timeStamp != null ) )
360            {
361                String[] info = new String[2];
362                info[0] = userJID;
363                info[1] = timeStamp;
364                tempHash.put( messageId, info );
365                return true;
366            }
367            return false;
368        }
369    
370    
371        /**
372         *  Gets the statusButton attribute of the BuddyList object
373         *
374         *@return    The statusButton value
375         */
376        public JButton getStatusButton()
377        {
378            return statusButton;
379        }
380    
381    
382        /**
383         *  Returns the away timer
384         *
385         *@return    the away timer
386         */
387        public javax.swing.Timer getAwayTimer()
388        {
389            return awayTimer;
390        }
391    
392    
393        /**
394         *  Sets the idleAway attribute of the BuddyList object
395         *
396         *@param  idleAway  The new idleAway value
397         */
398        public void setIdleAway( boolean idleAway )
399        {
400            this.idleAway = idleAway;
401        }
402    
403    
404        /**
405         *  Gets the idleAway attribute of the BuddyList object
406         *
407         *@return    The idleAway value
408         */
409        public boolean getIdleAway()
410        {
411            return idleAway;
412        }
413    
414    
415        /**
416         *  Gets the awayHandler attribute of the BuddyList object
417         *
418         *@return    The awayHandler value
419         */
420        public AwayHandler getAwayHandler()
421        {
422            return awayHandler;
423        }
424    
425    
426        /**
427         *  Sets up the current connection
428         *
429         *@param  connection  the current connection
430         */
431        public void init( XMPPConnection connection )
432        {
433            this.connection = connection;
434            if( connection == null )
435            {
436                return;
437            }
438            if( buddyStatuses == null )
439            {
440                buddyStatuses = new Hashtable();
441            }
442    
443            ConnectEvent event = new ConnectEvent( connection );
444            PluginChain.fireEvent( event );
445    
446            buddyListTree.setConnection( connection );
447            statusMenu.setIcon( Presence.Mode.AVAILABLE );
448            //pingTimer.start();
449        }
450    
451    
452        /**
453         *  Clears the buddy tree
454         */
455        public void clearTree()
456        {
457            buddyListTree.clearBuddies();
458        }
459    
460    
461        /**
462         *  initializes the buddy tree by loading the offline buddies
463         */
464        public void initBuddies()
465        {
466            buddyListTree.loadOfflineBuddies();
467        }
468    
469    
470        /**
471         *  Sets the current presence mode
472         *
473         *@param  mode  the mode to set it to
474         */
475        public void setCurrentPresenceMode( Presence.Mode mode )
476        {
477            this.currentMode = mode;
478        }
479    
480    
481        /**
482         *  Sets the current status string
483         *
484         *@param  string  the string to use
485         */
486        public void setCurrentStatusString( String string )
487        {
488            this.currentStatusString = string;
489        }
490    
491    
492        /**
493         *  Gets the current presence mode
494         *
495         *@return    the current presence mode
496         */
497        public Presence.Mode getCurrentPresenceMode()
498        {
499            return this.currentMode;
500        }
501    
502    
503        /**
504         *  Returns the current status string
505         *
506         *@return    the current status string
507         */
508        public String getCurrentStatusString()
509        {
510            return this.currentStatusString;
511        }
512    
513    
514        /**
515         *  Returns the group chat frame
516         *
517         *@return    the group chat frame
518         */
519        public TabFrame getTabFrame()
520        {
521            return tabFrame;
522        }
523    
524    
525        /**
526         *  Starts the group chat frame
527         */
528        public void startTabFrame()
529        {
530            if( tabFrame == null )
531            {
532                tabFrame = new TabFrame();
533            }
534        }
535    
536    
537        /**
538         *  checks to see if there are no more chat room windows in the
539         *  groupchatframe if there are no more, the groupchatframe is destroyed
540         */
541        public void stopTabFrame()
542        {
543            if( tabFrame == null )
544            {
545                return;
546            }
547    
548            if( tabFrame.tabsLeft() <= 0 )
549            {
550                tabFrame.saveStates();
551    
552                if( !docked )
553                {
554                    tabFrame.removeTabListeners();
555                    DialogTracker.removeDialog( tabFrame );
556                    tabFrame = null;
557                    com.valhalla.Logger.debug( "Removing chat frame" );
558                }
559            }
560        }
561    
562    
563        /**
564         *  Removes a ChatPanel
565         *
566         *@param  panel  the chat panel to remove
567         */
568        public void removeTabPanel( TabFramePanel panel )
569        {
570            if( tabFrame == null )
571            {
572                return;
573            }
574    
575            tabFrame.removePanel( panel );
576            stopTabFrame();
577        }
578    
579    
580        /**
581         *  Adds a chat room window to the groupchat frame. If there is not
582         *  groupchat frame one is created
583         *
584         *@param  panel  The feature to be added to the TabPanel attribute
585         */
586        public void addTabPanel( TabFramePanel panel )
587        {
588            if( tabFrame == null )
589            {
590                tabFrame = new TabFrame();
591            }
592            tabFrame.addPanel( panel );
593    
594            if( !tabFrame.isVisible() )
595            {
596                tabFrame.setVisible( true );
597                tabFrame.toFront();
598            }
599        }
600    
601    
602        /**
603         *  Gets the buddy status
604         *
605         *@param  userId  the user id of the BuddyStatus
606         *@return         The buddyStatus value
607         */
608        public BuddyStatus getBuddyStatus( String userId )
609        {
610            if( userId == null )
611            {
612                return null;
613            }
614            if( buddyStatuses == null )
615            {
616                buddyStatuses = new Hashtable();
617            }
618            BuddyStatus buddy = new BuddyStatus( userId );
619    
620            if( buddyStatuses.get( userId.toLowerCase() ) != null )
621            {
622                buddy = (BuddyStatus)buddyStatuses.get( userId.toLowerCase() );
623            }
624            else
625            {
626                buddy.newPubKey();
627                buddyStatuses.put( userId.toLowerCase(), buddy );
628            }
629    
630            return buddy;
631        }
632    
633    
634        /**
635         *  Returns all the buddy status that are available
636         *
637         *@return    the Hashtable containing all the buddy statuses
638         */
639        public Hashtable getBuddyStatuses()
640        {
641            return this.buddyStatuses;
642        }
643    
644    
645        /**
646         *  Returns the current connection
647         *
648         *@return    the current connection
649         */
650        public XMPPConnection getConnection()
651        {
652            return this.connection;
653        }
654    
655    
656        /**
657         *  Checks to see if a connection is active
658         *
659         *@return    true if the connection is active (connected)
660         */
661        public boolean checkConnection()
662        {
663            if( connection == null || !connection.isConnected() )
664            {
665                return false;
666            }
667            else
668            {
669                return true;
670            }
671        }
672    
673    
674        /**
675         *  Displays a generic connection error
676         */
677        public void connectionError()
678        {
679            Standard.warningMessage( null, resources.getString( "connectionError" ),
680                    resources.getString( "notConnected" ) );
681        }
682    
683    
684        /**
685         *  Gets the docked attribute of the BuddyList object
686         *
687         *@return    The docked value
688         */
689        public boolean isDocked()
690        {
691            return docked;
692        }
693    
694    
695        /**
696         *  Description of the Method
697         */
698        public void dockYourself()
699        {
700            if( frame != null )
701            {
702                frame.remove( this );
703                frame.dispose();
704            }
705    
706            if( tabFrame == null )
707            {
708                tabFrame = new TabFrame();
709            }
710            tabFrame.dockBuddyList( this );
711    //        tabFrame.setJMenuBar(topMenu);
712            frame = tabFrame;
713            frame.setVisible( true );
714            docked = true;
715        }
716    
717    
718        /**
719         *  Sets the tabFrame attribute of the BuddyList object
720         *
721         *@param  frame  The new tabFrame value
722         */
723        public void setTabFrame( TabFrame frame )
724        {
725            this.tabFrame = frame;
726        }
727    
728    
729        /**
730         *  Description of the Method
731         */
732        public void undock()
733        {
734            docked = false;
735            if( tabFrame != null )
736            {
737                tabFrame.undock();
738                stopTabFrame();
739            }
740    
741            if( tabFrame != null )
742            {
743                tabFrame.setVisible( true );
744            }
745    
746            BuddyListContainerFrame f = new BuddyListContainerFrame( this );
747            frame = f;
748            frame.setVisible( true );
749        }
750    
751    
752        /**
753         *  Loads the settings for this profile
754         */
755        public void loadSettings()
756        {
757            ConnectorThread.getInstance().resetCredentials();
758            buddyStatuses = new Hashtable();
759            loadBlockedUsers();
760            loadNotDeliveredMessages();
761            gnupgPassword = null;
762    
763            Vector panels = MessageDelegator.getInstance().getPanels();
764            int size = panels.size();
765            for( int i = 0; i < size; i++ )
766            {
767                ConversationPanel panel = (ConversationPanel)panels.get( 0 );
768                panel.closeHandler();
769            }
770    
771            panels.removeAllElements();
772    
773            if( tabFrame != null )
774            {
775                tabFrame.dispose();
776                tabFrame = null;
777            }
778    
779            if( Settings.getInstance().getBoolean( "useTabbedWindow" )
780                     && Settings.getInstance().getBoolean( "dockBuddyList" ) )
781            {
782                dockYourself();
783            }
784            else if( docked )
785            {
786                docked = false;
787            }
788    
789            if( !docked )
790            {
791                BuddyListContainerFrame f = new BuddyListContainerFrame( this );
792                frame = f;
793            }
794    
795            frame.setVisible( true );
796    
797            statusMenu.reloadStatusIcons();
798            statusMenu.setModeChecked( null );
799    
800            if( Arguments.getInstance().getBoolean( "xmlconsole" ) )
801            {
802    
803                ParsedBuddyInfo info = new ParsedBuddyInfo( resources.getString( "xmlConsole" ) );
804                String userId = info.getUserId().toLowerCase();
805                final BuddyStatus buddyStatus = BuddyList.getInstance()
806                        .getBuddyStatus( userId );
807                buddyStatus.setName( resources.getString( "xmlConsole" ) );
808                if( buddyStatus.getConversation() == null )
809                {
810                    buddyStatus.setConversation( ConsolePanel.getInstance( buddyStatus ) );
811                    MessageDelegator.getInstance().showPanel(
812                            buddyStatus.getConversation() );
813                    MessageDelegator.getInstance().frontFrame(
814                            buddyStatus.getConversation() );
815                }
816            }
817    
818            sound.setSelected(!Settings.getInstance().getBoolean("noSound"));
819            showOffline.setSelected( Settings.getInstance().getBoolean(
820                "showOfflineBuddies") );
821            showAway.setSelected(!Settings.getInstance().getBoolean("dontShowAwayBuddies"));
822        }
823    
824    
825        /**
826         *  Saves the current settings - like the height and width of the buddy list
827         */
828        public void saveSettings()
829        {
830            if( frame.isVisible() )
831            {
832                if( !docked )
833                {
834                    //save the current buddy list size and position
835                    Dimension size = new Dimension( frame.getSize() );
836                    Point location = new Point( frame.getLocationOnScreen() );
837    
838                    Settings.getInstance().setProperty( "buddyListX",
839                            new Double( location.getX() ).toString() );
840                    Settings.getInstance().setProperty( "buddyListY",
841                            new Double( location.getY() ).toString() );
842                    Settings.getInstance().setProperty( "buddyListWidth",
843                            new Integer( size.width ).toString() );
844                    Settings.getInstance().setProperty( "buddyListHeight",
845                            new Integer( size.height ).toString() );
846    
847    
848                }
849    
850            }
851    
852            Settings.writeSettings();
853        }
854    
855    
856        /**
857         *  Returns the buddy list tree
858         *
859         *@return    the buddy list tree
860         */
861        public BuddyListTree getBuddyListTree()
862        {
863            return this.buddyListTree;
864        }
865    
866    
867        /**
868         *  Init components just does some initial setup, sets the size and
869         *  preferred location of the buddy list. Sets up an new initial tree.
870         */
871        private void initComponents()
872        {
873            JPanel container = this;
874    
875            container.setLayout( new BorderLayout() );
876            container.setBorder( BorderFactory.createEmptyBorder( 1, 1, 1, 1 ) );
877    
878            showOffline.setPreferredSize( new Dimension( 24, 24 ) );
879            ShowOfflineAction.addItem( showOffline );
880            showOffline.setToolTipText(resources.getString("showOfflineBuddies"));
881    
882            showAway.setPreferredSize(new Dimension(24,24));
883            showAway.setToolTipText(resources.getString("showAwayBuddies"));
884            ShowAwayAction.addItem(showAway);
885            sound.setPreferredSize(new Dimension(24,24));
886            sound.setToolTipText(resources.getString("soundToggle"));
887            sound.addActionListener(new ActionListener()
888            {
889                public void actionPerformed(ActionEvent e) {
890                    Settings.getInstance().setBoolean("noSound", !sound.isSelected());
891                    com.valhalla.Logger.debug( Settings.getInstance().getBoolean( "noSound" ) + "" );
892                }
893            });
894    
895            JPanel buttonPanel = new JPanel();
896            buttonPanel.setLayout( new BoxLayout( buttonPanel, BoxLayout.X_AXIS ) );
897            buttonPanel.setBorder( BorderFactory.createEmptyBorder( 1, 1, 1, 1 ) );
898            buttonPanel.add( showOffline );
899            buttonPanel.add( Box.createRigidArea(new Dimension(2,0)));
900            buttonPanel.add(showAway);
901            buttonPanel.add( Box.createRigidArea(new Dimension(2,0)));
902    
903            buttonPanel.add(sound);
904            buttonPanel.add( Box.createHorizontalGlue());
905            container.add( buttonPanel, BorderLayout.NORTH );
906    
907            jbButton.addMouseListener( new ButtonListener() );
908            jbButton.setToolTipText(resources.getString("jbotherMenu"));
909            jbButton.setIcon( Standard.getIcon( "images/frameicon.png" ) );
910            statusButton.addMouseListener( new Button2Listener() );
911    
912            statusButton.setText( resources.getString( "offline" ) );
913            buddyListTree = new BuddyListTree();
914    
915            // so the menus get the correct size
916            JFrame temp = new JFrame();
917            temp.getContentPane().setLayout(new FlowLayout());
918            temp.getContentPane().add(jbMenu);
919            temp.getContentPane().add(statusMenu);
920            temp.pack();
921            temp.dispose();
922    
923            container.add( buddyListTree, BorderLayout.CENTER );
924    
925            JPanel status = new JPanel();
926    
927            GridBagLayout gbl = new GridBagLayout();
928            status.setLayout( gbl );
929            status.setMaximumSize( new Dimension( 1000, jbButton.getHeight() ) );
930    
931            GridBagConstraints gbc = new GridBagConstraints();
932            gbc.fill = GridBagConstraints.HORIZONTAL;
933            gbc.weightx = 1;
934            gbc.weighty = 1;
935            gbc.anchor = GridBagConstraints.EAST;
936            gbc.insets = new Insets( 1, 1, 1, 1 );
937            gbc.gridx = 1;
938            gbc.gridy = 1;
939            gbl.addLayoutComponent( statusButton, gbc );
940            gbc.fill = GridBagConstraints.NONE;
941            gbc.weightx = 0;
942            gbc.weighty = 0;
943            gbc.anchor = GridBagConstraints.WEST;
944            gbc.insets = new Insets( 1, 1, 1, 1 );
945            gbc.gridx = 0;
946            gbc.gridy = 1;
947            gbl.addLayoutComponent( jbButton, gbc );
948    
949            status.add( jbButton );
950            status.add( statusButton );
951    
952            container.add( status, BorderLayout.SOUTH );
953    
954        }
955    
956    
957        /**
958         *  Description of the Class
959         *
960         *@author     synic
961         *@created    April 12, 2005
962         */
963        class ButtonListener extends MouseAdapter
964        {
965            public void mousePressed( MouseEvent e )
966            {
967                jbMenu.showMenu( e.getComponent(), jbButton.getX() - 1, jbButton.getY() - jbMenu.getHeight() - 1 );
968            }
969        }
970    
971    
972        /**
973         *  Description of the Class
974         *
975         *@author     synic
976         *@created    April 12, 2005
977         */
978        class Button2Listener extends MouseAdapter
979        {
980            public void mousePressed( MouseEvent e )
981            {
982                statusMenu.showMenu( e.getComponent(), jbButton.getX() - 1, statusButton.getY() - statusMenu.getHeight() - 1 );
983            }
984    
985        }
986    
987        /**
988         *  Sets whether or not we have signed off
989         *
990         *@param  value  true if we have signed off
991         */
992        public void setSignoff( boolean value )
993        {
994            this.signoff = value;
995        }
996    
997    
998        /**
999         *  Gets whether or not we have signed off
1000         *
1001         *@return    true if we have signed off
1002         */
1003        public boolean getSignoff()
1004        {
1005            return signoff;
1006        }
1007    
1008    
1009        /**
1010         *  Gets the displayed name, or "me"
1011         *
1012         *@return    the myDisplayedName setting
1013         */
1014        public String getMyName()
1015        {
1016            if( Settings.getInstance().getProperty( "myDisplayedName" ) != null )
1017            {
1018                return Settings.getInstance().getProperty( "myDisplayedName" );
1019            }
1020            return connection.getUser();
1021        }
1022    
1023    
1024        /**
1025         *  Listens for user input events. If JBother is set to go "Away" on idle,
1026         *  this will restart the idle timer.
1027         *
1028         *@author     Adam Olsen
1029         *@created    October 26, 2004
1030         *@version    1.0
1031         */
1032        public class MyAWTEventListener implements AWTEventListener
1033        {
1034            /**
1035             *  Called by the event listener
1036             *
1037             *@param  evt  the event
1038             */
1039            public void eventDispatched( AWTEvent evt )
1040            {
1041                setLastActive();
1042                if( awayTimer != null && awayTimer.isRunning() )
1043                {
1044                    awayTimer.restart();
1045                }
1046                else if( idleAway )
1047                {
1048                    setStatus( Presence.Mode.AVAILABLE, getCurrentStatusString(),
1049                            false );
1050                    if( awayTimer != null ) awayTimer.start();
1051                    idleAway = false;
1052                }
1053    
1054            }
1055        }
1056    
1057    
1058        /**
1059         *  Closes the application
1060         */
1061        public void quitHandler()
1062        {
1063            saveSettings();
1064            if( connection != null && connection.isConnected() )
1065            {
1066                connection.close();
1067            }
1068            //and finally close the connection
1069    
1070            com.valhalla.Logger.closeLog();
1071            System.exit( 0 );
1072        }
1073    
1074    
1075        /**
1076         *  signs off, clears the buddy list
1077         */
1078        public void signOff()
1079        {
1080            //pingTimer.stop();
1081            signoff = false;
1082            buddyListTree.clearBuddies();
1083            if( awayTimer != null ) awayTimer.stop();
1084            currentMode = null;
1085            sendStatusChangedEvent();
1086    
1087            if( connection != null )
1088            {
1089                if( !statusMenu.blinkTimerIsRunning() )
1090                {
1091                    statusMenu.startBlinkTimer();
1092                }
1093                Thread thread = new Thread( new ConnectionCloseThread() );
1094                thread.start();
1095            }
1096    
1097            if( buddyStatuses != null )
1098            {
1099                Iterator iterator = buddyStatuses.keySet().iterator();
1100                while( iterator.hasNext() )
1101                {
1102                    String user = (String)iterator.next();
1103                    BuddyStatus buddy = (BuddyStatus)buddyStatuses.get( user );
1104                    buddy.resetBuddy();
1105                    if( buddy.getConversation() != null
1106                             && buddy.getConversation() instanceof ChatPanel )
1107                    {
1108                        ( (ChatPanel)buddy.getConversation() ).disconnected();
1109                    }
1110                }
1111            }
1112    
1113            if( tabFrame != null )
1114            {
1115                ArrayList remove = new ArrayList();
1116    
1117                TabbedPanel pane = tabFrame.getTabPane();
1118                for( int i = 0; i < pane.getTabCount(); i++ )
1119                {
1120                    TabFramePanel panel = (TabFramePanel)pane.getTabAt(i).getContentComponent();
1121                    if( panel instanceof ChatRoomPanel )
1122                    {
1123                        remove.add( panel );
1124                    }
1125                }
1126    
1127                for( int i = 0; i < remove.size(); i++ )
1128                {
1129                    TabFramePanel panel = (TabFramePanel)remove.get( i );
1130                    removeTabPanel( panel );
1131                    ( (ChatRoomPanel)panel ).serverNoticeMessage( "You have been disconnected" );
1132                    ( (ChatRoomPanel)panel ).disconnect();
1133                }
1134            }
1135        }
1136    
1137    
1138        /**
1139         *  Description of the Class
1140         *
1141         *@author     synic
1142         *@created    April 12, 2005
1143         */
1144        class ConnectionCloseThread implements Runnable
1145        {
1146            /**
1147             *  Main processing method for the ConnectionCloseThread object
1148             */
1149            public void run()
1150            {
1151                connection.close();
1152                SwingUtilities.invokeLater(
1153                    new Runnable()
1154                    {
1155                        public void run()
1156                        {
1157                            statusMenu.stopBlinkTimer();
1158                            statusMenu.setIcon( (Presence.Mode)null );
1159                        }
1160                    } );
1161    
1162                connection = null;
1163            }
1164        }
1165    
1166    
1167        /**
1168         *  Close down the buddy list. Also closes down any other windows that might
1169         *  be open
1170         */
1171        public void kill()
1172        {
1173            if( tabFrame != null )
1174            {
1175                tabFrame.leaveAll();
1176            }
1177    
1178            saveSettings();
1179    
1180            setVisible( false );
1181    
1182            currentMode = Presence.Mode.AVAILABLE;
1183            currentStatusString = resources.getString( "available" );
1184            connection = null;
1185    
1186            awayTimer.stop();
1187    
1188            Vector panels = MessageDelegator.getInstance().getPanels();
1189            int size = panels.size();
1190            for( int i = 0; i < size; i++ )
1191            {
1192                ConversationPanel panel = (ConversationPanel)panels.get( 0 );
1193                panel.closeHandler();
1194            }
1195    
1196            panels.removeAllElements();
1197    
1198            DialogTracker.kill();
1199            signoff = false;
1200        }
1201    
1202    
1203        /**
1204         *  Gets a list of blocked users
1205         *
1206         *@return    the list of blocked users
1207         */
1208        public Hashtable getBlockedUsers()
1209        {
1210            return blockedUsers;
1211        }
1212    
1213    
1214        /**
1215         *  Sets the list of blocked users
1216         *
1217         *@param  users  the list of blocked users
1218         */
1219        public void setBlockedUsers( Hashtable users )
1220        {
1221            this.blockedUsers = users;
1222        }
1223    
1224    
1225        /**
1226         *  Gets the GnuPG password
1227         *
1228         *@return    GnuPG password
1229         */
1230        public String getGnuPGPassword()
1231        {
1232            return gnupgPassword;
1233        }
1234    
1235    
1236        /**
1237         *  Sets the GnuPG password
1238         *
1239         *@param  gnupgPassword  GnuPG password
1240         */
1241        public void setGnuPGPassword( String gnupgPassword )
1242        {
1243            this.gnupgPassword = gnupgPassword;
1244        }
1245    
1246    
1247        /**
1248         *  Gets the encryption status
1249         *
1250         *@return    encryption status
1251         */
1252        public boolean isEncrypting()
1253        {
1254            return encrypting;
1255        }
1256    
1257    
1258        /**
1259         *  Sets the encryption status
1260         *
1261         *@param  variant  encryption status
1262         */
1263    
1264        public void isEncrypting( boolean variant )
1265        {
1266            this.encrypting = variant;
1267        }
1268    
1269    
1270    
1271    
1272        /**
1273         *  Updates all the dialogs window icons
1274         */
1275        public void updateIcons()
1276        {
1277            StatusIconCache.clearStatusIconCache();
1278            ImageIcon icon = StatusIconCache.getStatusIcon( org.jivesoftware.smack.packet.Presence.Mode.AVAILABLE );
1279            if( icon != null )
1280            {
1281    //            statusMenu.setIcon(icon);
1282            }
1283            statusMenu.reloadStatusIcons();
1284        }
1285    
1286    
1287        /**
1288         *  send StatusChanged event to plugins (and mb elsewhere too)
1289         */
1290        private void sendStatusChangedEvent()
1291        {
1292            StatusChangedEvent event = new StatusChangedEvent( getInstance() );
1293            PluginChain.fireEvent( event );
1294        }
1295    
1296    
1297        /**
1298         *  Set the current status by sending a Jabber packet
1299         *
1300         *@param  mode            the mode to set it to
1301         *@param  defaultMessage  the status message to set it to
1302         *@param  getMessage      whether or not to get a new message
1303         *@return                 true if the status packet was sent successfully
1304         */
1305        public boolean setStatus( Presence.Mode mode, String defaultMessage,
1306                boolean getMessage )
1307        {
1308            String result;
1309            if( getMessage )
1310            {
1311                StatusDialog statusDlg = new StatusDialog( mode );
1312                return false;
1313            }
1314            else
1315            {
1316                result = defaultMessage;
1317            }
1318            if( result == null || result.equals( "" ) )
1319            {
1320                return false;
1321            }
1322            else
1323            {
1324                if( mode != Presence.Mode.AWAY ) idleAway = false;
1325                if( awayTimer != null )
1326                {
1327                    if( mode == Presence.Mode.AVAILABLE )
1328                    {
1329                        awayTimer.start();
1330                    }
1331                    else
1332                    {
1333                        awayTimer.stop();
1334                    }
1335                }
1336    
1337                int priority = 5;
1338                try
1339                {
1340                    priority = Integer.parseInt( Settings.getInstance().getProperty(
1341                            "priority", "5" ) );
1342                }
1343                catch( NumberFormatException nfe )
1344                {
1345                }
1346    
1347                if( !checkConnection() )
1348                {
1349                    ConnectorThread.getInstance().init( mode, result, idleAway ).start();
1350                    return true;
1351                }
1352    
1353                Presence presence = new Presence( Presence.Type.AVAILABLE, result,
1354                        priority, mode );
1355    
1356                if( mode == Presence.Mode.INVISIBLE )
1357                {
1358                    final String r = result;
1359                    final int p = priority;
1360                    presence = new Presence( Presence.Type.AVAILABLE, result,
1361                        priority, mode )
1362                    {
1363                        public String toXML()
1364                        {
1365                            return "<presence type='invisible'>\n" +
1366                                "<status>" + r + "</status>\n" +
1367                                "<priority>" + p + " </priority>\n" +
1368                                "</presence>\n";
1369    
1370                        }
1371                    };
1372                }
1373    
1374                GnuPG gnupg = new GnuPG();
1375                String signedData = null;
1376                SecureExtension signedExtension = new SecureExtension( "signed" );
1377                String gnupgSecretKey = Settings.getInstance().getProperty(
1378                        "gnupgSecretKeyID" );
1379    
1380                if( JBotherLoader.isGPGEnabled()
1381                         && Settings.getInstance().getBoolean( "gnupgSignPresence" )
1382                         && gnupgSecretKey != null )
1383                {
1384                    signedData = gnupg.signExtension( result, gnupgSecretKey );
1385                    if( signedData != null )
1386                    {
1387                        signedData = signedData.replaceAll( "(\n)+$", "" );
1388                        signedExtension.setData( signedData );
1389                        presence.addExtension( signedExtension );
1390                    }
1391    
1392                }
1393                connection.sendPacket( presence );
1394    
1395                setCurrentPresenceMode( mode );
1396                if( !idleAway )
1397                {
1398                    setCurrentStatusString( result );
1399                }
1400    
1401                // if the group chat window is open, set the status there too
1402                if( getTabFrame() != null )
1403                {
1404                    getTabFrame().setStatus( mode, result );
1405                }
1406    
1407                ParsedBuddyInfo info = new ParsedBuddyInfo( connection.getUser() );
1408                BuddyStatus buddy = BuddyList.getInstance().getBuddyStatus(
1409                        info.getUserId() );
1410                buddy.addResource( info.getResource(), priority, mode, result );
1411                statusMenu.loadSelfStatuses();
1412    
1413                updateIcons();
1414                sendStatusChangedEvent();
1415                statusMenu.setModeChecked( mode );
1416                statusButton.setText( resources.getString( mode.toString() ) );
1417                statusButton.repaint();
1418    
1419            }
1420            return true;
1421        }
1422    
1423        /**
1424         *  Gets the statusMenu attribute of the BuddyList object
1425         *
1426         *@return    The statusMenu value
1427         */
1428        public SetStatusMenu getStatusMenu()
1429        {
1430            return statusMenu;
1431        }
1432    
1433    
1434        /**
1435         *  Gets the buddiesMenu attribute of the BuddyList object
1436         *
1437         *@return    The buddiesMenu value
1438         */
1439        public BuddyListBuddiesMenu getBuddiesMenu()
1440        {
1441            return jbMenu;
1442        }
1443    
1444    
1445        /**
1446         *  Sets the lastActive attribute of the BuddyList object
1447         */
1448        public void setLastActive()
1449        {
1450            this.date = new Date().getTime();
1451    
1452        }
1453    
1454    
1455        /**
1456         *  Gets the lastActive attribute of the BuddyList object
1457         *
1458         *@return    The lastActive value
1459         */
1460        public long getLastActive()
1461        {
1462            return this.date/1000;
1463        }
1464    
1465    }
1466