This is IKBrowserManager.m in view mode; [Download] [Up]
/* File IKBrowserManager.m Release 1.2, 7 June 1994 Copyright (C) 1994 by H. Scott Roy This code is part of IconKit, a general toolbox for drag-and-drop applications. IconKit is free for noncommercial use, but costs money for a commercial license. You should have received a copy of the license agreement with this file. If not, a copy of the license and the complete source of IconKit can be obtained from the author: H. Scott Roy 2573 Stowe Ct. Northbrook, IL 60062-8103 iconkit@cs.stanford.edu For your editing convenience, this file is best viewed using an editor that automatically wraps long lines, in a fixed point font at 80 columns, with tabs every 4 spaces. */ /* ========================================================================== */ /* Browsing and editing graph is one of the most natural uses for an IKBrowser. This class defines a graph manager that automates the generic operations. End applications only need to specify the graph structure. The IKBrowserManager takes care of filling in a browser with the appropriate information, responding to editing and dragging, selecting paths, and so forth. */ #import <appkit/appkit.h> #import <apps/InterfaceBuilder.h> #import "iconkit.h" @implementation IKBrowserManager /* ========================================================================== */ /* We use a pretty picture to represent the browser manager in InterfaceBuilder. */ - (NXImage *) getIBImage { char buf [MAXPATHLEN + 1]; id bundle; NXImage * image = [NXImage findImageNamed: "BrowserManager"]; if (image == nil) { bundle = [NXBundle bundleForClass: [self class]]; if ([bundle getPath: buf forResource: "BrowserManager" ofType: "tiff"]) { image = [[NXImage alloc] initFromFile: buf]; [image setName: "BrowserManager"]; } } return image; } /* ========================================================================== */ /* The initialization given here should work correctly whenever either unarchiving from a typed stream or initializing from a nib. The initBrowser:root: method is the designated initializer, and should be used to initialize the graph manager programmatically. */ - init; { return [self initBrowser: nil root: nil]; } - initBrowser: (id) theBrowser root: (id) theRoot; { if ((self = [super init]) != nil) { browser = theBrowser; root = theRoot; [self awake]; } return self; } - free { int i; id cells = [[browser iconPath] cellList]; IKdprintf ("freeing browser manager: %p\n", self); for (i = 0; i < [cells count]; i++) [[[cells objectAt: i] delegate] removeUser: self]; [browser setDelegate: nil]; if ([[browser window] delegate] == self) [[browser window] setDelegate: nil]; return [super free]; } - awakeFromNib { [self awake]; return self; } - awake { NXAtom * pasteTypes; int n = 0; pasteTypes = [[root class] respondsTo: @selector(pasteTypes)] ? [[root class] pasteTypes]: NULL; if (pasteTypes != NULL) while (pasteTypes[n] != NULL) n++; [[browser iconPath] registerForDraggedTypes: pasteTypes count: n]; [browser setDelegate: self]; return self; } /* ========================================================================== */ /* Here are the various set and get methods. Note that the set methods must conform to the InterfaceBuilder specifications, and so cannot coordinate the browser and root. Programmers should send an explicity awake message if they need to set the browser or root after initialization. */ - browser { return browser; } - root { return root; } - setBrowser: theBrowser; { [browser setDelegate: nil]; browser = theBrowser; [self awake]; return self; } - setRoot: theRoot; { root = theRoot; [self awake]; return self; } /* ========================================================================== */ /* Filling the browser columns is straightforward. The selected cell in the previous column is used to find the graph node that should be displayed. */ - (int) browser: sender fillMatrix: matrix inColumn: (int) column; { id node, selection, children; int k = 0, n, i; if (column == 0) node = root; else { selection = [sender getSelectionInColumn: column - 1]; node = ([selection count] == 1) ? [selection objectAt: 0]: nil; [selection free]; } if (column == 0 || ![node isLeaf]) { children = [node children]; n = [children count]; { id child [n]; for (i = 0; i < n; i++) if (![child[k] = [children objectAt: i] isHidden]) k++; [self sort: child count: k for: column]; for (i = 0; i < k; i++) [[[matrix addRow] cellAt: i : 0] setDelegate: child[i]]; } } return k; } /* ========================================================================== */ /* Setting the column icon is as easy as filling the column. The only trick part is checking for a multiple selection. When the IKBrowserManager installs a new object at the head of a column, it registers for notification messages so that it can correctly update the browser when the object's contents change. The getSelectedCells method is broken when running in InterfaceBuilder test mode. Accordingly, we need to jump through some hoops to get things to work correctly in that particular case. */ - browser: sender setColumnIcon: cell for: (int) column; { id node = nil, selection; if (column == 0) node = root; else { selection = [sender getSelectionInColumn: column - 1]; if ([selection count] == 1) node = [selection objectAt: 0]; else if ([selection count] > 1) { node = [[root class] respondsTo: @selector(multipleSelectionClass)] ? [[[[root class] multipleSelectionClass] alloc] init]: [[IKList alloc] init]; [node appendList: selection]; } [selection free]; } [node addUser: self]; [[cell setDelegate: node] removeUser: self]; return self; } /* ========================================================================== */ /* An IKBrowser sends out messages when it deletes columns, as well as when it adds them, so that the browser delegate can clear out the cells. */ - browser: sender emptyMatrix: matrix inColumn: (int) column { [[matrix cellList] makeObjectsPerform: @selector(setDelegate:) with: nil]; return self; } - browser: sender removeColumnIcon: cell for: (int) column { [[cell setDelegate: nil] removeUser: self]; return self; } /* ========================================================================== */ /* An IKBrowser alerts its delegate at two points in the update process: just before the window is flushed to show any changes, and just after. IKBrowserManager uses the first message to select the current cell in the icon path for editing. This behavior mirrors Workspace and makes it easier to read long titles. The browserDidChange method is the perfect place to update any inspectors attached to the browser. */ - browserWillFinishChange: sender { [[sender iconPath] editCellAt: 0 : [sender lastColumn]]; return self; } - browserDidChange: sender { return self; } /* ========================================================================== */ /* The provided sorting method does nothing. The FileViewer example shows how to call qsort to do sorting by name, and can easily be adapated to other sorting keys. */ - sort: (id *) folders count: (int) n for: (int) column { return self; } /* ========================================================================== */ /* Here is an action method to select an icon in the browser. It is intended to be sent by an IKCell or an IKShelf--something with a delegate that the IKBrowserManager can retrieve. The IKBrowserManager finds a path to the delegate and sets it in the browser. If the delegate cannot be found, the browser is reset to the root node. */ - takePathFrom: sender { id node, path = nil; char pathName [IKMAXPATHLENGTH]; int i; if ([sender respondsTo: @selector(delegate)] && [node = [sender delegate] respondsTo: @selector(pathFromNode:)]) { path = [node pathFromNode: root]; pathName[0] = '/'; pathName[1] = '\0'; if (path != nil) for (i = 1; i < [path count]; i++) { strcat (pathName, [[path objectAt: i] name]); strcat (pathName, "/"); } [browser setPath: pathName]; } return path; } /* ========================================================================== */ /* Here are action messages that let one edit the graph. A deleteLink: message erases the link between the current selection and its parent. A delete: message erases the selection, and a duplicate: message copies it. */ - deleteLink: sender { int n = [browser selectedColumn]; id parent, selection; if (n != -1) { parent = [[browser iconPath] objectInColumn: n]; if ([parent respondsTo: @selector(removeChildren:)]) { selection = [browser getSelectionInColumn: n]; [parent removeChildren: selection]; [selection free]; } } return self; } - delete: sender { int n = [browser selectedColumn]; id selection; if (n != -1) { selection = [browser getSelectionInColumn: n]; [selection makeObjectsPerform: @selector(addUser:) with: self]; [self deleteLink: self]; [selection freeObjects]; [selection free]; } return self; } - duplicate: sender { int n = [browser selectedColumn], i; id parent, selection; if (n != -1) { parent = [[browser iconPath] objectInColumn: n]; if ([parent respondsTo: @selector(addChildren:)]) { selection = [browser getSelectionInColumn: n]; for (i = 0; i < [selection count]; i++) [selection replaceObjectAt: i with: [[selection objectAt: i] copy]]; [parent addChildren: selection]; [selection free]; } } return self; } - newFolder: sender { static int count = 1; char name [100]; id delegate = [[browser iconPath] objectInColumn: [browser selectedColumn] + 1]; if ([delegate respondsTo: @selector(addChild:)]) { sprintf (name, "NewFolder%d", count++); [delegate addChild: [[[root class] alloc] init: name]]; } return self; } - toggleLeaf: sender { id selection = [browser getSelectionInColumn: [browser selectedColumn]], node; int i; for (i = 0; i < [selection count]; i++) { node = [selection objectAt: i]; if ([node respondsTo: @selector(setLeaf:)]) [node setLeaf: ![node isLeaf]]; } [selection free]; return self; } /* ========================================================================== */ /* The IKBrowserManager is responsible for updating the browser whenever one of the columns changes. It does this by scanning the icon path for the changed column, then reloading it. */ - didChangeProperties: sender { return [self changed: sender]; } - didAddChild: sender { return [self changed: sender]; } - didAddChildren: sender { return [self changed: sender]; } - didRemoveChild: sender { return [self changed: sender]; } - didRemoveChildren: sender { return [self changed: sender]; } - willFree: sender { if (sender == root) root = nil; return self; } - changed: who { int r, n; char path [IKMAXPATHLENGTH]; id iconPath = [browser iconPath]; [iconPath getRow: &r andCol: &n ofCell: [iconPath cellWithDelegate: who]]; if (n != -1 ) { [browser getPath: path toColumn: [browser lastColumn]]; [browser resetColumn: n usingPath: path]; } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.