// Copyright 2003 - 2004 Nathan Hamblen
// Modified BSD license:
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "CompletingFormCell.h"

// Subclass of NSComboBoxCell (NOT of NSFormCell) to be used by CompletingForm for each row. The cell draws a title (like NSFormCell) and a combobox to the right of the title (instead of a simple textfield). The comboboxes are limited to MAX_ITEMS unique entries, and the NSComoboBox is set to autocomplete. The superclass draws the combobox and a member NSTextCell draws the title.
@implementation CompletingFormCell

// space between the label and combobox
static float LABEL_PADDING =  5.0f;

// limit on number of items in the combobox applied in insertItemWithObjectValue:
static int MAX_ITEMS = 10;

#pragma mark  Initilization

// Initializer called by CompletingForm (from NSForm)
- (id)initTextCell:(NSString *)aString
{
  [super initTextCell:aString];

  mTitleCell = [[NSCell alloc]initTextCell:aString];

  [self setEditable:YES];
  [self setCompletes:YES];

  return self;
}

#pragma mark  Specify title & attrs

// The title is the label that appears to the left of the combobox.
- (void)setTitle:(NSString *)aString
{
  [mTitleCell setTitle:aString];
}

- (NSString *)title
{
  return [mTitleCell title];
}

- (NSFont *)titleFont
{
  return [mTitleCell font];
}

- (void)setTitleFont:(NSFont *)fontObj
{
  [mTitleCell setFont:fontObj];
}

- (NSTextAlignment)titleAlignment
{
  return [mTitleCell alignment];
}

- (void)setTitleAlignment:(NSTextAlignment)mode
{
  [mTitleCell setAlignment:mode];
}

- (void)setTitleWidth:(float)width
{
  mTitleWidth = width;
}

// Uses mTitleWidth if it is positive (thus intentionally specified); otherwise calculates a width for the title including some padding that is limited to aSize.
- (float)titleWidth:(NSSize)aSize
{
  NSSize minSize = [mTitleCell cellSize];
  minSize.width += LABEL_PADDING; 
// space between title and entry combobox
  minSize.width = (int) minSize.width;

  if (mTitleWidth < 0.0f)
    return (minSize.width < aSize.width)  ? minSize.width : aSize.width;
  return mTitleWidth;
}

// Uses mTitleWidth if it is positive (thus intentionally specified); otherwise calculates a width for the title including some padding.
- (float) titleWidth
{
  return (mTitleWidth < 0.0f) ? [mTitleCell cellSize].width + LABEL_PADDING : mTitleWidth;
}

#pragma mark  Handle mouse

// We don't want i-frame cursors over the title.
- (void)resetCursorRect:(NSRect)cellFrame inView:(NSView *)controlView
{
  cellFrame.origin.x += mTitleWidth;
  cellFrame.size.width -= mTitleWidth;
  [super resetCursorRect:cellFrame inView:controlView];
}

// NSForm will incorrectly set the editor frame if we don't catch its call here.
- (void)selectWithFrame:(NSRect)aRect
                 inView:(NSView *)controlView
                 editor:(NSText *)textObj
               delegate:(id)anObject
                  start:(int)selStart
                 length:(int)selLength
{
  aRect.origin.x += mTitleWidth;
  aRect.size.width -= mTitleWidth;
  [super selectWithFrame:aRect inView:controlView editor:textObj
                delegate:anObject start:selStart length:selLength];
}

// Adjust the editor frame as needed and filter out any calls that are outside of the combobox's editing area.
- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView
               editor:(NSText *)textObj delegate:(id)anObject
                event:(NSEvent *)theEvent
{
  NSPoint locationInView = [controlView convertPoint:[theEvent locationInWindow]
                                            fromView:nil];
  aRect.origin.x += mTitleWidth;
  aRect.size.width -= mTitleWidth;

  
// if not in the title area
  if (NSPointInRect(locationInView, aRect))
  {    
// if over the drop down button
    if (locationInView.x > (NSMaxX(aRect) - 20.0f))
    {
      [super selectWithFrame:aRect inView:controlView editor:textObj
                    delegate:anObject start:0 length:[[self title] length]];
      [self trackMouse:theEvent
                inRect:aRect
                ofView:controlView
          untilMouseUp:YES];
    }
    else 
// is over the editing area, let it through
      [super editWithFrame:aRect inView:controlView
                    editor:textObj delegate:anObject
                     event:theEvent];
  }
}

#pragma mark  Control list

// Add item if not already in list. If it is, bump it to the top of the list. Limit the list to moxItems.
- (void)insertItemWithObjectValue:(id)object atIndex:(int)index
{
  int existingIndex = [self indexOfItemWithObjectValue:object];
  int numberOfItems;
  if (existingIndex != NSNotFound)
    [self removeItemAtIndex:existingIndex];

  [super insertItemWithObjectValue:object atIndex:index];
  while ((numberOfItems = [self numberOfItems]) > MAX_ITEMS)
    [self removeItemAtIndex:numberOfItems-1];
}

#pragma mark  Draw

- (void) drawLabelWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
  NSSize minSize = [mTitleCell cellSize];
  cellFrame.origin.y += (cellFrame.size.height - minSize.height) / 2;
  cellFrame.size.height = minSize.height;
  cellFrame.size.width = (MAX([self titleWidth] - LABEL_PADDING, minSize.width));
  [mTitleCell drawWithFrame:cellFrame inView:controlView];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
  float titleWidth = [self titleWidth];

  [self drawLabelWithFrame:cellFrame inView:controlView];
  cellFrame.origin.x += titleWidth;
  cellFrame.size.width -= titleWidth;
  [super drawWithFrame:cellFrame
                inView:controlView];
}

#pragma mark  Deallocation

- (void) dealloc
{
  [mTitleCell release];
}

@end