Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update File Outline outline view to use view-based table cells #17

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
1,075 changes: 553 additions & 522 deletions trunk/Bricksmith/Resources/Interface/English.lproj/LDrawDocument.xib

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion trunk/Bricksmith/Source/Application/Document/LDrawDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
// class LDrawDocument
//
////////////////////////////////////////////////////////////////////////////////
@interface LDrawDocument : NSDocument <ViewportArrangerDelegate>
@interface LDrawDocument : NSDocument <ViewportArrangerDelegate, NSOutlineViewDataSource, NSOutlineViewDelegate>
{
IBOutlet DocumentToolbarController *toolbarController;
IBOutlet NSObjectController *bindingsController;
Expand Down Expand Up @@ -86,6 +86,11 @@
NSArray * markedSelection; // if we are mid-marquee selection, this is an array of the previously selected directives before drag started
}

/// Whether the document is in the process of updating the current selection in
/// the outline view. This is used to prevent an infinite loop of selection
/// change callbacks, specifically in -[LDrawDocument docChanged:].
@property (nonatomic) BOOL isMidSelection;

// Accessors
- (LDrawFile *) documentContents;
- (NSWindow *)foremostWindow;
Expand Down
118 changes: 64 additions & 54 deletions trunk/Bricksmith/Source/Application/Document/LDrawDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -3636,8 +3636,8 @@ - (id)outlineView:(NSOutlineView *)outlineView
//**** NSOutlineViewDataSource ****
//========== outlineView:objectValueForTableColumn:byItem: =====================
//
// Purpose: Returns the representation of item given for the given table
// column.
// Purpose: Returns the representation of the given item for the given table
// column, as either a string or attributed string object.
//
//==============================================================================
- (id) outlineView:(NSOutlineView *)outlineView
Expand Down Expand Up @@ -3919,48 +3919,40 @@ - (BOOL)outlineView:(NSOutlineView *)outlineView
#pragma mark -
#pragma mark Delegate

//**** NSOutlineView ****
//========== outlineView:willDisplayCell:forTableColumn:item: ==================
//
// Purpose: Returns the representation of item given for the given table
// column.
//
//==============================================================================
- (void) outlineView:(NSOutlineView *)outlineView
willDisplayCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
item:(id)item
{
//========== outlineView:viewForTableColumn:item: ==============================
/// Returns the representation of the given item for the given table column.
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSTableCellView *cellView = [outlineView makeViewWithIdentifier:@"FileOutlineCellView" owner:nil];
// Either an NSString, or an NSAttributedString.
id theText = [self outlineView:outlineView objectValueForTableColumn:tableColumn byItem:item];

NSString *imageName = nil;
NSImage *theImage;

if([item isKindOfClass:[LDrawDirective class]])
imageName = [item iconName];

if(imageName == nil || [imageName isEqualToString:@""])
theImage = nil;
else
theImage = [NSImage imageNamed:imageName];

[(IconTextCell *)cell setImage:theImage];

}//end outlineView:willDisplayCell:forTableColumn:item:

cellView.imageView.image = theImage;
cellView.textField.objectValue = theText;

return cellView;
}

//**** NSOutlineView ****
//========== outlineViewSelectionDidChange: ====================================
//
// Purpose: We have selected a different something in the file contents.
// We need to show it as selected in the OpenGL viewing area.
// This means we may have to change the active model or step in
// order to display the selection.
//
//==============================================================================
/// We have selected a different something in the file contents. We need to show
/// it as selected in the OpenGL viewing area. This means we may have to change
/// the active model or step in order to display the selection.
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
NSOutlineView *outlineView = [notification object];
NSOutlineView *outlineView = notification.object;
NSArray *selectedObjects = [self selectedObjects];
id lastSelectedItem = [outlineView itemAtRow:[outlineView selectedRow]];
id lastSelectedItem = [outlineView itemAtRow:outlineView.selectedRow];
LDrawMPDModel *selectedModel = [self selectedModel];
LDrawStep *selectedStep = [self selectedStep];
NSInteger selectedStepIndex = 0;
Expand All @@ -3971,19 +3963,19 @@ - (void)outlineViewSelectionDidChange:(NSNotification *)notification
// selecting parts can trigger OpenGL commands, we should make sure we have
// a context active, but we should also restore the current context when
// we're done.
NSOpenGLContext *originalContext = [NSOpenGLContext currentContext];
NSOpenGLContext *originalContext = NSOpenGLContext.currentContext;
[[LDrawApplication sharedOpenGLContext] makeCurrentContext];

