macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Understanding the NSTableView Class
Pages: 1, 2, 3, 4, 5

The Data Editing Process

The NSTableView class also allows its tabular data to be edited while being displayed. Furthermore, the displayed data can be edited either directly on the table view itself (inline editing), or indirectly through a separate view (panel editing). Choosing the appropriate technique depends largely on the amount and type of data being edited. However, the data-source controller must derive its internal buffer from a mutable collection class, such as an NSMutableArray, if it is to support editing.

Inline Editing is most suitable when the editable data fits the confines of its enclosing view. One notable example is a spreadsheet document whose alphanumeric data are edited directly within their respective cells. The edit process itself is usually initiated by either a click or double-click action administered to the desired cell. Since the entire process occurs directly on the table widget, minimal resources are required to track and display the resulting changes.



The NSTableView class supports inline editing through a special data-source protocol message,

      - (void)tableView:(NSTableView *)aTable setObjectValue:(id)aData 
                    forTableColumn:(NSTableColumn *)aCol row:(int)aRow

This message is sent by the view to its data-source controller whenever a user double-clicks on the data to be edited. It passes the following four parameters to the controller:

  • aTable, a reference to the table view generating the edit signal;
  • aData, the data being edited encapsulated in an NSObject;
  • aCol, a reference to the column subview where the edited data is located; and
  • aRow, an index to the table row wherein the data is located.

Figure 6 shows the signal flow of a typical inline editing session on a single-column table view. When the user double-clicks on a table cell, aTable sends the [tableView:setObjectValue:forTableColumn:row:] message to the data-source controller, aDatSrc. The controller updates its internal buffer, aBuffer, by sending it a [replaceObjectAtIndex:withObject:] message. Then, once the buffer is updated, aTable updates its display by sending itself a [reloadData] message, which starts a new data-source process.

A multiple-column table view also follows the same signal flow for its inline editing session. The only difference is that aDatSrc must now determine which column subview is being updated by sending an [identifier] message to the forTableColumn parameter, aCol. It then sends an [objectForKey:] to aBuffer, assuming the buffer is based on NSMutableDictionary. The buffer responds by returning a reference to the NSMutableArray object that corresponds to the specified column. Finally, aDatSrc updates the appropriate entry by sending a [replaceObjectAtIndex:withObject:] message with the edited data to the mutable array.

Figure 7. A typical inline editing process
Figure 7. A typical inline editing process

Listing 3 shows how inline-editing could be implemented in the data-source controller. Notice that the edited value is first compared against the buffered value before it is committed to the buffer. Interestingly enough, just double-clicking on a table cell and then moving the selection focus elsewhere also causes aTable to send a [tableView:setObjectValue:forTableColumn:row:] message to aDatSrc. This is a wasted message as the previously selected data may have remained unchanged. Verifying if an actual change did occurred helps minimize the resource overhead involved with buffer access.

Listing 3. Implementing an inline-editing process.

 - (void)tableView:(NSTableView *)aTable setObjectValue:(id)aData 
   forTableColumn:(NSTableColumn *)aCol 
			row:(int)aRow
{
     id loc_id, loc_data;
      NSString     *loc_log;
      
      // identify the table column
      loc_id = [aCol identifier];
      if ([loc_id isKindOfClass:[NSString class]])
      {
           // determine the old cell value
           loc_data = [[self testBuffer] objectForKey:loc_id];
           loc_data = [loc_data objectAtIndex:aRow];
           
           // compare the old cell value against the "new" value
           if (![loc_data isEqual:aData])
           {
                // update the data buffer
                [[[self testBuffer] objectForKey:loc_id]
                replaceObjectAtIndex:aRow withObject:aData];
           }
      }
}

Also, to enable inline editing for a specific column subview, send a [setEditable:] message with a BOOL argument of YES to that subview. The message tells the subview to interpret any double-action signal as the start of the inline editing session. For instance, to enable inline editing for the column subview, editMe, of aTable, send the message as follows:

      [[aTable tableColumnWithIdentifier:@"editMe"] setEditable:YES];

to that subview. Here, the [tableColumnWithIdentifier:] message is used to reference the column subview with the unique NSString identifier @"editMe". To determine if the same subview is editable, send an [isEditable] message as follows:

      BOOL aFlag;
      //..
      aFlag = [[aTable tableColumnWithIdentifier:@"editMe"] isEditable];

Inline editing is restricted to the amount of visible space provided by the table cell. If the edited information consists of a longer phrase, portions of it will be obscured from view. Also, inline editing is unsuitable in situations where the edited data is part of a larger set. For instance, editing customer information may mean editing the customer's name, which is visible, as well as the address, which is not. For these scenarios, panel editing is the more suitable approach.

Panel Editing uses a separate view to contain the relevant table information for editing. Once editing is done, a user action is required to confirm and commit the changes back into the buffer. Panel editing always require additional resources to display and track the changes made to the data. This is especially true when multiple data items are being edited at one time.

Figure 8 shows the signal flow of a typical panel-editing process for a single column table view. The controller, aDatEdit, is assigned as the action target of aTable using the [setTarget:] message.

[aTable setTarget:aDatEdit];

Also, all double-action signals generated by aTable are then routed to the doubleAction method of aDatEdit using the [setDoubleAction:] message. When a user double-clicks on a table cell, aTable directly invokes the doubleAction method of aDatSrc. Since the [setDoubleAction:] message takes a SEL argument, the doubleAction method needs to be recast using the @selector directive.

[aTable setDoubleAction:@selector(doubleAction)];

When aDatEdit receives the double-action signal, it displays its editing panel, aPanel, which is subclassed from NSPanel in this example. It then determines the currently selected row by sending a [selectedRow] message to aTable (not shown). It sends the row index to aDatSrc, which returns the data corresponding to that row. aDatEdit then populates its editing panel with the received table data.

Once the user finished changing the data and has decided to commit those changes, aDatEdit disposes of aPanel and sends the edited data back to aDatSrc for storage into its internal buffer. aDatSrc then sends a [reloadData] message to aTable, thus starting a new data-source process. For another example of the panel editing process, examine the DemoEdit.m source file of the TableDemo application.

Figure 8. A typical panel editing process.
Figure 8. A typical panel-editing process

In order for panel editing to work, make sure to disable inline editing for the column subview that will implement a panel editing session. If left enabled, the double-action signal will be intercepted instead by that subview and, as a result, the method assigned using the [setDoubleAction:] message will not be invoked. For example, to disable inline editing on the column subview, @"panelEdit", of aTable, send a [setEditable:] message with a BOOL value of NO to that subview.


      [[aTable tableColumnWithIdentifier:@"panelEdit"] setEditable:NO];
 

Since the editable states of each column subview can be individually set, NSTableView can support a mixture of inline and panel editing sessions. The type of data displayed on each column will then dictate the appropriate session for that column. For instance, a double-click action on a table column displaying the customer's surname starts an inline editing session whereas the same action on the column displaying the customer's address starts a panel editing session.

Pages: 1, 2, 3, 4, 5

Next Pagearrow