home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / YellowBox / Kits / MiscTableScroll-138.1 / Palettes / MiscTableScroll / Framework / MiscTableScrollEdit.M < prev    next >
Encoding:
Text File  |  1998-03-31  |  16.0 KB  |  503 lines

  1. //=============================================================================
  2. //
  3. //    Copyright (C) 1996,1997,1998 by Paul S. McCarthy and Eric Sunshine.
  4. //        Written by Paul S. McCarthy and Eric Sunshine.
  5. //                All Rights Reserved.
  6. //
  7. //    This notice may not be removed from this source code.
  8. //
  9. //    This object is included in the MiscKit by permission from the authors
  10. //    and its use is governed by the MiscKit license, found in the file
  11. //    "License.rtf" in the MiscKit distribution.  Please refer to that file
  12. //    for a list of all applicable permissions and restrictions.
  13. //
  14. //=============================================================================
  15. //-----------------------------------------------------------------------------
  16. // MiscTableScrollEdit.M
  17. //
  18. //    Text cell editing support for MiscTableScroll.
  19. //
  20. //-----------------------------------------------------------------------------
  21. //-----------------------------------------------------------------------------
  22. // $Id: MiscTableScrollEdit.M,v 1.8 98/03/29 23:56:01 sunshine Exp $
  23. // $Log:    MiscTableScrollEdit.M,v $
  24. // Revision 1.8  98/03/29  23:56:01  sunshine
  25. // v138.1: Now uses NSColor's "system" colors for editing.
  26. // Fixed bug: Was sending NSControlTextDidChangeNotification in
  27. // -textDidBeginEditing: rather than NSControlTextDidBeginEditingNotification.
  28. // 
  29. // Revision 1.7  98/03/23  07:48:00  sunshine
  30. // v134.1: Added -suspendEditing / -resumeEditing.  Eliminated delegate
  31. // -tableScroll:edit:atRow:column: methods.
  32. // 
  33. // Revision 1.6  97/06/22  10:22:48  sunshine
  34. // v127.1: Fixed bug: Wasn't grabbing a copy of cell when initiating editing;
  35. // only referenced it.  In lazy mode, scrolling around would cause the
  36. // contents of the referenced cell to change (even just the inital scroll-to-
  37. // visible), thus causing a bus-load of bugs.  Now grabs copy of cell instead.
  38. // Fixed bug: Keyboard cursor was lagging behind as TAB was used to change
  39. // from cell to cell during editing.
  40. // First-responder is now returned to TableScroll when cell editing ends via
  41. // Return key, rather than reverting uselessly to the window.
  42. //-----------------------------------------------------------------------------
  43. #import "MiscTableScrollPrivate.h"
  44. #import "MiscTableBorder.h"
  45. #import "MiscTableView.h"
  46. extern "Objective-C" {
  47. #import    <AppKit/NSCell.h>
  48. #import <AppKit/NSControl.h>
  49. #import    <AppKit/NSText.h>
  50. }
  51. extern "C" {
  52. #import    <stdlib.h>    // malloc(), free()
  53. }
  54.  
  55. typedef MiscDelegateFlags DF;
  56.  
  57. @implementation MiscTableScroll(Edit)
  58.  
  59. - (BOOL)isEditing { return editInfo.editing; }
  60.  
  61. //-----------------------------------------------------------------------------
  62. // cellFrameAtRow:column:
  63. //-----------------------------------------------------------------------------
  64. - (NSRect)cellFrameAtRow:(int)row column:(int)col
  65.     {
  66.     return [self convertRect:[tableView cellFrameAtRow:row column:col]
  67.         fromView:tableView];
  68.     }
  69.  
  70.  
  71. //-----------------------------------------------------------------------------
  72. // getRow:andCol:forPoint:
  73. //-----------------------------------------------------------------------------
  74. - (BOOL)getRow:(int*)row column:(int*)col forPoint:(NSPoint)point
  75.     {
  76.     return [tableView getRow:row column:col
  77.         forPoint:[tableView convertPoint:point fromView:self]];
  78.     }
  79.  
  80.  
  81. //-----------------------------------------------------------------------------
  82. // - controlNotify:with:
  83. //-----------------------------------------------------------------------------
  84. - (void)controlNotify:(NSString*)name with:(NSNotification*)n
  85.     {
  86.     [[NSNotificationCenter defaultCenter]
  87.     postNotificationName:name object:self
  88.         userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  89.         [n object], @"NSFieldEditor", 0]];
  90.     }
  91.  
  92.  
  93. //-----------------------------------------------------------------------------
  94. // forceEditorDirty:
  95. //    Unlike the NEXTSTEP 3.x Text system which *always* sends 
  96. //    -textWillEnd:, OPENSTEP 4.x only sends -textShouldEndEditing: if the 
  97. //    text was indeed edited.  However, -suspendEditing & -resumeEditing 
  98. //    work by ending the old edit session and beginning a new one (without 
  99. //    the informing the delegate or the cell).  The problem is that by 
  100. //    ending the old edit session, the text system loses the knowledge that 
  101. //    the user had changed the text, and consequently -textShouldEndEditing: 
  102. //    may not be called.  This "hack" works around the problem by forcing 
  103. //    the text system to flag itself as dirty.  (FIXME: The text is always 
  104. //    forced dirty even if the old session was not dirty.  This errs on the 
  105. //    side of safety, though.) 
  106. //-----------------------------------------------------------------------------
  107. - (void)forceEditorDirty:(NSText*)editor
  108.     {
  109.     [editor replaceCharactersInRange:NSMakeRange(0,0) withString:@"x"];
  110.     [editor setSelectedRange:NSMakeRange(0,1)];
  111.     [editor delete:0];
  112.     }
  113.  
  114.  
  115. //-----------------------------------------------------------------------------
  116. // setupFieldEditor:
  117. //-----------------------------------------------------------------------------
  118. - (void)setupFieldEditor:(NSText*)editor
  119.     {
  120.     [editor setDrawsBackground:YES];
  121.     [editor setBackgroundColor:[NSColor textBackgroundColor]];
  122.     [editor setTextColor:[NSColor textColor]];
  123.     [editor setNeedsDisplay:YES];
  124.     }
  125.  
  126.  
  127. //-----------------------------------------------------------------------------
  128. // resumeEditing
  129. //-----------------------------------------------------------------------------
  130. - (void)resumeEditing
  131.     {
  132.     if (editInfo.editing && --editInfo.suspended == 0)
  133.     {
  134.     int const row = editInfo.row;
  135.     int const col = editInfo.col;
  136.  
  137.     NSText* const editor = [[self window] fieldEditor:YES forObject:self];
  138.     editInfo.editor = [editor retain];
  139.     id const cell = editInfo.cell;
  140.  
  141.     NSRect rect = [tableView cellInsideAtRow:row column:col];
  142.     [tableView scrollRectToVisible:rect];
  143.  
  144.     [cell selectWithFrame:rect inView:tableView editor:editor
  145.         delegate:self start:0 length:0];
  146.     [self setupFieldEditor:editor];
  147.     [self forceEditorDirty:editor];
  148.     [editor selectAll:0];
  149.  
  150.     [self setClickedRow:row column:col];
  151.     }
  152.     }
  153.  
  154.  
  155. //-----------------------------------------------------------------------------
  156. // suspendEditing
  157. //-----------------------------------------------------------------------------
  158. - (void)suspendEditing
  159.     {
  160.     if (editInfo.editing && editInfo.suspended++ == 0)
  161.     {
  162.     id const cell = editInfo.cell;
  163.     NSText* const editor = editInfo.editor;
  164.     NSString* const s = [[[editor string] copy] autorelease];
  165.     [editor setDelegate:0]; // Prevent -textDidEndEditing: invocation.
  166.     [cell endEditing:editor];
  167.     [cell setStringValue:s];
  168.     [editor release];
  169.     editInfo.editor = 0;
  170.     }
  171.     }
  172.  
  173.  
  174. //-----------------------------------------------------------------------------
  175. // cleanupEditing
  176. //-----------------------------------------------------------------------------
  177. - (void)cleanupEditing
  178.     {
  179.     [editInfo.cell release];
  180.     editInfo.cell = 0;
  181.     [editInfo.editor release];
  182.     editInfo.editor = 0;
  183.     editInfo.editing = NO;
  184.     editInfo.suspended = 0;
  185.     }
  186.  
  187.  
  188. //-----------------------------------------------------------------------------
  189. // finishEditing
  190. //-----------------------------------------------------------------------------
  191. - (BOOL)finishEditing
  192.     {
  193.     NSWindow* win = [self window];
  194.     if ([self isEditing] && [win makeFirstResponder:win])
  195.     [win endEditingFor:editInfo.editor];
  196.     return ![self isEditing];
  197.     }
  198.  
  199.  
  200. //-----------------------------------------------------------------------------
  201. // abortEditing
  202. //-----------------------------------------------------------------------------
  203. - (BOOL)abortEditing
  204.     {
  205.     BOOL const rc = editInfo.editing;
  206.     if (rc)
  207.     {
  208.     editInfo.editing = NO;
  209.     int const r = editInfo.row;
  210.     int const c = editInfo.col;
  211.  
  212.     if (editInfo.editor != 0)
  213.         [editInfo.cell endEditing:editInfo.editor];
  214.     [self cleanupEditing];
  215.  
  216.     id d;
  217.     if ((d = [self responsibleDelegate:DF::DEL_ABORT_EDIT_AT]) != 0)
  218.         [d tableScroll:self abortEditAtRow:r column:c];
  219.  
  220.     if ((d = [self responsibleDelegate:DF::DEL_DID_EDIT_AT]) != 0)
  221.         [d tableScroll:self didEdit:NO atRow:r column:c];
  222.  
  223.     [self drawCellAtRow:r column:c];
  224.     }
  225.     return rc;
  226.     }
  227.  
  228.  
  229. //-----------------------------------------------------------------------------
  230. // getNext:editRow:column:
  231. //-----------------------------------------------------------------------------
  232. - (BOOL)getNext:(BOOL)foreward
  233.     editRow:(MiscCoord_P*)p_row
  234.     column:(MiscCoord_P*)p_col
  235.     {
  236.     MiscCoord_P pr = *p_row;
  237.     MiscCoord_P pc = *p_col;
  238.  
  239.     MiscTableBorder const* const br = rowInfo.border;
  240.     MiscTableBorder const* const bc = colInfo.border;
  241.  
  242.     MiscCoord_V const v_row = br->physicalToVisual( pr );    // Start pos.
  243.     MiscCoord_V const v_col = bc->physicalToVisual( pc );
  244.  
  245.     MiscCoord_V vr = v_row;
  246.     MiscCoord_V vc = v_col;
  247.  
  248.     do  {
  249.     if (foreward)
  250.         {
  251.         if (++vc >= num_cols)
  252.         {
  253.         vc = 0;
  254.         if (++vr >= num_rows)
  255.             vr = 0;
  256.         pr = br->visualToPhysical( vr );
  257.         }
  258.         }
  259.     else
  260.         {
  261.         if (--vc < 0)
  262.         {
  263.         vc = num_cols - 1;
  264.         if (--vr < 0)
  265.             vr = num_rows - 1;
  266.         pr = br->visualToPhysical( vr );
  267.         }
  268.         }
  269.     if (vc == v_col && vr == v_row)        // Wrapped to start pos.
  270.         return NO;
  271.     pc = bc->visualToPhysical( vc );
  272.     }
  273.     while (![self canEdit:0 atRow:pr column:pc]);
  274.  
  275.     *p_row = pr;
  276.     *p_col = pc;
  277.     return YES;
  278.     }
  279.  
  280.  
  281. //-----------------------------------------------------------------------------
  282. // getPreviousEditRow:column:
  283. //-----------------------------------------------------------------------------
  284. - (BOOL)getPreviousEditRow:(MiscCoord_P*)p_row column:(MiscCoord_P*)p_col
  285.     {
  286.     return [self getNext:NO editRow:p_row column:p_col];
  287.     }
  288.  
  289.  
  290. //-----------------------------------------------------------------------------
  291. // getNextEditRow:column:
  292. //-----------------------------------------------------------------------------
  293. - (BOOL)getNextEditRow:(MiscCoord_P*)p_row column:(MiscCoord_P*)p_col
  294.     {
  295.     return [self getNext:YES editRow:p_row column:p_col];
  296.     }
  297.  
  298.  
  299. //-----------------------------------------------------------------------------
  300. // textDidEndEditing:
  301. //-----------------------------------------------------------------------------
  302. - (void)textDidEndEditing:(NSNotification*)n
  303.     {
  304.     id d;
  305.     NSText* theText = [n object];
  306.     int const why = [[[n userInfo] objectForKey:@"NSTextMovement"] intValue];
  307.  
  308.     editInfo.editing = NO;
  309.     NSString* s = [[[theText string] copy] autorelease];
  310.     [editInfo.cell endEditing:editInfo.editor];
  311.     int const r = editInfo.row;
  312.     int const c = editInfo.col;
  313.     NSString* t = [self stringValueAtRow:r column:c];
  314.     BOOL changed = ![s isEqualToString:t];
  315.     if (changed)
  316.     {
  317.     if ((d = [self responsibleDelegate:DF::DEL_SET_STRINGVALUE_AT]) != 0)
  318.         changed = [d tableScroll:self setStringValue:s atRow:r column:c];
  319.     else
  320.         [[self cellAtRow:r column:c] setStringValue:s];
  321.     }
  322.     [self cleanupEditing];
  323.  
  324.     if ((d = [self responsibleDelegate:DF::DEL_DID_EDIT_AT]) != 0)
  325.     [d tableScroll:self didEdit:changed atRow:r column:c];
  326.  
  327.     [self drawCellAtRow:r column:c];
  328.  
  329.     if (changed)
  330.     {
  331.     if ([self autoSortRows])
  332.         [self sortRow:r];
  333.     if ([self autoSortColumns])
  334.         [self sortColumn:c];
  335.     }
  336.  
  337.     if ((d = [self responsibleDelegate:DF::DEL_TEXT_DID_END]) != 0)
  338.     [self controlNotify:NSControlTextDidEndEditingNotification with:n];
  339.     else
  340.     {
  341.     MiscCoord_P row = (MiscCoord_P)r;
  342.     MiscCoord_P col = (MiscCoord_P)c;
  343.  
  344.     switch (why)
  345.         {
  346.         case NSReturnTextMovement:
  347.         [[self window] makeFirstResponder:tableView];
  348.         [self sendAction];
  349.         break;
  350.     
  351.         case NSTabTextMovement:
  352.         if ([self getNext:YES editRow:&row column:&col])
  353.             [self editCellAtRow:row column:col];
  354.         else
  355.             [[self window] selectNextKeyView:self];
  356.         break;
  357.     
  358.         case NSBacktabTextMovement:
  359.         if ([self getNext:NO editRow:&row column:&col])
  360.             [self editCellAtRow:row column:col];
  361.         else
  362.             [[self window] selectPreviousKeyView:self];
  363.         break;
  364.     
  365.         // FIXME: CTRL-TAB => nextText, CTRL-SHIFT-TAB => previousText
  366.         default:
  367.         break;
  368.         }
  369.     }
  370.     }
  371.  
  372.  
  373. //-----------------------------------------------------------------------------
  374. // textDidBeginEditing:
  375. //-----------------------------------------------------------------------------
  376. - (void)textDidBeginEditing:(NSNotification*)n
  377.     {
  378.     [self controlNotify:NSControlTextDidBeginEditingNotification with:n];
  379.     }
  380.  
  381.  
  382. //-----------------------------------------------------------------------------
  383. // textDidChange:
  384. //-----------------------------------------------------------------------------
  385. - (void)textDidChange:(NSNotification*)n
  386.     {
  387.     [self controlNotify:NSControlTextDidChangeNotification with:n];
  388.     }
  389.  
  390.  
  391. //-----------------------------------------------------------------------------
  392. // textShouldBeginEditing:
  393. //-----------------------------------------------------------------------------
  394. - (BOOL)textShouldBeginEditing:(NSText*)text
  395.     {
  396.     id d = [self responsibleDelegate:DF::DEL_TEXT_WILL_CHANGE];
  397.     if (d != 0)
  398.     return [d control:(NSControl*)self textShouldBeginEditing:text];
  399.     return YES;
  400.     }
  401.  
  402.  
  403. //-----------------------------------------------------------------------------
  404. // textShouldEndEditing:
  405. //-----------------------------------------------------------------------------
  406. - (BOOL)textShouldEndEditing:(NSText*)text
  407.     {
  408.     id d = [self responsibleDelegate:DF::DEL_TEXT_WILL_END];
  409.     if (d != 0)
  410.     return [d control:(NSControl*)self textShouldEndEditing:text];
  411.     return YES;
  412.     }
  413.  
  414.  
  415. //-----------------------------------------------------------------------------
  416. // edit:atRow:column:
  417. //    NOTE *1*: Must set color *after* cell initiates editing, else cell will
  418. //    install its own colors making text selection invisible.
  419. //-----------------------------------------------------------------------------
  420. - (void)edit:(NSEvent*)ev atRow:(MiscCoord_P)row column:(MiscCoord_P)col
  421.     {
  422.     id d;
  423.     if ((d = [self responsibleDelegate:DF::DEL_WILL_EDIT_AT]) != 0)
  424.     [d tableScroll:self willEditAtRow:row column:col];
  425.  
  426.     id const cell = [[self cellAtRow:row column:col] copyWithZone:[self zone]];
  427.     id const editor = [[self window] fieldEditor:YES forObject:self];
  428.  
  429.     editInfo.editing = YES;
  430.     editInfo.row = row;
  431.     editInfo.col = col;
  432.     editInfo.editor = [editor retain];
  433.     editInfo.cell = [cell retain];
  434.  
  435.     NSRect const rect = [tableView cellInsideAtRow:row column:col];
  436.     [tableView scrollRectToVisible:rect];
  437.     if (ev != 0)
  438.     [cell editWithFrame:rect inView:tableView editor:editor
  439.         delegate:self event:ev];
  440.     else
  441.     [cell selectWithFrame:rect inView:tableView editor:editor
  442.         delegate:self start:0 length:[[cell stringValue] length]];
  443.  
  444.     [self setupFieldEditor:editor];        // NOTE *1*
  445.     }
  446.  
  447.  
  448. //-----------------------------------------------------------------------------
  449. // canEdit:atRow:column:
  450. //-----------------------------------------------------------------------------
  451. - (BOOL)canEdit:(NSEvent*)ev atRow:(MiscCoord_P)row column:(MiscCoord_P)col
  452.     {
  453.     id d = [self responsibleDelegate:DF::DEL_CAN_EDIT_AT];
  454.     if (d != 0)
  455.     return [d tableScroll:self canEdit:ev atRow:row column:col];
  456.  
  457.     id const cell = [self cellAtRow:row column:col];
  458.     if ([cell respondsToSelector:@selector(tableScroll:canEdit:atRow:column:)])
  459.     return [cell tableScroll:self canEdit:ev atRow:row column:col];
  460.  
  461.     return (ev == 0 || [ev clickCount] == 2) && 
  462.         [cell respondsToSelector:@selector(isEditable)] &&
  463.         [cell respondsToSelector:@selector(isEnabled)] &&
  464.         [cell isEditable] && [cell isEnabled];
  465.     }
  466.  
  467.  
  468. //-----------------------------------------------------------------------------
  469. // editIfAble:atRow:column:
  470. //-----------------------------------------------------------------------------
  471. - (BOOL)editIfAble:(NSEvent*)ev atRow:(MiscCoord_P)row column:(MiscCoord_P)col
  472.     {
  473.     if ([self canEdit:ev atRow:row column:col])
  474.     {
  475.     [self edit:ev atRow:row column:col];
  476.     return YES;
  477.     }
  478.     return NO;
  479.     }
  480.  
  481.  
  482. //-----------------------------------------------------------------------------
  483. // editCellAtRow:column:
  484. //-----------------------------------------------------------------------------
  485. - (void)editCellAtRow:(MiscCoord_P)row column:(MiscCoord_P)col
  486.     {
  487.     [self clearSelection];
  488.     if ([self trackingBy] == MISC_ROW_BORDER)
  489.     {
  490.     [self selectRow:row];
  491.     [self setCursorRow:row];
  492.     }
  493.     else
  494.     {
  495.     [self selectColumn:col];
  496.     [self setCursorColumn:col];
  497.     }
  498.     [self setClickedRow:row column:col];
  499.     [self edit:0 atRow:row column:col];
  500.     }
  501.  
  502. @end
  503.