//=============================================================================
//
// toolselect.js   | For making a div behave as a tool selector.
//
//

//-----------------------------------------------------------------------------
// COPYRIGHT 2003 DELCAM PLC., BIRMINGHAM, ENGLAND. 
//-----------------------------------------------------------------------------
//
// History.
// Who When     What   
// --- -------- ---------------------------------------------------------------
// sic 22/09/05 Written (based in select.js)
//-----------------------------------------------------------------------------


// TODO: Implement keyboard control.
//       Handle the "no selected tool" scenario.
//       no proper checks on button up for pinned (you can button up on something you didn't button down on)
//       don't like the fact that 'build' is required to be called for setting up tools after all are added for pinned.
//       dodgy.
//       Can we use the _built flag and override all the major functions like setselected index to force a build?
//       Or a global refresh for all tools to go in the Initialise function?


//=== PromoteDivToToolSelect ============================================
//
// This function transforms a span/div into a tool select control.
//
// The "sharedImagePath" argument is a path to the shared images.
// It defaults to (artcam.HtmlRootDir + "../SharedImages/") but you might
// not be in the right place so you can override this if you pass a path
// string in.
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// sic 21/05/03 Written
//-----------------------------------------------------------------------

function PromoteToToolSelect(select, sharedImagePath)
{
   // We check to see if the shared image path was passed in
   if (null == sharedImagePath)
      sharedImagePath = artcam.HtmlRootDir + "../SharedImages/";

   tsInitToolSelectMembers(select, sharedImagePath);

   select._ConfigureElement();
}


//=== tsInitToolSelectMembers ===========================================
//
// Add/configured all the required members.
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// sic 19/09/05 Written
//-----------------------------------------------------------------------