//Deselect all the previously-selected directives
// (clears the internal directive flag used for drawing)
for(counter = 0; counter < [self->selectedDirectives count]; counter++)
[[selectedDirectives objectAtIndex:counter] setSelected:NO];
for(counter = 0; counter < self->selectedDirectives.count; counter++)
[selectedDirectives[counter] setSelected:NO];

//Tell the newly-selected directives that they just got selected.
[selectedDirectives release];
selectedDirectives = [selectedObjects retain];
for(counter = 0; counter < [self->selectedDirectives count]; counter++)
[[selectedDirectives objectAtIndex:counter] setSelected:YES];
for(counter = 0; counter < self->selectedDirectives.count; counter++)
[selectedDirectives[counter] setSelected:YES];

// Update things which need to take into account the entire selection.
// The order matters: the search panel unregisters itself as the active colorwell
Expand Down Expand Up @@ -4084,8 +4076,8 @@ - (void) LDrawGLViewMouseNotPositioning:(LDrawGLView *)glView
//**** LDrawGLView ****
//========== LDrawGLView:acceptDrop: ===========================================
//
// Purpose: The user has deposited some drag-anddrop parts into an
// LDrawGLView. Now they need to be imported into the model.
// Purpose: The user has deposited some drag-and-drop parts into an
// LDrawGLView. Now they need to be imported into the model.
//
// Notes: Just like in -duplicate: and
// -outlineView:acceptDrop:item:childIndex:, we appropriate the
Expand Down Expand Up @@ -4675,10 +4667,12 @@ - (void)drawerWillOpen:(NSNotification *)notification
// notification on our document after many change notifications
// on parts. See docChanged for more.
//
// Notification: LDrawDirectiveDidChangeNotification
//
//==============================================================================
- (void) partChanged:(NSNotification *)notification
{
LDrawDirective *changedDirective = [notification object];
LDrawDirective *changedDirective = notification.object;
LDrawFile *docContents = [self documentContents];

// Since the document sends out part changes too, make sure it isn't the doc
Expand All @@ -4695,10 +4689,10 @@ - (void) partChanged:(NSNotification *)notification
// to refresh drawing, and we ned it to redo our menus.
NSNotification * doc_notification =
[NSNotification notificationWithName:LDrawDirectiveDidChangeNotification
object:docContents];
object:docContents];

