/* vim: set ft=objc ts=4 nowrap: */ /* * ExtendedOutlineView.h * * Copyright (c) 2002 * * Author: Andreas Heppel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ExtendedOutlineView.h" #include "Track.h" #include "Constants.h" #include "Functions.h" #include "ExtendedOutlineCell.h" static NSImage *collapsed = nil; static NSImage *expanded = nil; static NSImage *unexpandable = nil; @implementation ExtendedOutlineView - (id) initWithFrame: (NSRect) aRect { self = [super initWithFrame: aRect]; collapsed = [NSImage imageNamed: @"common_outlineCollapsed.tiff"]; expanded = [NSImage imageNamed: @"common_outlineExpanded.tiff"]; unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable.tiff"]; return self; } - (void) dealloc { RELEASE(collapsed); RELEASE(expanded); RELEASE(unexpandable); [super dealloc]; } - (NSImage *) dragImageForRows: (NSArray *) dragRows event: (NSEvent *) dragEvent dragImageOffset: (NSPoint *) dragImageOffset { if ([dragRows count] > 1) { int i; // if there is at least one data track we use the data icon for (i = 0; i < [dragRows count]; i++) { id item = [self itemAtRow: [[dragRows objectAtIndex: i] intValue]]; if ([[(Track*)item type] isEqual: @"data"] || [[(Track*)item type] isEqual: @"dir"]) return [NSImage imageNamed: @"iconDnDMulti.tiff"]; } // otherwise we use the audio icon return [NSImage imageNamed: @"iconDnDAudioMulti.tiff"]; } else { id item = [self itemAtRow: [[dragRows objectAtIndex: 0] intValue]]; if ([[(Track*)item type] isEqual: @"data"] || [[(Track*)item type] isEqual: @"dir"]) return [NSImage imageNamed: @"iconDnD.tiff"]; else return [NSImage imageNamed: @"iconDnDAudio.tiff"]; } return [super dragImageForRows: dragRows event: dragEvent dragImageOffset: dragImageOffset]; } - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { /* * Between our own windows we allow copy and move, but * we do not allow to drag outside, other than to remove the item. */ if (isLocal == YES) return NSDragOperationCopy | NSDragOperationPrivate; else return NSDragOperationNone; } #if 0 - (unsigned int)draggingEntered:(id )sender { [super draggingEntered: sender]; return [self validateDragging: sender]; } - (unsigned int)draggingUpdated:(id )sender { [super draggingUpdated: sender]; return [self validateDragging: sender]; } - (unsigned int) validateDragging: (id )sender { NSDragOperation sourceDragMask; NSArray *allTypes; sourceDragMask = [sender draggingSourceOperationMask]; /* * We don't allow to copy inside the same project. * Only move. */ if (([sender draggingSource] == self) && (sourceDragMask == NSDragOperationCopy)) return NSDragOperationNone; /* * A single directory can be dragged with a copy operation. * This will lead to the contents of this directory being * inserted into the CD description. This is not allowed when * dragging more than one file or when it's not a directory. */ allTypes = [[sender draggingPasteboard] types]; if (([allTypes containsObject: NSFilenamesPboardType]) && (sourceDragMask & NSDragOperationCopy)) { // We retrieve property list of files from paste board NSArray *sourcePaths = [[sender draggingPasteboard] propertyListForType: NSFilenamesPboardType]; if (!sourcePaths) { NSData *pbData = [[sender draggingPasteboard] dataForType: NSFilenamesPboardType]; if (pbData) { sourcePaths = [NSUnarchiver unarchiveObjectWithData: pbData]; } } if ([sourcePaths count] == 1) { NSFileManager *fileMan = [NSFileManager defaultManager]; BOOL isDir; if ([fileMan fileExistsAtPath: [sourcePaths objectAtIndex: 0] isDirectory: &isDir] && !isDir) return NSDragOperationNone; } else return NSDragOperationNone; } if ((sourceDragMask & NSDragOperationPrivate) == NSDragOperationPrivate) { return NSDragOperationPrivate; } else if ((sourceDragMask & NSDragOperationCopy) == NSDragOperationCopy) { return NSDragOperationCopy; } else { return NSDragOperationAll; } return NSDragOperationNone; } #endif - (void) concludeDragOperation:(id )sender { id dragSource; NSDragOperation sourceDragMask; sourceDragMask = [sender draggingSourceOperationMask]; dragSource = [sender draggingSource]; /* * If this dnd operation was internal we need to check * whether it was a move or a copy. */ if (![dragSource isKindOfClass: [self class]]) return; if ([[dragSource dataSource] respondsToSelector: @selector(removeMovedTracks:)]) { [[dragSource dataSource] removeMovedTracks: (sourceDragMask & NSDragOperationPrivate) == NSDragOperationPrivate]; } } - (void) drawRow: (int) rowIndex clipRect: (NSRect) aRect { NSRect drawingRect, imageRect; NSTableColumn *aTableColumn; ExtendedOutlineCell *cell; NSCell *imageCell; int i, startingColumn, endingColumn; float x_pos; if (_dataSource == nil) { return; } imageCell = nil; /* Using columnAtPoint: here would make it called twice per row per drawn rect - so we avoid it and do it natively */ if (rowIndex >= _numberOfRows) { return; } /* Determine starting column as fast as possible */ x_pos = NSMinX (aRect); i = 0; while ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns)) { i++; } startingColumn = (i - 1); if (startingColumn == -1) { startingColumn = 0; } /* Determine ending column as fast as possible */ x_pos = NSMaxX (aRect); // Nota Bene: we do *not* reset i while ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns)) { i++; } endingColumn = (i - 1); if (endingColumn == -1) { endingColumn = _numberOfColumns - 1; } /* Draw the row between startingColumn and endingColumn */ for (i = startingColumn; i <= endingColumn; i++) { if (i != _editedColumn || rowIndex != _editedRow) { id item = [self itemAtRow: rowIndex]; aTableColumn = [_tableColumns objectAtIndex: i]; cell = (ExtendedOutlineCell*)[aTableColumn dataCellForRow: rowIndex]; if ([_delegate respondsToSelector: @selector(outlineView:willDisplayCell:forTableColumn:item:)]) { [_delegate outlineView: self willDisplayCell: cell forTableColumn: aTableColumn item: item]; } [cell setObjectValue: [_dataSource outlineView: self objectValueForTableColumn: aTableColumn byItem: item]]; drawingRect = [self frameOfCellAtColumn: i row: rowIndex]; if (aTableColumn == _outlineTableColumn) { NSImage *image = nil; int level = 0; float indentationFactor = 0.0; // float originalWidth = drawingRect.size.width; if (![self isExpandable: item]) { if ([cell respondsToSelector: @selector(collapsedImage)]) { image = [cell collapsedImage]; } if (!image) image = unexpandable; } else { if ([self isItemExpanded: item]) { if ([cell respondsToSelector: @selector(expandedImage)]) { image = [cell expandedImage]; } if (!image) image = expanded; } else { if ([cell respondsToSelector: @selector(collapsedImage)]) { image = [cell collapsedImage]; } if (!image) image = collapsed; } } level = [self levelForItem: item]; indentationFactor = _indentationPerLevel * level; if (image) { imageCell = [[NSCell alloc] initImageCell: image]; } if (_indentationMarkerFollowsCell) { imageRect.origin.x = drawingRect.origin.x + indentationFactor; imageRect.origin.y = drawingRect.origin.y; } else { imageRect.origin.x = drawingRect.origin.x; imageRect.origin.y = drawingRect.origin.y; } if (image) { imageRect.size.width = [image size].width; imageRect.size.height = [image size].height; [imageCell drawWithFrame: imageRect inView: self]; drawingRect.origin.x += indentationFactor + [image size].width + 5; drawingRect.size.width -= indentationFactor + [image size].width + 5; RELEASE(imageCell); } else { drawingRect.origin.x += indentationFactor; drawingRect.size.width -= indentationFactor; } } [cell drawWithFrame: drawingRect inView: self]; } } } @end @implementation CDOutlineView - (id) initWithFrame: (NSRect) frameRect { NSTableColumn *column; id cell; NSRect frame = NSMakeRect(0,0,frameRect.size.width,frameRect.size.height); self = [super initWithFrame: frameRect]; if (self) { NSColor *bg = colorForKey(@"ProjectOutlineColor"); trackView = [[ExtendedOutlineView alloc] initWithFrame: frame]; [trackView setIndentationPerLevel: 10]; [trackView setRowHeight: 20]; [trackView setAllowsColumnSelection: NO]; [trackView setAllowsColumnReordering: NO]; [trackView setAllowsColumnResizing: YES]; [trackView setAllowsEmptySelection: YES]; [trackView setAllowsMultipleSelection: YES]; [trackView setAutoresizesAllColumnsToFit: YES]; [trackView setAutoresizesOutlineColumn: YES]; [trackView setIndentationMarkerFollowsCell: YES]; [trackView setVerticalMotionCanBeginDrag: NO]; [trackView sizeLastColumnToFit]; // we set the delegate and data source in awakeFromNib!! // we don't know them, yet!! column = [[NSTableColumn alloc] initWithIdentifier: @"Index"]; [column setEditable: NO]; [column setResizable: NO]; [[column headerCell] setStringValue: _(@"Common.nr")]; [[column dataCell] setAlignment: NSRightTextAlignment]; [column setMinWidth: 30]; [column setWidth: 30]; [trackView addTableColumn: column]; [column release]; cell = [[ExtendedOutlineCell alloc] init]; column = [[NSTableColumn alloc] initWithIdentifier: @"Track"]; [column setEditable: YES]; [column setResizable: YES]; [[column headerCell] setStringValue: _(@"Common.track")]; [column setMinWidth: 150]; [column setWidth: 300]; [column setDataCell: cell]; AUTORELEASE(cell); [trackView addTableColumn: column]; [trackView setOutlineTableColumn: column]; [column release]; column = [[NSTableColumn alloc] initWithIdentifier: @"Length"]; [column setEditable: NO]; [column setResizable: YES]; [[column headerCell] setStringValue: _(@"Common.length")]; [[column dataCell] setAlignment: NSCenterTextAlignment]; [column setMinWidth: 90]; [column setMaxWidth: 90]; [trackView addTableColumn: column]; [column release]; scrollView = [[NSScrollView alloc] initWithFrame: frame]; [scrollView setHasHorizontalScroller: NO]; [scrollView setHasVerticalScroller: YES]; [scrollView setDocumentView: trackView]; [scrollView setBorderType: NSBezelBorder]; [scrollView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; // We set our outline view background color if (bg) { [trackView setBackgroundColor: bg]; [scrollView setBackgroundColor: bg]; } [self addSubview: scrollView]; } return self; } - (void) dealloc { [trackView release]; [scrollView release]; [super dealloc]; } - (void) awakeFromNib { [trackView setDelegate: delegate]; [trackView setDataSource: delegate]; // We register the table view for dragged types [trackView registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, AudioCDPboardType, BurnTrackPboardType, nil]]; } - (ExtendedOutlineView *) trackView { return trackView; } - (void) reloadData { [trackView reloadData]; } - (void) forwardInvocation: (NSInvocation *)invocation { if ([trackView respondsToSelector: [invocation selector]]) [invocation invokeWithTarget: trackView]; else [self doesNotRecognizeSelector: [invocation selector]]; } @end