// Sample code file: FilesystemListView.java

// Warning: This code has been marked up for HTML

/*
   Copyright (c) 1997-1999 Novell, Inc.  All Rights Reserved.

   THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
   USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE AGREEMENT
   ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS THIS WORK.
   PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO DEVELOPER A
   ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE CODE IN ITS
   PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS TO MARKET,
   DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF DEVELOPER'S
   PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR DEVELOPER'S
   CUSTOMERS WITH RESPECT TO THIS CODE.
*/

package com.novell.sample.snapins.filesystem;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import com.novell.application.console.snapin.*;
import com.novell.application.console.snapin.scope.*;
import com.novell.application.console.snapin.context.ViewSnapinContext;

/**
   This class is an example of a view snap-in that builds a list of
   ObjectEntry children based on the tree selection in the sample
   FileSystem namespace.
*/
public class FilesystemListView
   implements ViewSnapin, ViewSnapinRev2, FocusListener
{
   Shell shell = null;
   ObjectEntry[] entries = new ObjectEntry[0];
   MyPanel p;
   ViewSnapinContext lastViewContext = null;

   /*
     Implementation of the ViewSnapin interface.
   */

   /**
    The getSnapinName() method (derived from Snapin interface) is called by
    configurators to display the localized name of this snap-in. It returns
    the translated name for this snap-in. The shell displays the name you
    provide together with all the snap-ins of a given type. The user is then
    allowed to select which snap-ins should be set as active in the current
    configuration. The getSnapinName() method may be called before initSnapin().
    */
   public String getSnapinName()
   {
      return "Novell's Sample Filesytem Namespace List View";
   }

   /**
    The getSnapinDescription() method (derived from Snapin interface) is called
    by the configurators to display the localized description information
    about this snap-in. It returns the snap-in description as a String.
    */
   public String getSnapinDescription()
   {
      return "Novell's Sample Filesystem Namespace Listview";
   }

   /**
    The initSnapin() method initializes this class. You should do any
    onetime initialization here, such as adding event listeners. The method
    returns a boolean set to <i>true</i> if the snap-in is able to successfully
    complete initialization, or <i>false</i> if initialization fails. The 'info'
    parameter contains data the snap-in may use for initialization, such as
    references to the shell and the snap-in type, and may contain a reference
    to snap-in context data.
    */
   public boolean initSnapin(InitSnapinInfo info)
   {
      shell = info.getShell();
      p = new MyPanel(shell);
      p.addFocusListener(this);
      p.setLayout(new FlowLayout(FlowLayout.LEFT));
      p.setBackground(UIManager.getColor("Tree.textBackground"));
      return true;
   }

   /**
    Implement shutdownSnapin() in your extending snap-in if there is
    anything that needs to be cleaned up, such as removing shell event listeners.
    */
   public void shutdownSnapin()
   {
   }

   /**
      This method (defined by ViewSnapin) returns the object derived
      from Component that will be used in the shell as the view.

      The Component object returned is associated with the ObjectEntry in
      the view context passed to the snap-in in the context parameter. You
      should construct the return Component in the initSnapin() method, then
      in getView() the new tree selection should be cleared so that new data
      can be put in. That is, all Components in the view should be removed
      and a new set of Components set.
   */
   public Component getView(ViewSnapinContext viewContext)
   {
      lastViewContext = viewContext;
      p.removeAll();
      try
      {
         ObjectEntryEnumeration children =
            shell.getChildren(viewContext.getObjectEntry(),
                              viewContext.getResultModifier());

         while(children.hasMoreElements())
         {
            ObjectEntry child = children.next();
            MyLabel node = new MyLabel(child, shell);
            node.addMouseListener(node);
            p.add(node);
         }
      }
      catch(SnapinException e)
      {
         shell.handleSnapinException(e);
      }

      return p;
   }

   public void refresh()
   {
      getView(lastViewContext);
   }

  /**
      This method (defined by ViewSnapin) returns the unique, non-localized
      name of the view. This is the unique identifier for snap-ins to
      identify the view and to register against the view.

      In a view snap-in the getUniqueID() method is called before the
      initSnapin() method. Also, if a view is changed by ConsoleOne, the
      getViewMenuName() method is called before the initSnapin() method.
   */
   public String getUniqueID()
   {
      return "Novell Sample List View";
   }

   /**
      This method (defined by ViewSnapin) returns the currently selected
      ObjectEntry array within a view.

      The shell calls getSelectedObjects() to get the currently selected
      objects (such as context menus) when the getViewSelection() is called
      in the shell. If no objects are selected, return an empty object entry
      array (i.e. new ObjectEntry[0];).
    */
   public ObjectEntry [] getSelectedObjects()
   {
      return this.entries;
   }

   /**
      This method (defined by ViewSnapin) provides a shell notification that
      the current view has become inactive. It is called be the shell to let the
      View snap-in know to cleanup resources on inactive views. The parameter
      'c' is passed by the shell as an instance of the previous view.
   */
   public void setInactive(Component c){};

  /**
    The getViewMenuName() method returns the language dependent translated
    (localized) name that will be used as the text in the view menu.  Use
    the '&' character to specify the menu mnemonic.
   */
   public String getViewMenuName()
   {
      return "S&ample Filesystem List View";
   };

   /*
     Implementation of the FocusListener interface.
   */

   public void focusGained(FocusEvent e)
   {
      shell.viewGainedFocus();
   }

   public void focusLost(FocusEvent e)
   {
   }

   /**
       MyLabel creates a selectable item in the list view.
    */
   class MyLabel extends JLabel implements MouseListener
   {
      final int MENU_OFFSET = 2;
      final int IMAGE_WIDTH = 18;
      final int IMAGE_HEIGHT = 16;
      ObjectEntry entry;
      boolean isSelected = false;

      final int imageWidth = IMAGE_WIDTH;
      final int imageHeight = IMAGE_HEIGHT;
      int totalWidth;
      int fontHeight, fontDescent;

      public MyLabel(ObjectEntry entry, Shell shell)
      {
         super();
         this.entry = entry;
         setText(shell.getDisplayName(entry));
         setIcon(shell.getDisplayIcon(entry));
         setOpaque(true);
      }

      public void setObjectEntry(ObjectEntry entry)
      {
         this.entry = entry;
      }

      public ObjectEntry getObjectEntry()
      {
         return this.entry;
      }

      public void setSelected(boolean isSelected)
      {
         this.isSelected = isSelected;
         repaint();
      }

      public boolean isSelected()
      {
         return this.isSelected;
      }

      public void paintSelections()
      {
         Component[] components = getParent().getComponents();
         for (int i = 0; i < components.length; i++)
         {
            if(components[i] instanceof MyLabel)
            {
               MyLabel label = (MyLabel)components[i];
               if(label.isSelected)
               {
                  label.setSelected(false);
               }
            }
         }

         p.gainFocus();
         setSelected(true);
      }

      public void paint(Graphics g)
      {
         if(isSelected && shell.getFocusState() == Shell.FOCUS_TREE)
         {
            setBackground(UIManager.getColor("Tree.textBackground"));
            setForeground(UIManager.getColor("Tree.textForeground"));
         }
         else if (isSelected && shell.getFocusState() == Shell.FOCUS_VIEW)
         {
            setBackground(UIManager.getColor("Tree.selectionBackground"));
            setForeground(UIManager.getColor("Tree.selectionForeground"));
         }
         else
         {
            setBackground(UIManager.getColor(
               "Tree.textBackground"));
            setForeground(UIManager.getColor("Tree.textForeground"));
         }
         super.paint(g);
         if(isSelected && shell.getFocusState() == Shell.FOCUS_TREE)
         {
            g.drawRect(0,0,getWidth()-1,getHeight()-1);
         }
      }

      public void mousePressed(MouseEvent e)
      {
         if(e.getModifiers() == InputEvent.META_MASK)
         {
            MyLabel label = (MyLabel)e.getComponent();
            ObjectEntry[] array = new ObjectEntry[1];
            array[0] = label.getObjectEntry();
            JPopupMenu popupMenu = shell.getPopupMenu(new DefaultObjectEntryCollection(array));
            Point p = label.getLocationOnScreen();
            if(popupMenu != null)
            {
                popupMenu.setLocation(p.x+MENU_OFFSET,p.y+MENU_OFFSET);
                popupMenu.setVisible(true);
            }
         }
      }

      public void mouseEntered(MouseEvent e){}
      public void mouseExited(MouseEvent e){}
      public void mouseReleased(MouseEvent e){}

      public void mouseClicked(MouseEvent e)
      {
         if(e.getClickCount() == 1)
         {
            MyLabel label = (MyLabel)e.getComponent();
            entries = new ObjectEntry[1];
            entries[0] = label.getObjectEntry();
            label.paintSelections();
         }
         else if (e.getClickCount() == 2 && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
         {
            MyLabel label = (MyLabel)e.getComponent();

            if(label.getObjectEntry().getObjectType().isContainer())
            {
               Container p = label.getParent();
               Cursor oldCursor = p.getCursor();
               p.setCursor(new Cursor(Cursor.WAIT_CURSOR));
               p.removeAll();
               shell.setTreeSelection(label.getObjectEntry());
               try
               {
                  ObjectEntryEnumeration children =
                     shell.getChildren(shell.getTreeSelection(), null);

                  while(children.hasMoreElements())
                  {
                     ObjectEntry child = children.next();
                     MyLabel node = new MyLabel(child, shell);
                     node.addMouseListener(node);
                     p.add(node);
                  }
               }
               catch(SnapinException exception)
               {
                  shell.handleSnapinException(exception);
               }
               p.setCursor(oldCursor);
            }
            else
            {
               shell.executeDefaultPopupMenuItemAction(
                  label.getObjectEntry());
            }
         }
      }
   }

   class MyPanel extends JPanel
   {
      Shell shell;

      public MyPanel(Shell shell)
      {
         this.shell = shell;
         enableEvents(AWTEvent.MOUSE_EVENT_MASK);
      }

      protected void processMouseEvent(MouseEvent e)
      {
         if(e.getID() == MouseEvent.MOUSE_CLICKED)
         {
            if(shell.getFocusState() != Shell.FOCUS_VIEW)
            {
               gainFocus();
            }
         }
      }

      public void gainFocus()
      {
         requestFocus();
         //super.processFocusEvent(e);
         shell.viewGainedFocus();
      }

      public boolean isFocusTraversable()
      {
         return true;
      }
   }
}