// Notification is queued and coalesced;
[[NSNotificationQueue defaultQueue]
[NSNotificationQueue.defaultQueue
enqueueNotification:doc_notification
postingStyle:NSPostASAP
coalesceMask:NSNotificationCoalescingOnName|NSNotificationCoalescingOnSender
Expand All @@ -4719,26 +4713,42 @@ - (void) partChanged:(NSNotification *)notification
// operation. This is where we do the expensive stuff like
// resync the hierarchy and update the menus.
//
// Notification: LDrawDirectiveDidChangeNotification
//
//==============================================================================
- (void)docChanged:(NSNotification *)notification
{
// This functionality was in partChanged through Bricksmith 3.0.
[fileContentsOutline selectObjects:selectedDirectives];
[fileContentsOutline reloadData];

[self updateInspector];

// Technically we don't need to redo the model menu on every UI edit. In the
// future we should add specific notifications or directive observations to
// detect this case. But 3.0 and earlier ran this once for every edit (when
// part changes were escalated to doc changes) and then twice on real model
// changes (because we'd hit the case on the model and again on the doc).
//
// In practice it doesn't matter - for now, for the test cases we have,
// rebuilding the menus is relatively cheap. A user can only add so many
// MPD parts to a file without going completely insane.
[self addModelsToMenus];

// -------------------------------------------------------------
// Due to using a view-based outline (post-Bricksmith 3.1), this had to
// change in order to work properly with the behavioral change of outline
// views regarding selection across calls to -reloadData.

// We don't want -selectObjects: below to bring us to an infinite loop
// because the outline view is sending out *another* selection changed
// notification!
if (!self.isMidSelection) {
self.isMidSelection = YES;
[fileContentsOutline reloadData];

[self updateInspector];

[fileContentsOutline selectObjects:selectedDirectives];

// Technically we don't need to redo the model menu on every UI edit. In the
// future we should add specific notifications or directive observations to
// detect this case. But 3.0 and earlier ran this once for every edit (when
// part changes were escalated to doc changes) and then twice on real model
// changes (because we'd hit the case on the model and again on the doc).
//
// In practice it doesn't matter - for now, for the test cases we have,
// rebuilding the menus is relatively cheap. A user can only add so many
// MPD parts to a file without going completely insane.
[self addModelsToMenus];

// Let's not forget to allow future changes!
self.isMidSelection = NO;
}
}//end docChanged:


Expand Down
7 changes: 7 additions & 0 deletions trunk/Bricksmith/Source/Application/General/RelatedParts.m
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ - (id) initWithFilePath:(NSString *)filePath
else
{
#if DEBUG
// Where related.ldr includes comments throughout, it ends up
// filling the console with output we probably don't care about.
// Filtering for the "!" prefix may be overly generic if we want to
// actually catch something unexpected here, but as the majority of
// meta commands we could possibly care about include the prefix, it
// seems like a good balance.
if ([parsedField containsString:@"!"])
printf("Unparsable META command: %s\n", [orig_line UTF8String]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I’m not sure what the actual intention of this logging is, it may be that something just needs updated so that normal meta commands aren’t reaching this; or maybe this logging should be removed altogether?

I was just having trouble doing some debugging with the console filled with every comment or meta command in the related parts file so I did this so I wouldn’t have to deal with it. If there is something else you would rather do let me know.

#endif
}
Expand Down
4 changes: 2 additions & 2 deletions trunk/Bricksmith/Source/LDraw/Support/LDrawDirective.m
Original file line number Diff line number Diff line change
Expand Up @@ -785,10 +785,10 @@ - (BOOL)isAncestorInList:(NSArray *)containers
//==============================================================================
- (void) noteNeedsDisplay
{
[[NSNotificationCenter defaultCenter]
[NSNotificationCenter.defaultCenter
postNotificationName:LDrawDirectiveDidChangeNotification
object:self];
}//end setNeedsDisplay
}//end noteNeedsDisplay


//========== registerUndoActions: ==============================================
Expand Down
2 changes: 1 addition & 1 deletion trunk/Bricksmith/Source/LDraw/Support/LDrawGLRenderer.m
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,7 @@ - (void) activeModelDidChange:(NSNotification *)notification

[self->delegate LDrawGLRendererNeedsRedisplay:self];

}//end displayNeedsUpdating
}//end activeModelDidChange



Expand Down
4 changes: 2 additions & 2 deletions trunk/Bricksmith/Source/Widgets/LDrawFileOutlineView.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ - (NSIndexSet *) selectObjects:(NSArray *)objects
NSInteger counter = 0;

//Gather up the indices of the pasted objects.
for(counter = 0; counter < [objects count]; counter++)
for(counter = 0; counter < objects.count; counter++)
{
currentObject = [objects objectAtIndex:counter];
currentObject = objects[counter];
indexOfObject = [self rowForItem:currentObject];
[indexesToSelect addIndex:indexOfObject];
}
Expand Down