function tsInitToolSelectMembers(select, sharedImagePath)
{
   /////////////////////////////////////////////////////////////////////////
   //
   // Private data members.
   //
   /////////////////////////////////////////////////////////////////////////


   select._sharedImagePath = sharedImagePath;
   select._popup = null;

   select._rows = new Array();
   select._mapTitleToIndex = new Object();
   select._mapDataToIndex = new Object();

   select._selectedIndex = -1; // Nothing selected to begin with
   select._mouseOver = false;
   select._mouseOverButton = false;
   select._built = false;

   // Make a colour for the button face for items with state == 2.
   select._State2ButtonFace = ColorToString( GetColorShade (artcam.GetSysColor(COLOR_BTNHIGHLIGHT), 3, artcam.GetSysColor(COLOR_BTNFACE), 1));
   //select._State2ButtonFace = "buttonhighlight";

   // We are either in pinned or flyout mode. See if it is stored in the registry if not default to flyout mode
   select._mode = artcam.RetrieveString(document.title, select.id, "flyout");


   /////////////////////////////////////////////////////////////////////////
   //
   // Public methods.
   //
   /////////////////////////////////////////////////////////////////////////


   //=== GetItemData =======================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select.GetItemData = function(idx)
   {
      if (idx < 0 || idx >= this._rows.length)
         return;

      return this._rows[idx].data;
   }


   //=== AddItem ===========================================================
   //
   // For adding in a row to the menus html and an data marker
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 21/05/03 Written
   // sic 21/09/05 Modified for toolselect.
   // tpb 24/01/06 Handle a deferred build for pinned mode
   //-----------------------------------------------------------------------

   select.AddItem = function(src, data, title, state_data)
   {
      // Put the information into a new object
      var obj  = new Object();
      obj.title = title;
      obj.src = src;
      obj.state = 0; // Disabled by default.
      obj.data = data;
      obj.state_data = null == state_data ? data : state_data;

      // Store the new object in the maps and *then* in the rows array.
      this._mapTitleToIndex[obj.title] = this._rows.length;
      this._mapDataToIndex[obj.data] = this._rows.length;
      this._rows.push(obj);

      this._DestroyPopup();

      // If there is now a choice of tools,
      // reconfigure to include the flyout arrow.
      // Only in flyout mode. Pinned mode must be built later
      if (2 == this._rows.length && this._mode == "flyout" )
      {
         this._ConfigureElement();
      }
   }


   
   //=== Build =====================================================
   //
   // A speed up for pinned tool loading times. Build is called after all the tools have been added.
   // What about just setmode with a null argument?
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05
   //-----------------------------------------------------------------------

   select.Build = function()
   {
      // If this is a pinned mode, we configure the element, this should speed up loading
      if (this._mode == "pinned" )
         this._ConfigureElement();
      this._built = true; // Mark it as built
   }



   //=== IsOpen ============================================================
   //
   // This function overrides the default isOpen on _popup to get around
   // a problem we have when we click the drop box while the popup is open.
   // The popup loses focus, dissappears and then we get a mousedown to
   // the drop box which would see that the popup was closed and open it again!
   // So this function relies on the monitoring interval to tell us when it
   // has closed. We only think we have closed once the monitoring interval
   // has seen it. This interval is also used for auto scrolling.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/05/03 Written
   //-----------------------------------------------------------------------

   select.IsOpen = function()
   {
      if( this._popup == null )
         return false;
      if( this._popupOpenInterval != null ) // If our popup open interval is running still, we assume we are open
         return true;
      return this._popup.isOpen;
   }


   //=== PreLoad ===========================================================
   //
   // This will cause the popup to preload by actually creating it and trying to open it briefly.
   // This is used as a speedup for fuller lists and is performed in the background on a timeout
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 21/05/03 Written
   //-----------------------------------------------------------------------

   select.PreLoad = function()
   {
      if( this._PreloadTimeout == null )
      {
         // Call the preload after an instant timeout
         this._PreloadTimeout = setTimeout(this.id + ".PreLoad()", 0);
      }
      else
      {
         // Make sure pinned controls are built
         //if(!this._built)
         //   this.Build();

         this._PreloadTimeout = null;
         this._CreatePopup();
         this._popup.show(0, 0, 0, 0);
         this._popup.hide();

         // Now put the focus on the document body
         document.body.focus();
      }
   }


   //=== GetSelectedIndex ==================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 22/05/03 Written
   //-----------------------------------------------------------------------

   select.GetSelectedIndex = function()
   {
      return this._selectedIndex;
   }


   //=== SetSelectedIndex ==================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 22/05/03 Written
   //-----------------------------------------------------------------------

   select.SetSelectedIndex = function(idx)
   {
      // Make sure pinned controls are built
      //if(!this._built)
      //   this.Build();

      this._selectedIndex = idx;
      if(this._displayButton != null)
         this._displayButton.innerHTML = this._GetItemHtml(idx);
   }


   //=== GetItem ============================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 22/05/03 Written
   //-----------------------------------------------------------------------

   select.GetItem = function(idx)
   {
      // if we are out of range, return null
      if( idx < 0 || idx >= this._rows.length )
         return null;

      // Now return the row object
      return this._rows[idx];
   }


   //=== GetItemCount =======================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 22/05/03 Written
   //-----------------------------------------------------------------------

   select.GetItemCount = function()
   {
      return this._rows.length;
   }


   //=== FindIndexWithData =================================================
   //
   // Returns the index of the item in the list with the passed data else
   // returns -1.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 23/09/05 Written
   //-----------------------------------------------------------------------

   select.FindIndexWithData = function(data)
   {
      var idx = this._mapDataToIndex[data];

      if (null == idx) idx = -1;
      return idx;
   }


   //=== FindIndexWithTitle ================================================
   //
   // Returns the index of the item in the list with the passed title else
   // returns -1.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 23/09/05 Written
   //-----------------------------------------------------------------------

   select.FindIndexWithTitle = function(title)
   {
      var idx = this._mapTitleToIndex[title];

      if (null == idx) idx = -1;
      return idx;
   }


   //=== SetItemStates =====================================================
   //
   // Sets each item's state according to Artcam wishes.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05
   // tpb 23/01/06 Handle "pinned" mode.
   //              NOTE about states: 0 - Unavailable
   //                                 1 - Available
   //                                 2 - Selected
   //-----------------------------------------------------------------------

   select.UpdateItemByData = function(data, state)
   {
      var idx = this._mapDataToIndex[data];

      // If the tool is adsent from this tool selector, return.
      if (null == idx) return;

      var item = this._rows[idx];
      var changed = false;

      if (item.state != state)
      {
         item.state = state;
         changed = true;
      }

      if (2 == state && idx != this._selectedIndex)
      {
         this._selectedIndex = idx;
         changed = true;
      }

      if (true == changed)
      {
         this._DestroyPopup();
         this.SetSelectedIndex(this._selectedIndex);         
         this._SetButtonAppearance();

         // If this is a pinned mode we need to set the cell appearance
         if( this._mode == "pinned" )
            this._SetCellAppearance(idx);
      }
   }


   //=== Refresh ===========================================================
   //
   // for refreshing the control when it is shown
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 26/09/03 Written
   // sic 21/09/05 Modified for toolselect.
   // tpb 23/01/06 Handle "pinned" mode.
   //              NOTE about states: 0 - Unavailable
   //                                 1 - Available
   //                                 2 - Selected
   //-----------------------------------------------------------------------

   select.Refresh = function(toolbar_cache)
   {
      var itemCount = this._rows.length, changed = false;

      for (var idx = 0; idx < itemCount; idx++)
      {
         var item = this._rows[idx];

         // If this is a 'special' tool, skip it.
         if (item.state_data < 0) continue;

         var state = toolbar_cache.IsCommandAvailable(item.state_data);

         if (item.state != state)
         {
            item.state = state;
            changed = true;
         }

         if (2 == state && idx != this._selectedIndex)
         {
            this._selectedIndex = idx;
            changed = true;
         }

         // If this is a pinned mode and we have changed we need to set the cell appearance
         if( true == changed && this._mode == "pinned" )
            this._SetCellAppearance(idx);
      }

      if (true == changed)
      {
         this._DestroyPopup();

         // This will cause a resize of everything and refresh the control
         this.SetSelectedIndex(this.GetSelectedIndex());
         this._SetButtonAppearance();
      }
   }


   //=== select_GetSelected...==============================================
   //
   // Series of direct return functions for the currently selected object
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // ejp 18/12/03 Written
   // sic 21/09/05 Modified for toolselect.
   //-----------------------------------------------------------------------

   select.GetSelectedItem = function()
   {
      return this._rows[this._selectedIndex];
   }

   select.GetSelectedData = function()
   {
      return this._rows[this._selectedIndex].data
   }

   select.GetSelectedHtml = function()
   {
      return this._GetItemHtml(this._selectedIndex);
   }

   select.GetSelectedTitle = function()
   {
      return this._rows[this._selectedIndex].title;
   }



   //=== SetMode ==============================================
   //
   // Sets the control to "flyout" or "pinned" mode
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/01/06 Written
   //-----------------------------------------------------------------------

   select.SetMode = function(mode)
   {
      switch(mode)
      {
      case "pinned":
      case "flyout":
         // If "pinned" or "flyout" was specified, hide any popup, set the mode, store in registry and configure the element.
         this._DestroyPopup();
         this._mode = mode;
         artcam.StoreString(document.title, this.id, this._mode);
         this._ConfigureElement();
         break;

      default:
         // Otherwise this is an error. We assert and then do nothing
         artcam.Alert("Invalid Mode passed into toolselect SetMode", 0);
         break;
      }
   }



   /////////////////////////////////////////////////////////////////////////
   //
   // Private methods.
   //
   /////////////////////////////////////////////////////////////////////////


   //=== _ConfigureElement =================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._ConfigureElement = function()
   {
      var html = new Array();

      switch(this._mode)
      {
      case "flyout":
         html.push("<table id='" + this.id + "Table' tabindex='0' cellspacing='0' cellpadding='0' border='0' height='100%' width='100%'>");
         html.push(  "<tr align='center' valign='center' ");
         html.push(    "onmouseenter='" + this.id + "._OnMouseEnterTable()' ");
         html.push(    "onmouseleave='" + this.id + "._OnMouseLeaveTable()' ");
         html.push(    "onkeydown='" + this.id + "._OnKeyDown()'>");
         html.push(    "<td width='100%' id='" + this.id + "Button' ");
         html.push(      "style='border:buttonface solid 1px; overflow:hidden;' ");
         html.push(      "onmouseenter='" + this.id + "._OnMouseEnterButton()' ");
         html.push(      "onmouseleave='" + this.id + "._OnMouseLeaveButton()' ");
         html.push(      "onmousedown='" + this.id + "._OnMouseDown()' ");
         html.push(      "onmouseup='" + this.id + "._OnMouseUp()' ");
         html.push(      "onmousemove='" + this.id + "._OnMouseMove()'>");
         html.push(    "</td>");

         if (this._rows.length > 1)
         {
            html.push( "<td id='" + this.id + "Arrow' style='border:buttonface solid 1px;' ");
            html.push(   "onmousedown='" + this.id + "._ShowPopup()'>");
            html.push(   "<img src='" + this._sharedImagePath + "rightarrow.gif'>");
            html.push( "</td>");
         }

         html.push(  "</tr>");
         html.push("</table>");

         // Set the width style and margin according to whether or not we have a pin icon
         // We always have an overall width of 42px for a nicer tabular layout without using tables
         // and we do this by adjusting the margins
         if (this._rows.length > 1)
         {
            this.style.width = "42px";
            this.style.marginRight = "0px";
         }
         else
         {
            this.style.width = "36px";
            this.style.marginRight = "6px";
         }
         this.style.height = "36px";
         this.style.backgroundColor = "";

         // Set this in the html
         this.innerHTML = html.join("");

         // Get our object references
         this._displayTable  = eval(this.id + "Table");
         this._displayButton = eval(this.id + "Button");
         this._displayArrow  = eval("typeof(" + this.id + "Arrow) != 'undefined' ? " + this.id + "Arrow : null");

         // If our display button is in an item 2 state, we need to set this up.
         this._SetButtonAppearance();

         break;



      case "pinned":
         // Add what would normally go into the popup window into a single span
         for(var idx=0; idx < this._rows.length; idx++)
         {
            var rightMargin = 7;
            if( idx == this._rows.length - 1)
               rightMargin = 0;
            html.push( "<span style='border:buttonface solid 1px;width:"+(35+rightMargin)+"px;height:36px;padding:1px;padding-top:2px;" );
            html.push(              "padding-right:"+rightMargin+"px;' " );
            html.push(       "id='" + this.id + "Cell" + idx + "' " );
            html.push(       "onmouseenter='"+this.id+"._OnPopupItemMouseEnter(" + idx + ")' " );
            html.push(       "onmouseleave='"+this.id+"._OnPopupItemMouseLeave(" + idx + ")' " );
            html.push(       "onmouseup='"+this.id+"._OnPopupItemSelect(" + idx + ")' " );
            html.push( ">" + this._GetItemHtml(idx) + "</span>" );
         }

         // Add the pushpin end - when clicked it sets the mode to "flyout"
         var gHalfGrayStr = ColorToString(GetColorShade(artcam.GetSysColor(COLOR_BTNFACE), 2, artcam.GetSysColor(COLOR_BTNSHADOW), 1));
         html.push( "<span id='" + this.id + "Cell" + idx + "' style='border:buttonface solid 1px;background-color:"+gHalfGrayStr+";width:5px;height:36px;padding-top:2px;' ");
         html.push(       "onmouseenter='"+this.id+"._OnPopupItemMouseEnter(" + idx + ")' " );
         html.push(       "onmouseleave='"+this.id+"._OnPopupItemMouseLeave(" + idx + ")' " );
         html.push(       "onmousedown='" + this.id + ".SetMode(\"flyout\")'>");
         html.push(   "<img src='" + this._sharedImagePath + "pin.gif'>");
         html.push( "</span>");

         // Turn off the width and height styles
         this.style.height = "";
         this.style.width  = "";
         var gVLGrayStr = ColorToString(GetColorShade(artcam.GetSysColor(COLOR_BTNFACE), 5, artcam.GetSysColor(COLOR_BTNSHADOW), 1));
         this.style.backgroundColor = gVLGrayStr;

         this.innerHTML = html.join("");


         // Set these global references to nothing - check this
         this._displayTable  = null;
         this._displayButton = null;
         this._displayArrow  = null;

         // Ensure our state 2 items are correct now that the items are created
         for(var idx=0; idx < this._rows.length; idx++)
         {
            if(2 == this._rows[idx].state)
            {
               var cell = this._GetItemCell(idx);
               this._Inset(cell);
               cell.style.backgroundColor = this._State2ButtonFace;
            }
         }

         break;
      }


      this.SetSelectedIndex(this.GetSelectedIndex());
   }


   //=== _SetArrowBorder ===================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._SetArrowBorder = function(borderFn)
   {
      if (this._displayArrow) borderFn(this._displayArrow);
   }


   //=== _OnResize =========================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   // tpb 20/10/06 This isn't being called. So removed code.
   //              If it is ever re-instated, see select_OnResize() in select.js first
   //              about how to deal with the recursion issue.
   select._OnResize = function()
   {
      // Just set the selected index again. It will sort the sizing issues out
      //this.SetSelectedIndex(this.GetSelectedIndex()); // Reset the index
   }


   //=== _GetItemCell ======================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._GetItemCell = function(idx)
   {
      if( this._mode == "flyout" )
         return this._popup.document.getElementById(this.id + "Cell" + idx);
      if( this._mode == "pinned" )
         return document.getElementById(this.id + "Cell" + idx);
      artcam.Alert("Incorrect Mode Set!", 0);
      return null;
   }


   //=== _OnPopupOpen ======================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._OnPopupOpen = function()
   {
      this._mouseButtonDown = false;
      this._SetArrowBorder(this._Inset);
   }


   //=== _OnPopupClose =====================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._OnPopupClose = function()
   {
      this._SetButtonAppearance();

      if (true == this._mouseOver)
      {
         this._SetArrowBorder(this._Outset);
      }
      else
      {
         this._SetArrowBorder(this._Flatten);
      }
   }


   //=== _OnPopupItemMouseEnter ============================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   // tpb 23/01/06 Allow highlighting of pushpin - and state 2 tools in pinned mode
   //-----------------------------------------------------------------------

   select._OnPopupItemMouseEnter = function(idx)
   {
      // Check Pushpin area
      if( idx == this._rows.length )
      {
         this._Outset(this._GetItemCell(idx));
         return;
      }

      // Now handle normal buttons
      if (idx < 0 || idx >= this._rows.length)
         return;

      // And highlight if this is a state 1 tool or state 2 tool in flyout mode
      var state = this._rows[idx].state;
      if (1 == state || (2 == state && this._mode == "flyout"))
      {
         this._Outset(this._GetItemCell(idx));
      }
   }


   //=== _OnPopupItemMouseLeave=============================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   // tpb 23/01/06 Allow highlighting of pushpin - and state 2 tools in pinned mode
   //-----------------------------------------------------------------------

   select._OnPopupItemMouseLeave = function(idx)
   {
      // Check Pushpin area
      if( idx == this._rows.length )
      {
         this._Flatten(this._GetItemCell(idx));
         return;
      }

      // Now handle normal buttons
      if (idx < 0 || idx >= this._rows.length)
         return;

      // And flatten this is not a state 2 tool in pinned mode
      if (2 != this._rows[idx].state || this._mode != "pinned")
      {
         this._Flatten(this._GetItemCell(idx));
      }
   }


   //=== _OnPopupItemSelect ================================================
   //
   // Called when a row from the popup has been selected
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 21/05/03 Written
   // sic 21/09/05 Modified for toolselect.
   // tpb 23/01/06 Handle "pinned" mode
   // tpb 24/01/06 Handle left and right button presses. We actually allow right for flyouts, because we can popup and select in one mouse click
   //              But disallow in "pinned" mode to avoid confusion
   //-----------------------------------------------------------------------

   select._OnPopupItemSelect = function(idx)
   {
      // Ignore right mouse events
      if( event && event.button == 2 && this._mode == "pinned" )
         return;

      var item = this._rows[idx];

      if (0 != item.state)
      {
         this._Flatten(this._GetItemCell(idx));
         if( this._popup != null )
            this._popup.hide();
         this.SetSelectedIndex(idx);

         // Inform the callback if one has been specified
         this._FireOnSelect();
      }
   }


   //=== _SetButtonAppearance ==============================================
   //
   // Set the appropriate button appearance given our current state.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 21/09/05 Written
   //-----------------------------------------------------------------------

   select._SetButtonAppearance = function()
   {
      var item = this._rows[this._selectedIndex];
      if( item == null )
         return;

      if (2 == item.state)
      {
         if( this._displayButton != null )
         {
            this._Inset(this._displayButton);
            this._displayButton.style.backgroundColor = this._State2ButtonFace;
         }
      }
      else if (0 == item.state)
      {
         if( this._displayButton != null )
         {
            this._Flatten(this._displayButton);
            this._displayButton.style.backgroundColor = "transparent";
         }
      }
      else
      {
         if (true == this._mouseOver)
         {
            if (true == this._mouseOverButton && true == this._mouseButtonDown)
               this._Inset(this._displayButton);
            else
               this._Outset(this._displayButton);
         }
         else
         {
            this._Flatten(this._displayButton);
         }

         if( this._displayButton != null )
            this._displayButton.style.backgroundColor = "transparent";
      }
   }


   //=== _SetCellAppearance ==============================================
   //
   // Set the appropriate cell appearance given our current state.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/01/06 Written - taken largely from SetButtonAppearance
   //-----------------------------------------------------------------------

   select._SetCellAppearance = function(idx)
   {
      // Get the cell and the item
      var cell = this._GetItemCell(idx);
      var item = this._rows[idx];
      if(cell == null || item == null)
         return;

      // Set it's HTML
      cell.innerHTML = this._GetItemHtml(idx);

      if (2 == item.state)
      {
         this._Inset(cell);
         cell.style.backgroundColor = this._State2ButtonFace;
      }
      else
      {
         this._Flatten(cell);
         cell.style.backgroundColor = "transparent";
      }
   }


   //=== _CreatePopup ======================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 21/05/03 Written
   // sic 21/09/05 Modified for toolselect.
   //-----------------------------------------------------------------------

   select._CreatePopup = function()
   {
      this._popup = window.createPopup();
      this._popup.document.ondragstart   = function() {return false}; // Make sure we can't drag
      this._popup.document.onselectstart = function() {return false}; // or select anything

      // Store the popup window in our object and set a _select object
      // on it for the popup's events to refer to.
      this._popupWindow = this._popup.document.parentWindow;
      this._popupWindow._select = this;

      // Build up the Popup body
      var html = new Array();

      html.push("<div id='popupdiv' style='padding-bottom:1px; padding-right:1px; " + 
                  "border-top:buttonhighlight solid 1px; border-left:buttonhighlight solid 1px; " +
                  "border-bottom:buttonshadow solid 1px; border-right:buttonshadow solid 1px; " +
                  "overflow:visible;'>");

      html.push(  "<span id='popupspan' style='position:relative; top:2px; left:1px; " + 
                    "cursor:default; overflow:auto;'>");

      for(var idx=0; idx < this._rows.length; idx++)
      {
         html.push( "<span style='border:buttonface solid 1px;' id='" + this.id + "Cell" + idx + "' " +
                    "onmouseenter='_select._OnPopupItemMouseEnter(" + idx + ")' " +
                    "onmouseleave='_select._OnPopupItemMouseLeave(" + idx + ")' " +
                    "onmouseup='_select._OnPopupItemSelect(" + idx + ")' " + 
                    ">" + this._GetItemHtml(idx) + "</span>");
      }

      var gHalfGrayStr = ColorToString(GetColorShade(artcam.GetSysColor(COLOR_BTNFACE), 2, artcam.GetSysColor(COLOR_BTNSHADOW), 1));

      html.push(    "<span id='" + this.id + "Cell" + idx + "' style='border:buttonface solid 1px; background-color:" + gHalfGrayStr + ";' ");
      html.push(      "onmouseenter='_select._OnPopupItemMouseEnter(" + idx + ")' ");
      html.push(      "onmouseleave='_select._OnPopupItemMouseLeave(" + idx + ")' ");
      html.push(      "onmousedown='_select.SetMode(\"pinned\")'>");
      html.push(      "<span style='height:30px;'>");
      html.push(        "<img src='" + this._sharedImagePath + "pin.gif'>");
      html.push(      "</span>");
      html.push(    "</span>");

      html.push(  "</span>");
      html.push("<div>");

      // Now set this as the document body
      this._popup.document.body.innerHTML = html.join("");

      // Now figure out it's dimensions for when we show it
      // We have to show and hide it once with a zero height to get it to render so we can retrieve the rendered dimensions   

      this._popup.show(this.offsetWidth + 1, 0, screen.width / 2, 0, this);   // Create popup with zero height
      this._popupHeight = this._popupWindow.popupdiv.offsetHeight - 1;      // Grab the html width from the popup table
      this._popupWidth  = this._popupWindow.popupspan.offsetWidth + 4;       // Grab the html height from the popup table
      this._popup.hide();

      // Now that the popup is ready, install the event handlers we want
      // called when the popup is used.

      this._popupWindow.document.body.onload = function()
      {
         this._select._OnPopupOpen();
      };

      this._popupWindow.document.body.onunload = function()
      {
         this._select._OnPopupClose();
      };
   }


   //=== _ShowPopup ========================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 21/05/03 Written
   // sic 21/09/05 Modified for toolselect.
   //-----------------------------------------------------------------------

   select._ShowPopup = function()
   {
      // If we are diabled, go no further
      if( this.disabled )
         return;

      // If the popup has never been created, create it.
      if( this._popup == null )
         this._CreatePopup();

      // If the popup is open, hide it. Otherwise we show it.
      if( this.IsOpen() )
         this._popup.hide();
      else
      {
         this._SetArrowBorder(this._Inset);
         // Show the popup. The width and height are worked out in the CreatePopup function
         var w = this._popupHeight > this.offsetHeight ? this._popupHeight : this.offsetHeight; // Allow it to get bigger if the main drop box has resized
         this._popup.show(this.offsetWidth + 1, 0, this._popupWidth, w, this);

         // Start the monitoring interval so we can have a close event.
         // There seems to be no other way around this as I can't catch a close event
         // on the window. IsOpen checks whether this interval is still running
         this._popupOpenInterval = setInterval(this.id + "._OpenIntervalCheck()", 100);
      }
   }


   //=== _Flatten ==========================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._Flatten = function(obj)
   {
      if( obj != null && obj.style != null )
         obj.style.border = "buttonface solid 1px";
   }


   //=== _Inset ============================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._Inset = function(obj)
   {
      if( obj != null && obj.style != null )
      {
         obj.style.borderBottom = obj.style.borderRight = "buttonhighlight solid 1px";
         obj.style.borderTop =    obj.style.borderLeft  = "buttonshadow solid 1px";
      }
   }


   //=== _Outset ===========================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._Outset = function(obj)
   {
      if( obj != null && obj.style != null )
      {
         obj.style.borderBottom = obj.style.borderRight = "buttonshadow solid 1px";
         obj.style.borderTop =    obj.style.borderLeft  = "buttonhighlight solid 1px";
      }
   }


   //=== _OpenIntervalCheck ================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/05/03 Written
   //-----------------------------------------------------------------------

   select._OpenIntervalCheck = function()
   {
      if(!this._popup || !this._popup.isOpen)
      {
         clearInterval(this._popupOpenInterval);
         this._popupOpenInterval = null;
      }
   }


   //=== _OnMouseMove ======================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   //-----------------------------------------------------------------------

   select._OnMouseMove = function()
   {
      if (0 == (event.button & 1)) this._mouseButtonDown = false;
   }

   //=== _OnMouseEnterTable ================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 21/09/05
   //-----------------------------------------------------------------------

   select._OnMouseEnterTable = function()
   {
      this._mouseOver = true;

      if (!this.IsOpen())
      {
         this._SetButtonAppearance();
         this._SetArrowBorder(this._Outset);
      }
   }


   //=== _OnMouseEnterButton ===============================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 21/09/05
   //-----------------------------------------------------------------------

   select._OnMouseEnterButton = function()
   {
      this._mouseOverButton = true;

      if (!this.IsOpen())
      {
         this._SetButtonAppearance();
      }
   }


   //=== _OnMouseLeaveTable ================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 21/09/05
   //-----------------------------------------------------------------------

   select._OnMouseLeaveTable = function()
   {
      this._mouseOver = false;

      if (!this.IsOpen())
      {
         this._SetButtonAppearance();
         this._SetArrowBorder(this._Flatten);
      }
   }


   //=== _OnMouseLeaveButton ===============================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 21/09/05
   //-----------------------------------------------------------------------

   select._OnMouseLeaveButton = function()
   {
      this._mouseOverButton = false;
      this._SetButtonAppearance();
   }


   //=== _OnMouseDown ======================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   // tpb 19/01/06 Added functionality to handle right and left clicks separately. Right click now 
   //-----------------------------------------------------------------------

   select._OnMouseDown = function()
   {
      // Which button was pressed?
      if(event.button == 1)
      {
         // Left mouse button
         this._mouseButtonDown = true;
         this._SetButtonAppearance();
      }
      else if(event.button == 2 && this._rows.length > 1)
      {
         // Right mouse button AND we have more than one item - fire off the popup
         this._ShowPopup();
      }
   }


   //=== _OnMouseUp ========================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05 Written
   // tpb 24/01/06 Only respond to left click up. Right click down, starts the popup
   //-----------------------------------------------------------------------

   select._OnMouseUp = function()
   {
      if (this._mouseButtonDown && event.button == 1)
      {
         this._mouseButtonDown = false;
         this._SetButtonAppearance();
         this._FireOnSelect();
      }
   }


   //=== _GetItemHtml ======================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 22/05/03 Written
   // sic 21/09/05 Modified for toolselect.
   // tpb 20/11/06 Changed alt title to be surrounded by double quotes as item.title can contain single quotes
   //-----------------------------------------------------------------------

   select._GetItemHtml = function(idx)
   {
      if (idx >= 0 && idx < this._rows.length && null != this._rows[idx])
      {
         var item = this._rows[idx];
         var src = " src='" + item.src + "' ";
         var alt = " alt=\"" + item.title + "\" ";
         var style = (0 == item.state ? " style='filter:alpha(opacity=50) gray(enabled=1);' " : "");

         return "<img width='30px' height='30px'" + src + alt + style + ">";
      }
      else
      {
         return "";
      }
   }


   //=== _OnKeyDown ========================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/05/03 Written
   //-----------------------------------------------------------------------

   select._OnKeyDown = function()
   {
      /*
      // Different things occur, depending on whether the popup is open or not
      switch(event.keyCode)
      {
      case 13: // Enter
      case 32: // Space
         if( this.IsOpen() )
            this._OnPopupItemSelect(this._lastSelected); // Make the selection with the current row
         else
            this._ShowPopup(); // Otherwise just show the popup
         break;

      case 33: // PgUp
      case 36: // Home - Select the first
         if( this.IsOpen() )
         {
            if(this._lastSelected == 0)
               break;
            this.SetSelectedIndex(0); 
            this._OnPopupItemMouseEnter(0);
         }
         else
         {
            if(this._selectedIndex == 0)
               break;
            this.SetSelectedIndex(0); 
            this._FireOnSelect();
         }
         break;

      case 34: // PgDown
      case 35: // End    - Select the last
         if( this.IsOpen() )
         {
            if(this._lastSelected == this._rows.length-1)
               break;
            this.SetSelectedIndex(this._rows.length-1); 
            this._OnPopupItemMouseEnter(this._rows.length-1);
         }
         else
         {
            if(this._selectedIndex == this._rows.length-1)
               break;
            this.SetSelectedIndex(this._rows.length-1); 
            this._FireOnSelect();
         }
         break;

      case 38: // Up - Select the previous
         if( this.IsOpen() )
         {
            if(this._lastSelected == 0)
               break;
            this.SetSelectedIndex(this._lastSelected-1); 
            this._OnPopupItemMouseEnter(this._lastSelected-1);
         }
         else
         {
            if(this._selectedIndex == 0)
               break;
            this.SetSelectedIndex(this._selectedIndex-1); 
            this._FireOnSelect();
         }
         break;

      case 40: // Down - Select the next
         if( this.IsOpen() )
         {
            if(this._lastSelected == this._rows.length-1)
               break;
            this.SetSelectedIndex(this._lastSelected+1); 
            this._OnPopupItemMouseEnter(this._lastSelected+1);
         }
         else
         {
            if(this._selectedIndex == this._rows.length-1)
               break;
            this.SetSelectedIndex(this._selectedIndex+1); 
            this._FireOnSelect();
         }
         break;
      }
      */
      // We need to stop this event continuing down the page
      event.returnValue = false;
      return false;
   }


   //=== _FireOnSelect =====================================================
   //
   // 
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/05/03 Written
   //-----------------------------------------------------------------------

   select._FireOnSelect = function()
   {
      if(this.onselect == null)
         return; // Nothing to fire

      if( this._FireOnSelectTimeout == null )
      {
         // Fire the onselect event after an instant timeout
         // This allows the page to update and other events to pass through first
         // It also means that we don't fire one on top of another.
         // If the timeout already exists, we don't make another call.
         this._FireOnSelectTimeout  = setTimeout(this.id + "._FireOnSelectEval()", 0);
      }
   }


   //=== _FireOnSelectEval =================================================
   //
   //
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // tpb 23/05/03 Written
   //-----------------------------------------------------------------------

   select._FireOnSelectEval = function()
   {
      if(this.onselect == null)
         return; // Nothing to fire
      this._FireOnSelectTimeout = null; // Cancel this timeout, it will allow another to fire
      eval(this.onselect);
   }


   //=== _DestroyPopup =====================================================
   //
   // Destroys the popup window.
   //
   // History
   // Who When     What
   // --- -------- ---------------------------------------------------------
   // sic 19/09/05
   //-----------------------------------------------------------------------

   select._DestroyPopup = function()
   {
      if (null != this._popup)
      {
         if (this._popup.isOpen) this._popup.hide();
         this._popup = null;
      }
   }


} // END.