/[gnustep]/gnustep/core/gui/Source/NSBrowser.m
ViewVC logotype

Contents of /gnustep/core/gui/Source/NSBrowser.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.102 - (show annotations) (download)
Mon Sep 29 21:15:37 2003 UTC (20 years, 6 months ago) by FredKiefer
Branch: MAIN
CVS Tags: gui-0_9_0
Changes since 1.101: +8 -2 lines
Patch by Christopher Culver:
Define empty [sizeToFit] method to override NSControl implementation.

1 /** <title>NSBrowser</title>
2
3 <abstract>Control to display and select from hierarchal lists</abstract>
4
5 Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc.
6
7 Author: Scott Christley <scottc@net-community.com>
8 Date: 1996
9 Author: Felipe A. Rodriguez <far@ix.netcom.com>
10 Date: August 1998
11 Author: Franck Wolff <wolff@cybercable.fr>
12 Date: November 1999
13 Author: Mirko Viviani <mirko.viviani@rccr.cremona.it>
14 Date: September 2000
15 Author: Fred Kiefer <FredKiefer@gmx.de>
16 Date: September 2002
17
18 This file is part of the GNUstep GUI Library.
19
20 This library is free software; you can redistribute it and/or
21 modify it under the terms of the GNU Library General Public
22 License as published by the Free Software Foundation; either
23 version 2 of the License, or (at your option) any later version.
24
25 This library is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 Library General Public License for more details.
29
30 You should have received a copy of the GNU Library General Public
31 License along with this library; see the file COPYING.LIB.
32 If not, write to the Free Software Foundation,
33 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 */
35
36 #include <math.h> // (float)rintf(float x)
37 #include "config.h"
38 #include <Foundation/NSArray.h>
39 #include <Foundation/NSDebug.h>
40 #include <Foundation/NSException.h>
41 #include "AppKit/NSBrowser.h"
42 #include "AppKit/NSBrowserCell.h"
43 #include "AppKit/AppKitExceptions.h"
44 #include "AppKit/NSScroller.h"
45 #include "AppKit/NSCell.h"
46 #include "AppKit/NSColor.h"
47 #include "AppKit/NSFont.h"
48 #include "AppKit/NSScrollView.h"
49 #include "AppKit/NSGraphics.h"
50 #include "AppKit/NSMatrix.h"
51 #include "AppKit/NSTableHeaderCell.h"
52 #include "AppKit/NSEvent.h"
53 #include "AppKit/NSWindow.h"
54 #include "AppKit/NSBezierPath.h"
55
56 DEFINE_RINT_IF_MISSING
57
58 /* Cache */
59 static float scrollerWidth; // == [NSScroller scrollerWidth]
60 static NSTextFieldCell *titleCell;
61
62 #define NSBR_COLUMN_SEP 4
63 #define NSBR_VOFFSET 2
64
65 #define NSBR_COLUMN_IS_VISIBLE(i) \
66 (((i)>=_firstVisibleColumn)&&((i)<=_lastVisibleColumn))
67
68 //
69 // Internal class for maintaining information about columns
70 //
71 @interface NSBrowserColumn : NSObject <NSCoding>
72 {
73 @public
74 BOOL _isLoaded;
75 id _columnScrollView;
76 id _columnMatrix;
77 NSString *_columnTitle;
78 }
79
80 - (void) setIsLoaded: (BOOL)flag;
81 - (BOOL) isLoaded;
82 - (void) setColumnScrollView: (id)aView;
83 - (id) columnScrollView;
84 - (void) setColumnMatrix: (id)aMatrix;
85 - (id) columnMatrix;
86 - (void) setColumnTitle: (NSString *)aString;
87 - (NSString *) columnTitle;
88 @end
89
90 @implementation NSBrowserColumn
91
92 - (id) init
93 {
94 [super init];
95
96 _isLoaded = NO;
97
98 return self;
99 }
100
101 - (void) dealloc
102 {
103 TEST_RELEASE(_columnScrollView);
104 TEST_RELEASE(_columnMatrix);
105 TEST_RELEASE(_columnTitle);
106 [super dealloc];
107 }
108
109 - (void) setIsLoaded: (BOOL)flag
110 {
111 _isLoaded = flag;
112 }
113
114 - (BOOL) isLoaded
115 {
116 return _isLoaded;
117 }
118
119 - (void) setColumnScrollView: (id)aView
120 {
121 ASSIGN(_columnScrollView, aView);
122 }
123
124 - (id) columnScrollView
125 {
126 return _columnScrollView;
127 }
128
129 - (void) setColumnMatrix: (id)aMatrix
130 {
131 ASSIGN(_columnMatrix, aMatrix);
132 }
133
134 - (id) columnMatrix
135 {
136 return _columnMatrix;
137 }
138
139 - (void) setColumnTitle: (NSString *)aString
140 {
141 if (!aString)
142 aString = @"";
143
144 ASSIGN(_columnTitle, aString);
145 }
146
147 - (NSString *) columnTitle
148 {
149 return _columnTitle;
150 }
151
152 - (void) encodeWithCoder: (NSCoder *)aCoder
153 {
154 int dummy = 0;
155
156 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isLoaded];
157 [aCoder encodeObject: _columnScrollView];
158 [aCoder encodeObject: _columnMatrix];
159 [aCoder encodeValueOfObjCType: @encode(int) at: &dummy];
160 [aCoder encodeObject: _columnTitle];
161 }
162
163 - (id) initWithCoder: (NSCoder *)aDecoder
164 {
165 int dummy = 0;
166
167 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isLoaded];
168 _columnScrollView = [aDecoder decodeObject];
169 if (_columnScrollView)
170 RETAIN(_columnScrollView);
171 _columnMatrix = [aDecoder decodeObject];
172 if (_columnMatrix)
173 RETAIN(_columnMatrix);
174 [aDecoder decodeValueOfObjCType: @encode(int) at: &dummy];
175 _columnTitle = [aDecoder decodeObject];
176 if (_columnTitle)
177 RETAIN(_columnTitle);
178 return self;
179 }
180
181 @end
182
183 // NB: this is used in the NSFontPanel too
184 @interface GSBrowserTitleCell: NSTableHeaderCell
185 @end
186
187 @implementation GSBrowserTitleCell
188 - (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
189 {
190 if (NSIsEmptyRect (cellFrame) || ![controlView window])
191 {
192 return;
193 }
194
195 NSDrawGrayBezel (cellFrame, NSZeroRect);
196 [self drawInteriorWithFrame: cellFrame inView: controlView];
197 }
198 @end
199
200 //
201 // Private NSBrowser methods
202 //
203 @interface NSBrowser (Private)
204 - (NSString *) _getTitleOfColumn: (int)column;
205 - (void) _performLoadOfColumn: (int)column;
206 - (void) _remapColumnSubviews: (BOOL)flag;
207 - (void) _setColumnTitlesNeedDisplay;
208 @end
209
210 //
211 // NSBrowser implementation
212 //
213 @implementation NSBrowser
214
215 /** Returns the NSBrowserCell class (regardless of whether a
216 setCellClass: message has been sent to a particular instance). This
217 method is not meant to be used by applications.
218 */
219
220 + (Class) cellClass
221 {
222 return [NSBrowserCell class];
223 }
224
225 /** Sets the class of NSCell used in the columns of the NSBrowser. */
226 - (void) setCellClass: (Class)classId
227 {
228 NSCell *aCell;
229
230 aCell = [[classId alloc] init];
231 // set the prototype for the new class
232 [self setCellPrototype: aCell];
233 RELEASE(aCell);
234 }
235
236 /** Returns the NSBrowser's prototype NSCell instance.*/
237 - (id) cellPrototype
238 {
239 return _browserCellPrototype;
240 }
241
242 /** Sets the NSCell instance copied to display items in the columns of
243 NSBrowser. */
244 - (void) setCellPrototype: (NSCell *)aCell
245 {
246 ASSIGN(_browserCellPrototype, aCell);
247 }
248
249 /** Returns the class of NSMatrix used in the NSBrowser's columns. */
250 - (Class) matrixClass
251 {
252 return _browserMatrixClass;
253 }
254
255 /** Sets the matrix class (NSMatrix or an NSMatrix subclass) used in the
256 NSBrowser's columns. */
257 - (void) setMatrixClass: (Class)classId
258 {
259 _browserMatrixClass = classId;
260 }
261
262 /*
263 * Getting matrices, cells, and rows
264 */
265
266 /** Returns the last (rightmost and lowest) selected NSCell. */
267 - (id) selectedCell
268 {
269 int i;
270 id matrix;
271
272 // Nothing selected
273 if ((i = [self selectedColumn]) == -1)
274 {
275 return nil;
276 }
277
278 if (!(matrix = [self matrixInColumn: i]))
279 {
280 return nil;
281 }
282
283 return [matrix selectedCell];
284 }
285
286 /** Returns the last (lowest) NSCell that's selected in column. */
287 - (id) selectedCellInColumn: (int)column
288 {
289 id matrix;
290
291 if (!(matrix = [self matrixInColumn: column]))
292 {
293 return nil;
294 }
295
296 return [matrix selectedCell];
297 }
298
299 /** Returns all cells selected in the rightmost column. */
300 - (NSArray *) selectedCells
301 {
302 int i;
303 id matrix;
304
305 // Nothing selected
306 if ((i = [self selectedColumn]) == -1)
307 {
308 return nil;
309 }
310
311 if (!(matrix = [self matrixInColumn: i]))
312 {
313 return nil;
314 }
315
316 return [matrix selectedCells];
317 }
318
319 /** Selects all NSCells in the last column of the NSBrowser. */
320 - (void) selectAll: (id)sender
321 {
322 id matrix;
323
324 if (!(matrix = [self matrixInColumn: _lastColumnLoaded]))
325 {
326 return;
327 }
328
329 [matrix selectAll: sender];
330 }
331
332 /** Returns the row index of the selected cell in the column specified by
333 index column. */
334 - (int) selectedRowInColumn: (int)column
335 {
336 id matrix;
337
338 if (!(matrix = [self matrixInColumn: column]))
339 {
340 return -1;
341 }
342
343 return [matrix selectedRow];
344 }
345
346 /** Selects the cell at index row in the column identified by index column. */
347 - (void) selectRow: (int)row inColumn: (int)column
348 {
349 id matrix;
350 id cell;
351 BOOL didSelect;
352
353 if ((matrix = [self matrixInColumn: column]) == nil)
354 {
355 return;
356 }
357
358 if ((cell = [matrix cellAtRow: row column: 0]) == nil)
359 {
360 return;
361 }
362
363 [self setLastColumn: column];
364
365 if (_allowsMultipleSelection == NO)
366 {
367 [matrix deselectAllCells];
368 }
369
370 if ([_browserDelegate respondsToSelector:
371 @selector(browser:selectRow:inColumn:)])
372 {
373 didSelect = [_browserDelegate browser: self
374 selectRow: row
375 inColumn: column];
376 }
377 else
378 {
379 [matrix selectCellAtRow: row column: 0];
380 didSelect = YES;
381 }
382
383 if (didSelect && [cell isLeaf] == NO)
384 {
385 [self addColumn];
386 }
387 }
388
389 /** Loads if necessary and returns the NSCell at row in column. */
390 /* if you change this code, you may want to look at the _loadColumn
391 method in which the following code is integrated (for speed) */
392 - (id) loadedCellAtRow: (int)row
393 column: (int)column
394 {
395 NSMatrix *matrix;
396 id cell;
397
398 if ((matrix = [self matrixInColumn: column]) == nil)
399 {
400 return nil;
401 }
402
403 // Get the cell
404 if ((cell = [matrix cellAtRow: row column: 0]) == nil)
405 {
406 return nil;
407 }
408
409 // Load if not already loaded
410 if ([cell isLoaded])
411 {
412 return cell;
413 }
414 else
415 {
416 if (_passiveDelegate || [_browserDelegate respondsToSelector:
417 @selector(browser:willDisplayCell:atRow:column:)])
418 {
419 [_browserDelegate browser: self willDisplayCell: cell
420 atRow: row column: column];
421 }
422 [cell setLoaded: YES];
423 }
424
425 return cell;
426 }
427
428 /** Returns the matrix located in the column identified by index column. */
429 - (NSMatrix *) matrixInColumn: (int)column
430 {
431 NSBrowserColumn *bc;
432
433 if (column < 0 || column > _lastColumnLoaded)
434 {
435 return nil;
436 }
437
438 bc = [_browserColumns objectAtIndex: column];
439
440 if ((bc == nil) || !(bc->_isLoaded))
441 {
442 return nil;
443 }
444
445 return bc->_columnMatrix;
446 }
447
448 /*
449 * Getting and setting paths
450 */
451
452 /** Returns the browser's current path. */
453 - (NSString *) path
454 {
455 return [self pathToColumn: _lastColumnLoaded + 1];
456 }
457
458 /**
459 * <p>Parses path and selects corresponding items in the NSBrowser columns.
460 * </p>
461 * <p>This is the primary mechanism for programmatically updating the
462 * selection of a browser. It should result in the browser cells
463 * corresponding to the components being selected, and the
464 * browser columns up to the end of path (and just beyond if the
465 * last selected cell's [NSBrowserCell-isLeaf] returns YES).<br />
466 * It does <em>not</em> result in the browsers action being sent to its
467 * target, just in a change to the browser selection and display.
468 * </p>
469 * <p>If path begins with the -pathSeparator then it is taken to be absolute
470 * and the first component in it is expected to denote a cell in column
471 * zero. Otherwise it is taken to be relative to the currently selected
472 * column.
473 * </p>
474 * <p>Empty components (ie where a -pathSeparator occurs immediately
475 * after another or at the end of path) are simply ignored.
476 * </p>
477 * <p>The receivers delegate is asked to select each cell in turn
478 * using the -browser:selectCellWithString:inColumn: method (if it
479 * implements it). If this call to the delegate returns NO then
480 * the attempt to set the path fails.<br />
481 * If the delegate does not implement the method, the browser attempts
482 * to locate and select the cell itsself, and the method fails if it
483 * is unable to locate the cell by matching its [NSCell-stringValue] with
484 * the component of the path.
485 * </p>
486 * <p>The method returns YES if path contains no components or if a cell
487 * corresponding to the path was found. Otherwise it returns NO.
488 * </p>
489 */
490 - (BOOL) setPath: (NSString *)path
491 {
492 NSMutableArray *subStrings;
493 unsigned numberOfSubStrings;
494 unsigned indexOfSubStrings;
495 int column;
496 BOOL useDelegate = NO;
497
498 if ([_browserDelegate respondsToSelector:
499 @selector(browser:selectCellWithString:inColumn:)])
500 {
501 useDelegate = YES;
502 }
503
504 /*
505 * Ensure that our starting column is loaded.
506 */
507 if (_lastColumnLoaded < 0)
508 {
509 [self loadColumnZero];
510 }
511
512 /*
513 * Decompose the path.
514 */
515 subStrings = [[path componentsSeparatedByString: _pathSeparator] mutableCopy];
516 [subStrings removeObject: @""];
517 numberOfSubStrings = [subStrings count];
518
519 if ([path hasPrefix: _pathSeparator])
520 {
521 int i;
522 /*
523 * If the path begins with a separator, start at column 0.
524 * Otherwise start at the currently selected column.
525 */
526
527 column = 0;
528 /*
529 * Optimisation. If there are columns loaded, it may be that the
530 * specified path is already partially selected. If this is the
531 * case, we can avoid redrawing those columns.
532 */
533 for (i = 0; i <= _lastColumnLoaded && (unsigned)i < numberOfSubStrings; i++)
534 {
535 NSString *c = [[self selectedCellInColumn: i] stringValue];
536
537 if ([c isEqualToString: [subStrings objectAtIndex: i]])
538 {
539 column = i;
540 }
541 else
542 {
543 break;
544 }
545 }
546
547 [self setLastColumn: column];
548 indexOfSubStrings = column;
549 }
550 else
551 {
552 column = _lastColumnLoaded;
553 indexOfSubStrings = 0;
554 }
555
556 // cycle thru str's array created from path
557 while (indexOfSubStrings < numberOfSubStrings)
558 {
559 NSString *aStr = [subStrings objectAtIndex: indexOfSubStrings];
560 NSBrowserColumn *bc = [_browserColumns objectAtIndex: column];
561 NSMatrix *matrix = [bc columnMatrix];
562 NSBrowserCell *selectedCell = nil;
563 BOOL found = NO;
564
565 if (useDelegate == YES)
566 {
567 if ([_browserDelegate browser: self
568 selectCellWithString: aStr
569 inColumn: column])
570 {
571 found = YES;
572 selectedCell = [matrix selectedCell];
573 }
574 }
575 else
576 {
577 int numOfRows = [matrix numberOfRows];
578 int row;
579
580 // find the cell in the browser matrix which is equal to aStr
581 for (row = 0; row < numOfRows; row++)
582 {
583 selectedCell = [matrix cellAtRow: row column: 0];
584
585 if ([[selectedCell stringValue] isEqualToString: aStr])
586 {
587 [matrix selectCellAtRow: row column: 0];
588 found = YES;
589 break;
590 }
591 }
592 }
593
594 if (found)
595 {
596 indexOfSubStrings++;
597 }
598 else
599 {
600 // if unable to find a cell whose title matches aStr return NO
601 NSDebugLLog (@"NSBrowser",
602 @"unable to find cell '%@' in column %d\n",
603 aStr, column);
604 break;
605 }
606
607 // if the cell is a leaf, we are finished setting the path
608 if ([selectedCell isLeaf])
609 {
610 break;
611 }
612
613 // else, it is not a leaf: get a column in the browser for it
614 [self addColumn];
615 column++;
616 }
617
618 if (indexOfSubStrings == numberOfSubStrings)
619 {
620 return YES;
621 }
622 else
623 {
624 return NO;
625 }
626 }
627
628 /** Returns a string representing the path from the first column up to,
629 but not including, the column at index column. */
630 - (NSString *) pathToColumn: (int)column
631 {
632 NSMutableString *s = [_pathSeparator mutableCopy];
633 NSString *string;
634 int i;
635
636 /*
637 * Cannot go past the number of loaded columns
638 */
639 if (column > _lastColumnLoaded)
640 {
641 column = _lastColumnLoaded + 1;
642 }
643
644 for (i = 0; i < column; ++i)
645 {
646 id c = [self selectedCellInColumn: i];
647
648 if (i != 0)
649 {
650 [s appendString: _pathSeparator];
651 }
652
653 string = [c stringValue];
654
655 if (string == nil)
656 {
657 /* This should happen only when c == nil, in which case it
658 doesn't make sense to go with the path */
659 break;
660 }
661 else
662 {
663 [s appendString: string];
664 }
665 }
666 /*
667 * We actually return a mutable string, but that's ok since a mutable
668 * string is a string and the documentation specifically says that
669 * people should not depend on methods that return strings to return
670 * immutable strings.
671 */
672
673 return AUTORELEASE (s);
674 }
675
676 /** Returns the path separator. The default is "/". */
677 - (NSString *) pathSeparator
678 {
679 return _pathSeparator;
680 }
681
682 /** Sets the path separator to newString. */
683 - (void) setPathSeparator: (NSString *)aString
684 {
685 ASSIGN(_pathSeparator, aString);
686 }
687
688
689 /*
690 * Manipulating columns
691 */
692 - (NSBrowserColumn *) _createColumn
693 {
694 NSBrowserColumn *bc;
695 NSScrollView *sc;
696 NSRect rect = {{0, 0}, {100, 100}};
697
698 bc = [[NSBrowserColumn alloc] init];
699
700 // Create a scrollview
701 sc = [[NSScrollView alloc] initWithFrame: rect];
702 [sc setHasHorizontalScroller: NO];
703 [sc setHasVerticalScroller: YES];
704
705 if (_separatesColumns)
706 {
707 [sc setBorderType: NSBezelBorder];
708 }
709 else
710 {
711 [sc setBorderType: NSNoBorder];
712 }
713
714 [bc setColumnScrollView: sc];
715 [self addSubview: sc];
716 RELEASE(sc);
717
718 [_browserColumns addObject: bc];
719 RELEASE(bc);
720
721 return bc;
722 }
723
724 /** Adds a column to the right of the last column. */
725 - (void) addColumn
726 {
727 int i;
728
729 if ((unsigned)(_lastColumnLoaded + 1) >= [_browserColumns count])
730 {
731 i = [_browserColumns indexOfObject: [self _createColumn]];
732 }
733 else
734 {
735 i = _lastColumnLoaded + 1;
736 }
737
738 if (i < 0)
739 {
740 i = 0;
741 }
742
743 [self _performLoadOfColumn: i];
744 [self setLastColumn: i];
745
746 _isLoaded = YES;
747
748 [self tile];
749
750 if (i > 0 && i - 1 == _lastVisibleColumn)
751 {
752 [self scrollColumnsRightBy: 1];
753 }
754 }
755
756 - (BOOL) acceptsFirstResponder
757 {
758 return YES;
759 }
760
761 - (BOOL) becomeFirstResponder
762 {
763 NSMatrix *matrix;
764 int selectedColumn;
765
766 selectedColumn = [self selectedColumn];
767 if (selectedColumn == -1)
768 matrix = [self matrixInColumn: 0];
769 else
770 matrix = [self matrixInColumn: selectedColumn];
771
772 if (matrix)
773 [_window makeFirstResponder: matrix];
774
775 return YES;
776 }
777
778 /** Updates the NSBrowser to display all loaded columns. */
779 - (void) displayAllColumns
780 {
781 [self tile];
782 }
783
784 /** Updates the NSBrowser to display the column with the given index. */
785 - (void) displayColumn: (int)column
786 {
787 id bc, sc;
788
789 // If not visible then nothing to display
790 if ((column < _firstVisibleColumn) || (column > _lastVisibleColumn))
791 {
792 return;
793 }
794
795 [self tile];
796
797 // Update and display title of column
798 if (_isTitled)
799 {
800 [self lockFocus];
801 [self drawTitleOfColumn: column
802 inRect: [self titleFrameOfColumn: column]];
803 [self unlockFocus];
804 }
805
806 // Display column
807 if (!(bc = [_browserColumns objectAtIndex: column]))
808 return;
809 if (!(sc = [bc columnScrollView]))
810 return;
811
812 /* FIXME: why the following ? Are we displaying now, or marking for
813 * later display ?? Given the name, I think we are displaying
814 * now. */
815 [sc setNeedsDisplay: YES];
816 }
817
818 /** Returns the column number in which matrix is located. */
819 - (int) columnOfMatrix: (NSMatrix *)matrix
820 {
821 int i, count;
822
823 // Loop through columns and compare matrixes
824 count = [_browserColumns count];
825 for (i = 0; i < count; ++i)
826 {
827 if (matrix == [self matrixInColumn: i])
828 return i;
829 }
830
831 // Not found
832 return -1;
833 }
834
835 /** Returns the index of the last column with a selected item. */
836 - (int) selectedColumn
837 {
838 int i;
839 id matrix;
840
841 for (i = _lastColumnLoaded; i >= 0; i--)
842 {
843 if (!(matrix = [self matrixInColumn: i]))
844 continue;
845 if ([matrix selectedCell])
846 return i;
847 }
848
849 return -1;
850 }
851
852 /** Returns the index of the last column loaded. */
853 - (int) lastColumn
854 {
855 return _lastColumnLoaded;
856 }
857
858 /** Sets the last column to column. */
859 - (void) setLastColumn: (int)column
860 {
861 int i, count, num;
862 id bc, sc;
863
864 if (column > _lastColumnLoaded)
865 {
866 return;
867 }
868
869 if (column < 0)
870 {
871 column = -1;
872 _isLoaded = NO;
873 }
874
875 _lastColumnLoaded = column;
876
877 // Unloads columns.
878 count = [_browserColumns count];
879 num = [self numberOfVisibleColumns];
880
881 for (i = column + 1; i < count; ++i)
882 {
883 bc = [_browserColumns objectAtIndex: i];
884 sc = [bc columnScrollView];
885
886 if ([bc isLoaded])
887 {
888 // Make the column appear empty by removing the matrix
889 if (sc)
890 {
891 [sc setDocumentView: nil];
892 }
893 [bc setIsLoaded: NO];
894 [self setTitle: nil ofColumn: i];
895 }
896
897 if (!_reusesColumns && i > _lastVisibleColumn)
898 {
899 [sc removeFromSuperview];
900 [_browserColumns removeObject: bc];
901 count--;
902 i--;
903 }
904 }
905
906 [self scrollColumnToVisible:column];
907 }
908
909 /** Returns the index of the first visible column. */
910 - (int) firstVisibleColumn
911 {
912 return _firstVisibleColumn;
913 }
914
915 /** Returns the number of columns visible. */
916 - (int) numberOfVisibleColumns
917 {
918 int num;
919
920 num = _lastVisibleColumn - _firstVisibleColumn + 1;
921
922 return (num > 0 ? num : 1);
923 }
924
925 /** Returns the index of the last visible column. */
926 - (int) lastVisibleColumn
927 {
928 return _lastVisibleColumn;
929 }
930
931 /** Invokes delegate method browser:isColumnValid: for visible columns. */
932 - (void) validateVisibleColumns
933 {
934 int i;
935
936 // If delegate doesn't care, just return
937 if (![_browserDelegate respondsToSelector:
938 @selector(browser:isColumnValid:)])
939 {
940 return;
941 }
942
943 // Loop through the visible columns
944 for (i = _firstVisibleColumn; i <= _lastVisibleColumn; ++i)
945 {
946 // Ask delegate if the column is valid and if not
947 // then reload the column
948 if (![_browserDelegate browser: self isColumnValid: i])
949 {
950 [self reloadColumn: i];
951 }
952 }
953 }
954
955
956 /*
957 * Loading columns
958 */
959
960 /** Returns whether column zero is loaded. */
961 - (BOOL) isLoaded
962 {
963 return _isLoaded;
964 }
965
966 /** Loads column zero; unloads previously loaded columns. */
967 - (void) loadColumnZero
968 {
969 // set last column loaded
970 [self setLastColumn: -1];
971
972 // load column 0
973 [self addColumn];
974
975 [self _remapColumnSubviews: YES];
976 [self _setColumnTitlesNeedDisplay];
977 }
978
979 /** Reloads column if it is loaded; sets it as the last column.
980 Reselects previously selected cells, if they remain. */
981 - (void) reloadColumn: (int)column
982 {
983 NSArray *selectedCells;
984 NSEnumerator *selectedCellsEnumerator;
985 NSMatrix *matrix;
986 NSCell *cell;
987
988 matrix = [self matrixInColumn: column];
989 if (matrix == nil)
990 {
991 return;
992 }
993
994 // Get the previously selected cells
995 selectedCells = [[matrix selectedCells] copy];
996
997 // Perform the data load
998 [self _performLoadOfColumn: column];
999 // set last column loaded
1000 [self setLastColumn: column];
1001
1002 // Restore the selected cells
1003 matrix = [self matrixInColumn: column];
1004 selectedCellsEnumerator = [selectedCells objectEnumerator];
1005 while ((cell = [selectedCellsEnumerator nextObject]) != nil)
1006 {
1007 int sRow, sColumn;
1008
1009 if ([matrix getRow: &sRow column: &sColumn ofCell: cell])
1010 {
1011 [matrix selectCellAtRow: sRow column: sColumn];
1012 }
1013 }
1014 RELEASE(selectedCells);
1015 }
1016
1017
1018 /*
1019 * Setting selection characteristics
1020 */
1021
1022 /** Returns whether the user can select branch items when multiple selection
1023 is enabled. */
1024 - (BOOL) allowsBranchSelection
1025 {
1026 return _allowsBranchSelection;
1027 }
1028
1029 /** Sets whether the user can select branch items when multiple selection
1030 is enabled. */
1031 - (void) setAllowsBranchSelection: (BOOL)flag
1032 {
1033 _allowsBranchSelection = flag;
1034 }
1035
1036 /** Returns whether there can be nothing selected. */
1037 - (BOOL) allowsEmptySelection
1038 {
1039 return _allowsEmptySelection;
1040 }
1041
1042 /** Sets whether there can be nothing selected. */
1043 - (void) setAllowsEmptySelection: (BOOL)flag
1044 {
1045 _allowsEmptySelection = flag;
1046 }
1047
1048 /** Returns whether the user can select multiple items. */
1049 - (BOOL) allowsMultipleSelection
1050 {
1051 return _allowsMultipleSelection;
1052 }
1053
1054 /** Sets whether the user can select multiple items. */
1055 - (void) setAllowsMultipleSelection: (BOOL)flag
1056 {
1057 _allowsMultipleSelection = flag;
1058 }
1059
1060
1061 /*
1062 * Setting column characteristics
1063 */
1064
1065 /** Returns YES if NSMatrix objects aren't freed when their columns
1066 are unloaded. */
1067 - (BOOL) reusesColumns
1068 {
1069 return _reusesColumns;
1070 }
1071
1072 /** If flag is YES, prevents NSMatrix objects from being freed when
1073 their columns are unloaded, so they can be reused. */
1074 - (void) setReusesColumns: (BOOL)flag
1075 {
1076 _reusesColumns = flag;
1077 }
1078
1079 /** Returns the maximum number of visible columns. */
1080 - (int) maxVisibleColumns
1081 {
1082 return _maxVisibleColumns;
1083 }
1084
1085 /** Sets the maximum number of columns displayed. */
1086 - (void) setMaxVisibleColumns: (int)columnCount
1087 {
1088 if ((columnCount < 1) || (_maxVisibleColumns == columnCount))
1089 return;
1090
1091 _maxVisibleColumns = columnCount;
1092
1093 // Redisplay
1094 [self tile];
1095 }
1096
1097 /** Returns the minimum column width in pixels. */
1098 - (int) minColumnWidth
1099 {
1100 return _minColumnWidth;
1101 }
1102
1103 /** Sets the minimum column width in pixels. */
1104 - (void) setMinColumnWidth: (int)columnWidth
1105 {
1106 float sw;
1107
1108 sw = scrollerWidth;
1109 // Take the border into account
1110 if (_separatesColumns)
1111 sw += 2 * (_sizeForBorderType (NSBezelBorder)).width;
1112
1113 // Column width cannot be less than scroller and border
1114 if (columnWidth < sw)
1115 _minColumnWidth = sw;
1116 else
1117 _minColumnWidth = columnWidth;
1118
1119 [self tile];
1120 }
1121
1122 /** Returns whether columns are separated by bezeled borders. */
1123 - (BOOL) separatesColumns
1124 {
1125 return _separatesColumns;
1126 }
1127
1128 /** Sets whether to separate columns with bezeled borders. */
1129 - (void) setSeparatesColumns: (BOOL)flag
1130 {
1131 NSBrowserColumn *bc;
1132 NSScrollView *sc;
1133 NSBorderType bt;
1134 int i, columnCount;
1135
1136 // if this flag already set or browser is titled -- do nothing
1137 if (_separatesColumns == flag || _isTitled)
1138 return;
1139
1140 columnCount = [_browserColumns count];
1141 bt = flag ? NSBezelBorder : NSNoBorder;
1142 for (i = 0; i < columnCount; i++)
1143 {
1144 bc = [_browserColumns objectAtIndex: i];
1145 sc = [bc columnScrollView];
1146 [sc setBorderType:bt];
1147 }
1148
1149 _separatesColumns = flag;
1150 [self setNeedsDisplay:YES];
1151 [self tile];
1152 }
1153
1154 /** Returns YES if the title of a column is set to the string value of
1155 the selected NSCell in the previous column.*/
1156 - (BOOL) takesTitleFromPreviousColumn
1157 {
1158 return _takesTitleFromPreviousColumn;
1159 }
1160
1161 /** Sets whether the title of a column is set to the string value of the
1162 selected NSCell in the previous column. */
1163 - (void) setTakesTitleFromPreviousColumn: (BOOL)flag
1164 {
1165 if (_takesTitleFromPreviousColumn != flag)
1166 {
1167 _takesTitleFromPreviousColumn = flag;
1168 [self setNeedsDisplay: YES];
1169 }
1170 }
1171
1172
1173 /*
1174 * Manipulating column titles
1175 */
1176
1177 /** Returns the title displayed for the column at index column. */
1178 - (NSString *) titleOfColumn: (int)column
1179 {
1180 NSBrowserColumn *bc;
1181
1182 bc = [_browserColumns objectAtIndex: column];
1183
1184 return bc->_columnTitle;
1185 }
1186
1187 /** Sets the title of the column at index column to aString. */
1188 - (void) setTitle: (NSString *)aString
1189 ofColumn: (int)column
1190 {
1191 NSBrowserColumn *bc;
1192
1193 bc = [_browserColumns objectAtIndex: column];
1194
1195 [bc setColumnTitle: aString];
1196
1197 // If column is not visible then nothing to redisplay
1198 if (!_isTitled || !NSBR_COLUMN_IS_VISIBLE(column))
1199 return;
1200
1201 [self setNeedsDisplayInRect: [self titleFrameOfColumn: column]];
1202 }
1203
1204 /** Returns whether columns display titles. */
1205 - (BOOL) isTitled
1206 {
1207 return _isTitled;
1208 }
1209
1210 /** Sets whether columns display titles. */
1211 - (void) setTitled: (BOOL)flag
1212 {
1213 if (_isTitled == flag || !_separatesColumns)
1214 return;
1215
1216 _isTitled = flag;
1217 [self tile];
1218 [self setNeedsDisplay: YES];
1219 }
1220
1221 - (void) drawTitleOfColumn: (int)column
1222 inRect: (NSRect)aRect
1223 {
1224 [self drawTitle: [self titleOfColumn: column]
1225 inRect: aRect
1226 ofColumn: column];
1227 }
1228
1229 /** Draws the title for the column at index column within the rectangle
1230 defined by aRect. */
1231 - (void) drawTitle: (NSString *)title
1232 inRect: (NSRect)aRect
1233 ofColumn: (int)column
1234 {
1235 if (!_isTitled || !NSBR_COLUMN_IS_VISIBLE(column))
1236 return;
1237
1238 [titleCell setStringValue: title];
1239 [titleCell drawWithFrame: aRect inView: self];
1240 }
1241
1242 /** Returns the height of column titles. */
1243 - (float) titleHeight
1244 {
1245 // Nextish look requires 21 here
1246 return 21;
1247 }
1248
1249 /** Returns the bounds of the title frame for the column at index column. */
1250 - (NSRect) titleFrameOfColumn: (int)column
1251 {
1252 // Not titled then no frame
1253 if (!_isTitled)
1254 {
1255 return NSZeroRect;
1256 }
1257 else
1258 {
1259 // Number of columns over from the first
1260 int n = column - _firstVisibleColumn;
1261 int h = [self titleHeight];
1262 NSRect r;
1263
1264 // Calculate origin
1265 if (_separatesColumns)
1266 {
1267 r.origin.x = n * (_columnSize.width + NSBR_COLUMN_SEP);
1268 }
1269 else
1270 {
1271 r.origin.x = n * _columnSize.width;
1272 }
1273 r.origin.y = _frame.size.height - h;
1274
1275 // Calculate size
1276 if (column == _lastVisibleColumn)
1277 {
1278 r.size.width = _frame.size.width - r.origin.x;
1279 }
1280 else
1281 {
1282 r.size.width = _columnSize.width;
1283 }
1284 r.size.height = h;
1285
1286 return r;
1287 }
1288 }
1289
1290
1291 /*
1292 * Scrolling an NSBrowser
1293 */
1294
1295 /** Scrolls to make the column at index column visible. */
1296 - (void) scrollColumnToVisible: (int)column
1297 {
1298 // If its the last visible column then we are there already
1299 if (_lastVisibleColumn < column)
1300 {
1301 [self scrollColumnsRightBy: (column - _lastVisibleColumn)];
1302 }
1303 else if (_firstVisibleColumn > column)
1304 {
1305 [self scrollColumnsLeftBy: (_firstVisibleColumn - column)];
1306 }
1307 }
1308
1309 /** Scrolls columns left by shiftAmount columns. */
1310 - (void) scrollColumnsLeftBy: (int)shiftAmount
1311 {
1312 // Cannot shift past the zero column
1313 if ((_firstVisibleColumn - shiftAmount) < 0)
1314 shiftAmount = _firstVisibleColumn;
1315
1316 // No amount to shift then nothing to do
1317 if (shiftAmount <= 0)
1318 return;
1319
1320 // Notify the delegate
1321 if ([_browserDelegate respondsToSelector: @selector(browserWillScroll:)])
1322 [_browserDelegate browserWillScroll: self];
1323
1324 // Shift
1325 _firstVisibleColumn = _firstVisibleColumn - shiftAmount;
1326 _lastVisibleColumn = _lastVisibleColumn - shiftAmount;
1327
1328 // Update the scroller
1329 [self updateScroller];
1330
1331 // Update the scrollviews
1332 [self tile];
1333 [self _remapColumnSubviews: YES];
1334 [self _setColumnTitlesNeedDisplay];
1335
1336 // Notify the delegate
1337 if ([_browserDelegate respondsToSelector: @selector(browserDidScroll:)])
1338 [_browserDelegate browserDidScroll: self];
1339 }
1340
1341 /** Scrolls columns right by shiftAmount columns. */
1342 - (void) scrollColumnsRightBy: (int)shiftAmount
1343 {
1344 // Cannot shift past the last loaded column
1345 if ((shiftAmount + _lastVisibleColumn) > _lastColumnLoaded)
1346 shiftAmount = _lastColumnLoaded - _lastVisibleColumn;
1347
1348 // No amount to shift then nothing to do
1349 if (shiftAmount <= 0)
1350 return;
1351
1352 // Notify the delegate
1353 if ([_browserDelegate respondsToSelector: @selector(browserWillScroll:)])
1354 [_browserDelegate browserWillScroll: self];
1355
1356 // Shift
1357 _firstVisibleColumn = _firstVisibleColumn + shiftAmount;
1358 _lastVisibleColumn = _lastVisibleColumn + shiftAmount;
1359
1360 // Update the scroller
1361 [self updateScroller];
1362
1363 // Update the scrollviews
1364 [self tile];
1365 [self _remapColumnSubviews: NO];
1366 [self _setColumnTitlesNeedDisplay];
1367
1368 // Notify the delegate
1369 if ([_browserDelegate respondsToSelector: @selector(browserDidScroll:)])
1370 [_browserDelegate browserDidScroll: self];
1371 }
1372
1373 /** Updates the horizontal scroller to reflect column positions. */
1374 - (void) updateScroller
1375 {
1376 int num;
1377
1378 num = [self numberOfVisibleColumns];
1379
1380 // If there are not enough columns to scroll with
1381 // then the column must be visible
1382 if ((_lastColumnLoaded == 0) ||
1383 (_lastColumnLoaded <= (num - 1)))
1384 {
1385 [_horizontalScroller setEnabled: NO];
1386 }
1387 else
1388 {
1389 if (!_skipUpdateScroller)
1390 {
1391 float prop = (float)num / (float)(_lastColumnLoaded + 1);
1392 float i = _lastColumnLoaded - num + 1;
1393 float f = 1 + ((_lastVisibleColumn - _lastColumnLoaded) / i);
1394
1395 [_horizontalScroller setFloatValue: f knobProportion: prop];
1396 }
1397 [_horizontalScroller setEnabled: YES];
1398 }
1399
1400 [_horizontalScroller setNeedsDisplay: YES];
1401 }
1402
1403 /** Scrolls columns left or right based on an NSScroller. */
1404 - (void) scrollViaScroller: (NSScroller *)sender
1405 {
1406 NSScrollerPart hit;
1407
1408 if ([sender class] != [NSScroller class])
1409 return;
1410
1411 hit = [sender hitPart];
1412
1413 switch (hit)
1414 {
1415 // Scroll to the left
1416 case NSScrollerDecrementLine:
1417 case NSScrollerDecrementPage:
1418 [self scrollColumnsLeftBy: 1];
1419 break;
1420
1421 // Scroll to the right
1422 case NSScrollerIncrementLine:
1423 case NSScrollerIncrementPage:
1424 [self scrollColumnsRightBy: 1];
1425 break;
1426
1427 // The knob or knob slot
1428 case NSScrollerKnob:
1429 case NSScrollerKnobSlot:
1430 {
1431 float f = [sender floatValue];
1432
1433 _skipUpdateScroller = YES;
1434 [self scrollColumnToVisible: rintf(f * _lastColumnLoaded)];
1435 _skipUpdateScroller = NO;
1436 }
1437 break;
1438
1439 // NSScrollerNoPart ???
1440 default:
1441 break;
1442 }
1443 }
1444
1445
1446 /*
1447 * Showing a horizontal scroller
1448 */
1449
1450 /** Returns whether an NSScroller is used to scroll horizontally. */
1451 - (BOOL) hasHorizontalScroller
1452 {
1453 return _hasHorizontalScroller;
1454 }
1455
1456 /** Sets whether an NSScroller is used to scroll horizontally. */
1457 - (void) setHasHorizontalScroller: (BOOL)flag
1458 {
1459 if (_hasHorizontalScroller != flag)
1460 {
1461 _hasHorizontalScroller = flag;
1462 if (!flag)
1463 [_horizontalScroller removeFromSuperview];
1464 else
1465 [self addSubview: _horizontalScroller];
1466 [self tile];
1467 [self setNeedsDisplay: YES];
1468 }
1469 }
1470
1471
1472 /*
1473 * Setting the behavior of arrow keys
1474 */
1475
1476 /** Returns YES if the arrow keys are enabled. */
1477 - (BOOL) acceptsArrowKeys
1478 {
1479 return _acceptsArrowKeys;
1480 }
1481
1482 /** Enables or disables the arrow keys as used for navigating within
1483 and between browsers. */
1484 - (void) setAcceptsArrowKeys: (BOOL)flag
1485 {
1486 _acceptsArrowKeys = flag;
1487 }
1488
1489 /** Returns NO if pressing an arrow key only scrolls the browser, YES if
1490 it also sends the action message specified by setAction:. */
1491 - (BOOL) sendsActionOnArrowKeys
1492 {
1493 return _sendsActionOnArrowKeys;
1494 }
1495
1496 /** Sets whether pressing an arrow key will cause the action message
1497 to be sent (in addition to causing scrolling). */
1498 - (void) setSendsActionOnArrowKeys: (BOOL)flag
1499 {
1500 _sendsActionOnArrowKeys = flag;
1501 }
1502
1503
1504 /*
1505 * Getting column frames
1506 */
1507
1508 /** Returns the rectangle containing the column at index column. */
1509 - (NSRect) frameOfColumn: (int)column
1510 {
1511 NSRect r = NSZeroRect;
1512 NSSize bs = _sizeForBorderType (NSBezelBorder);
1513 int n;
1514
1515 // Number of columns over from the first
1516 n = column - _firstVisibleColumn;
1517
1518 // Calculate the frame
1519 r.size = _columnSize;
1520 r.origin.x = n * _columnSize.width;
1521
1522 if (_separatesColumns)
1523 {
1524 r.origin.x += n * NSBR_COLUMN_SEP;
1525 }
1526 else
1527 {
1528 if (column == _firstVisibleColumn)
1529 r.origin.x = (n * _columnSize.width) + 2;
1530 else
1531 r.origin.x = (n * _columnSize.width) + (n + 2);
1532 }
1533
1534 // Adjust for horizontal scroller
1535 if (_hasHorizontalScroller)
1536 {
1537 if (_separatesColumns)
1538 r.origin.y = (scrollerWidth - 1) + (2 * bs.height) + NSBR_VOFFSET;
1539 else
1540 r.origin.y = scrollerWidth + bs.width;
1541 }
1542
1543 // Padding : _columnSize.width is rounded in "tile" method
1544 if (column == _lastVisibleColumn)
1545 {
1546 if (_separatesColumns)
1547 r.size.width = _frame.size.width - r.origin.x;
1548 else
1549 r.size.width = _frame.size.width
1550 - (r.origin.x + (2 * bs.width) + ([self numberOfVisibleColumns] - 1));
1551 }
1552
1553 if (r.size.width < 0)
1554 {
1555 r.size.width = 0;
1556 }
1557 if (r.size.height < 0)
1558 {
1559 r.size.height = 0;
1560 }
1561
1562 return r;
1563 }
1564
1565 /** Returns the rectangle containing the column at index column, */
1566 // not including borders.
1567 - (NSRect) frameOfInsideOfColumn: (int)column
1568 {
1569 // xxx what does this one do?
1570 return [self frameOfColumn: column];
1571 }
1572
1573
1574 /*
1575 * Arranging browser components
1576 */
1577
1578 /** Adjusts the various subviews of NSBrowser-scrollers, columns,
1579 titles, and so on-without redrawing. Your code shouldn't send this
1580 message. It's invoked any time the appearance of the NSBrowser
1581 changes. */
1582 - (void) tile
1583 {
1584 NSSize bs = _sizeForBorderType (NSBezelBorder);
1585 int i, num, columnCount, delta;
1586 float frameWidth;
1587
1588 _columnSize.height = _frame.size.height;
1589
1590 // Titles (there is no real frames to resize)
1591 if (_isTitled)
1592 {
1593 _columnSize.height -= [self titleHeight] + NSBR_VOFFSET;
1594 }
1595
1596 // Horizontal scroller
1597 if (_hasHorizontalScroller)
1598 {
1599 _scrollerRect.origin.x = bs.width;
1600 _scrollerRect.origin.y = bs.height - 1;
1601 _scrollerRect.size.width = (_frame.size.width - (2 * bs.width)) + 1;
1602 _scrollerRect.size.height = scrollerWidth;
1603
1604 if (_separatesColumns)
1605 _columnSize.height -= (scrollerWidth - 1) + (2 * bs.height)
1606 + NSBR_VOFFSET;
1607 else
1608 _columnSize.height -= scrollerWidth + (2 * bs.height);
1609
1610 if (!NSEqualRects(_scrollerRect, [_horizontalScroller frame]))
1611 {
1612 [_horizontalScroller setFrame: _scrollerRect];
1613 }
1614 }
1615 else
1616 {
1617 _scrollerRect = NSZeroRect;
1618 }
1619
1620 num = _lastVisibleColumn - _firstVisibleColumn + 1;
1621
1622 if (_minColumnWidth > 0)
1623 {
1624 float colWidth = _minColumnWidth + scrollerWidth;
1625
1626 if ((int)(_frame.size.width > _minColumnWidth))
1627 {
1628 if (_separatesColumns)
1629 colWidth += NSBR_COLUMN_SEP;
1630
1631 columnCount = (int)(_frame.size.width / colWidth);
1632 }
1633 else
1634 columnCount = 1;
1635 }
1636 else
1637 columnCount = num;
1638
1639 if (_maxVisibleColumns > 0 && columnCount > _maxVisibleColumns)
1640 columnCount = _maxVisibleColumns;
1641
1642 if (columnCount != num)
1643 {
1644 if (num > 0)
1645 delta = columnCount - num;
1646 else
1647 delta = columnCount - 1;
1648
1649 if ((delta > 0) && (_lastVisibleColumn <= _lastColumnLoaded))
1650 {
1651 _firstVisibleColumn = (_firstVisibleColumn - delta > 0) ?
1652 _firstVisibleColumn - delta : 0;
1653 }
1654
1655 for (i = [_browserColumns count]; i < columnCount; i++)
1656 [self _createColumn];
1657
1658 _lastVisibleColumn = _firstVisibleColumn + columnCount - 1;
1659 }
1660
1661 // Columns
1662 if (_separatesColumns)
1663 frameWidth = _frame.size.width - ((columnCount - 1) * NSBR_COLUMN_SEP);
1664 else
1665 frameWidth = _frame.size.width - (columnCount + (2 * bs.width));
1666
1667 _columnSize.width = (int)(frameWidth / (float)columnCount);
1668
1669 if (_columnSize.height < 0)
1670 _columnSize.height = 0;
1671
1672 for (i = _firstVisibleColumn; i <= _lastVisibleColumn; i++)
1673 {
1674 id bc, sc;
1675 id matrix;
1676
1677 // FIXME: in some cases the column is not loaded
1678 while (i >= [_browserColumns count]) [self _createColumn];
1679
1680 bc = [_browserColumns objectAtIndex: i];
1681
1682 if (!(sc = [bc columnScrollView]))
1683 {
1684 NSLog(@"NSBrowser error, sc != [bc columnScrollView]");
1685 return;
1686 }
1687
1688 [sc setFrame: [self frameOfColumn: i]];
1689 matrix = [bc columnMatrix];
1690
1691 // Adjust matrix to fit in scrollview if column has been loaded
1692 if (matrix && [bc isLoaded])
1693 {
1694 NSSize cs, ms;
1695
1696 cs = [sc contentSize];
1697 ms = [matrix cellSize];
1698 ms.width = cs.width;
1699 [matrix setCellSize: ms];
1700 [sc setDocumentView: matrix];
1701 }
1702 }
1703
1704 if (columnCount != num)
1705 {
1706 [self updateScroller];
1707 [self _remapColumnSubviews: YES];
1708 // [self _setColumnTitlesNeedDisplay];
1709 [self setNeedsDisplay: YES];
1710 }
1711 }
1712
1713 /** Override from NSControl. Don't do anything to change the size of the
1714 browser. */
1715 - (void) sizeToFit
1716 {
1717 }
1718
1719 /*
1720 * Setting the delegate
1721 */
1722
1723 /** Returns the NSBrowser's delegate. */
1724 - (id) delegate
1725 {
1726 return _browserDelegate;
1727 }
1728
1729 /** Sets the NSBrowser's delegate to anObject. Raises
1730 NSBrowserIllegalDelegateException if the delegate specified by
1731 anObject doesn't respond to browser:willDisplayCell:atRow:column: (if
1732 passive) and either of the methods browser:numberOfRowsInColumn: or
1733 browser:createRowsForColumn:inMatrix:. */
1734 - (void) setDelegate: (id)anObject
1735 {
1736 BOOL flag = NO;
1737
1738 if ([anObject respondsToSelector:
1739 @selector(browser:numberOfRowsInColumn:)])
1740 {
1741 _passiveDelegate = YES;
1742 flag = YES;
1743 if (![anObject respondsToSelector:
1744 @selector(browser:willDisplayCell:atRow:column:)])
1745 [NSException raise: NSBrowserIllegalDelegateException
1746 format: @"(Passive) Delegate does not respond to %s\n",
1747 "browser: willDisplayCell: atRow: column: "];
1748 }
1749
1750 if ([anObject respondsToSelector:
1751 @selector(browser:createRowsForColumn:inMatrix:)])
1752 {
1753 _passiveDelegate = NO;
1754
1755 // If flag is already set
1756 // then delegate must respond to both methods
1757 if (flag)
1758 {
1759 [NSException raise: NSBrowserIllegalDelegateException
1760 format: @"Delegate responds to both %s and %s\n",
1761 "browser: numberOfRowsInColumn: ",
1762 "browser: createRowsForColumn: inMatrix: "];
1763 }
1764
1765 flag = YES;
1766 }
1767
1768 if (!flag)
1769 [NSException raise: NSBrowserIllegalDelegateException
1770 format: @"Delegate does not respond to %s or %s\n",
1771 "browser: numberOfRowsInColumn: ",
1772 "browser: createRowsForColumn: inMatrix: "];
1773
1774 _browserDelegate = anObject;
1775 }
1776
1777
1778 /*
1779 * Target and action
1780 */
1781
1782 /** Returns the NSBrowser's double-click action method. */
1783 - (SEL) doubleAction
1784 {
1785 return _doubleAction;
1786 }
1787
1788 /** Sets the NSBrowser's double-click action to aSelector. */
1789 - (void) setDoubleAction: (SEL)aSelector
1790 {
1791 _doubleAction = aSelector;
1792 }
1793
1794 /** Sends the action message to the target. Returns YES upon success,
1795 NO if no target for the message could be found. */
1796 - (BOOL) sendAction
1797 {
1798 return [self sendAction: [self action] to: [self target]];
1799 }
1800
1801
1802 /*
1803 * Event handling
1804 */
1805
1806 /** Responds to (single) mouse clicks in a column of the NSBrowser. */
1807 - (void) doClick: (id)sender
1808 {
1809 NSArray *a;
1810 NSMutableArray *selectedCells;
1811 NSEnumerator *enumerator;
1812 NSBrowserCell *cell;
1813 int column, aCount, selectedCellsCount;
1814
1815 if ([sender class] != _browserMatrixClass)
1816 return;
1817
1818 column = [self columnOfMatrix: sender];
1819 // If the matrix isn't ours then just return
1820 if (column < 0 || column > _lastColumnLoaded)
1821 return;
1822
1823 a = [sender selectedCells];
1824 aCount = [a count];
1825 if(aCount == 0)
1826 return;
1827
1828 selectedCells = [a mutableCopy];
1829
1830 enumerator = [a objectEnumerator];
1831 while ((cell = [enumerator nextObject]))
1832 {
1833 if (_allowsBranchSelection == NO && [cell isLeaf] == NO)
1834 {
1835 [selectedCells removeObject: cell];
1836 }
1837 }
1838
1839 if ([selectedCells count] == 0 && [sender selectedCell] != nil)
1840 [selectedCells addObject: [sender selectedCell]];
1841
1842 selectedCellsCount = [selectedCells count];
1843
1844 if (selectedCellsCount == 0)
1845 {
1846 // If we should not select the cell then deselect it
1847
1848 [sender deselectAllCells];
1849 }
1850 else if (selectedCellsCount < aCount)
1851 {
1852 [sender deselectSelectedCell];
1853
1854 enumerator = [selectedCells objectEnumerator];
1855 while ((cell = [enumerator nextObject]))
1856 [sender selectCell: cell];
1857 }
1858
1859 [self setLastColumn: column];
1860 // Single selection
1861 if (selectedCellsCount == 1)
1862 {
1863 cell = [selectedCells objectAtIndex: 0];
1864
1865 // If the cell is not a leaf we need to load a column
1866 if (![cell isLeaf])
1867 {
1868 [self addColumn];
1869 }
1870
1871 [sender scrollCellToVisibleAtRow: [sender selectedRow] column: 0];
1872 }
1873
1874 // Send the action to target
1875 [self sendAction];
1876
1877 RELEASE(selectedCells);
1878 }
1879
1880 /** Responds to double-clicks in a column of the NSBrowser. */
1881 - (void) doDoubleClick: (id)sender
1882 {
1883 // We have already handled the single click
1884 // so send the double action
1885
1886 [self sendAction: _doubleAction to: [self target]];
1887 }
1888
1889 + (void) initialize
1890 {
1891 if (self == [NSBrowser class])
1892 {
1893 // Initial version
1894 [self setVersion: 1];
1895 scrollerWidth = [NSScroller scrollerWidth];
1896 }
1897 }
1898
1899 /*
1900 * Override superclass methods
1901 */
1902
1903 /** Setups browser with frame 'rect'. */
1904 - (id) initWithFrame: (NSRect)rect
1905 {
1906 NSSize bs;
1907 //NSScroller *hs;
1908
1909 /* Created the shared titleCell if it hasn't been created already. */
1910 if (!titleCell)
1911 {
1912 titleCell = [GSBrowserTitleCell new];
1913 }
1914
1915 self = [super initWithFrame: rect];
1916
1917 // Class setting
1918 _browserCellPrototype = [[[NSBrowser cellClass] alloc] init];
1919 _browserMatrixClass = [NSMatrix class];
1920
1921 // Default values
1922 _pathSeparator = @"/";
1923 _allowsBranchSelection = YES;
1924 _allowsEmptySelection = YES;
1925 _allowsMultipleSelection = YES;
1926 _reusesColumns = NO;
1927 _separatesColumns = YES;
1928 _isTitled = YES;
1929 _takesTitleFromPreviousColumn = YES;
1930 _hasHorizontalScroller = YES;
1931 _isLoaded = NO;
1932 _acceptsArrowKeys = YES;
1933 _acceptsAlphaNumericalKeys = YES;
1934 _lastKeyPressed = 0.;
1935 _charBuffer = nil;
1936 _sendsActionOnArrowKeys = YES;
1937 _sendsActionOnAlphaNumericalKeys = YES;
1938 _browserDelegate = nil;
1939 _passiveDelegate = YES;
1940 _doubleAction = NULL;
1941 bs = _sizeForBorderType (NSBezelBorder);
1942 _minColumnWidth = scrollerWidth + (2 * bs.width);
1943 if (_minColumnWidth < 100.0)
1944 _minColumnWidth = 100.0;
1945
1946 // Horizontal scroller
1947 _scrollerRect.origin.x = bs.width;
1948 _scrollerRect.origin.y = bs.height;
1949 _scrollerRect.size.width = _frame.size.width - (2 * bs.width);
1950 _scrollerRect.size.height = scrollerWidth;
1951 _horizontalScroller = [[NSScroller alloc] initWithFrame: _scrollerRect];
1952 [_horizontalScroller setTarget: self];
1953 [_horizontalScroller setAction: @selector(scrollViaScroller:)];
1954 [self addSubview: _horizontalScroller];
1955 _skipUpdateScroller = NO;
1956
1957 // Columns
1958 _browserColumns = [[NSMutableArray alloc] init];
1959
1960 // Create a single column
1961 _lastColumnLoaded = -1;
1962 _firstVisibleColumn = 0;
1963 _lastVisibleColumn = 0;
1964 _maxVisibleColumns = 3;
1965 [self _createColumn];
1966
1967 return self;
1968 }
1969
1970 - (void) dealloc
1971 {
1972 RELEASE(_browserCellPrototype);
1973 RELEASE(_pathSeparator);
1974 RELEASE(_horizontalScroller);
1975 RELEASE(_browserColumns);
1976 TEST_RELEASE(_charBuffer);
1977
1978 [super dealloc];
1979 }
1980
1981
1982
1983 /*
1984 * Target-actions
1985 */
1986
1987 /** Set target to 'target' */
1988 - (void) setTarget: (id)target
1989 {
1990 _target = target;
1991 }
1992
1993 /** Return current target. */
1994 - (id) target
1995 {
1996 return _target;
1997 }
1998
1999 /** Set action to 's'. */
2000 - (void) setAction: (SEL)s
2001 {
2002 _action = s;
2003 }
2004
2005 /** Return current action. */
2006 - (SEL) action
2007 {
2008 return _action;
2009 }
2010
2011
2012
2013 /*
2014 * Events handling
2015 */
2016
2017 - (void) drawRect: (NSRect)rect
2018 {
2019 NSRectClip(rect);
2020 [[_window backgroundColor] set];
2021 NSRectFill(rect);
2022
2023 // Load the first column if not already done
2024 if (!_isLoaded)
2025 {
2026 [self loadColumnZero];
2027 }
2028
2029 // Draws titles
2030 if (_isTitled)
2031 {
2032 int i;
2033
2034 for (i = _firstVisibleColumn; i <= _lastVisibleColumn; ++i)
2035 {
2036 NSRect titleRect = [self titleFrameOfColumn: i];
2037 if (NSIntersectsRect (titleRect, rect) == YES)
2038 {
2039 [self drawTitleOfColumn: i
2040 inRect: titleRect];
2041 }
2042 }
2043 }
2044
2045 // Draws scroller border
2046 if (_hasHorizontalScroller)
2047 {
2048 NSRect scrollerBorderRect = _scrollerRect;
2049 NSSize bs = _sizeForBorderType (NSBezelBorder);
2050
2051 scrollerBorderRect.origin.x = 0;
2052 scrollerBorderRect.origin.y = 0;
2053 scrollerBorderRect.size.width += 2 * bs.width - 1;
2054 scrollerBorderRect.size.height += (2 * bs.height) - 1;
2055
2056 if ((NSIntersectsRect (scrollerBorderRect, rect) == YES) && _window)
2057 {
2058 NSDrawGrayBezel (scrollerBorderRect, rect);
2059 }
2060 }
2061
2062 if (!_separatesColumns)
2063 {
2064 NSPoint p1,p2;
2065 NSRect browserRect;
2066 int i, visibleColumns;
2067
2068 // Columns borders
2069 browserRect = NSMakeRect(0, 0, rect.size.width, rect.size.height);
2070 NSDrawGrayBezel (browserRect, rect);
2071
2072 [[NSColor blackColor] set];
2073 visibleColumns = [self numberOfVisibleColumns];
2074 for (i = 1; i < visibleColumns; i++)
2075 {
2076 p1 = NSMakePoint((_columnSize.width * i) + 2 + (i-1),
2077 _columnSize.height + scrollerWidth + 2);
2078 p2 = NSMakePoint((_columnSize.width * i) + 2 + (i-1), scrollerWidth + 3);
2079 [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
2080 }
2081
2082 // Horizontal scroller border
2083 p1 = NSMakePoint(2, scrollerWidth + 2);
2084 p2 = NSMakePoint(rect.size.width - 2, scrollerWidth + 2);
2085 [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
2086 }
2087 }
2088
2089 /* Informs the receivers's subviews that the receiver's bounds
2090 rectangle size has changed from oldFrameSize. */
2091 - (void) resizeSubviewsWithOldSize: (NSSize)oldSize
2092 {
2093 [self tile];
2094 }
2095
2096
2097 /* Override NSControl handler (prevents highlighting). */
2098 - (void) mouseDown: (NSEvent *)theEvent
2099 {
2100 }
2101
2102 - (void) moveLeft: (id)sender
2103 {
2104 if (_acceptsArrowKeys)
2105 {
2106 NSMatrix *matrix;
2107 int selectedColumn;
2108
2109 matrix = (NSMatrix *)[_window firstResponder];
2110 selectedColumn = [self columnOfMatrix:matrix];
2111 if (selectedColumn == -1)
2112 {
2113 selectedColumn = [self selectedColumn];
2114 matrix = [self matrixInColumn: selectedColumn];
2115 }
2116 if (selectedColumn > 0)
2117 {
2118 [matrix deselectAllCells];
2119 [matrix scrollCellToVisibleAtRow:0 column:0];
2120 [self setLastColumn: selectedColumn];
2121
2122 selectedColumn--;
2123 [self scrollColumnToVisible: selectedColumn];
2124 matrix = [self matrixInColumn: selectedColumn];
2125 [_window makeFirstResponder: matrix];
2126
2127 if (_sendsActionOnArrowKeys == YES)
2128 {
2129 [super sendAction: _action to: _target];
2130 }
2131 }
2132 }
2133 }
2134
2135 - (void) moveRight: (id)sender
2136 {
2137 if (_acceptsArrowKeys)
2138 {
2139 NSMatrix *matrix;
2140 int selectedColumn;
2141
2142 matrix = (NSMatrix *)[_window firstResponder];
2143 selectedColumn = [self columnOfMatrix:matrix];
2144 if (selectedColumn == -1)
2145 {
2146 selectedColumn = [self selectedColumn];
2147 matrix = [self matrixInColumn: selectedColumn];
2148 }
2149 if (selectedColumn == -1)
2150 {
2151 selectedColumn = 0;
2152 matrix = [self matrixInColumn: 0];
2153
2154 if ([[matrix cells] count])
2155 {
2156 [matrix selectCellAtRow: 0 column: 0];
2157 }
2158 }
2159 else
2160 {
2161 // if there is one selected cell and it is a leaf, move right
2162 // (column is already loaded)
2163 if (![[matrix selectedCell] isLeaf]
2164 && [[matrix selectedCells] count] == 1)
2165 {
2166 selectedColumn++;
2167 matrix = [self matrixInColumn: selectedColumn];
2168 if ([[matrix cells] count] && [matrix selectedCell] == nil)
2169 {
2170 [matrix selectCellAtRow: 0 column: 0];
2171 }
2172 // if selected cell is a leaf, we need to add a column
2173 if (![[matrix selectedCell] isLeaf]
2174 && [[matrix selectedCells] count] == 1)
2175 {
2176 [self addColumn];
2177 }
2178 }
2179 }
2180
2181 [_window makeFirstResponder: matrix];
2182
2183 if (_sendsActionOnArrowKeys == YES)
2184 {
2185 [super sendAction: _action to: _target];
2186 }
2187 }
2188 }
2189
2190 - (void) keyDown: (NSEvent *)theEvent
2191 {
2192 NSString *characters = [theEvent characters];
2193 unichar character = 0;
2194
2195 if ([characters length] > 0)
2196 {
2197 character = [characters characterAtIndex: 0];
2198 }
2199
2200 if (_acceptsArrowKeys)
2201 {
2202 switch (character)
2203 {
2204 case NSUpArrowFunctionKey:
2205 case NSDownArrowFunctionKey:
2206 return;
2207 case NSLeftArrowFunctionKey:
2208 [self moveLeft:self];
2209 return;
2210 case NSRightArrowFunctionKey:
2211 [self moveRight:self];
2212 return;
2213 case NSTabCharacter:
2214 {
2215 if ([theEvent modifierFlags] & NSShiftKeyMask)
2216 {
2217 [_window selectKeyViewPrecedingView: self];
2218 }
2219 else
2220 {
2221 [_window selectKeyViewFollowingView: self];
2222 }
2223 }
2224 return;
2225 break;
2226 }
2227 }
2228
2229 if (_acceptsAlphaNumericalKeys && (character < 0xF700)
2230 && ([characters length] > 0))
2231 {
2232 NSMatrix *matrix;
2233 NSString *sv;
2234 int i, n, s;
2235 int selectedColumn;
2236 SEL lcarcSel = @selector(loadedCellAtRow:column:);
2237 IMP lcarc = [self methodForSelector: lcarcSel];
2238
2239 selectedColumn = [self selectedColumn];
2240 if(selectedColumn != -1)
2241 {
2242 matrix = [self matrixInColumn: selectedColumn];
2243 n = [matrix numberOfRows];
2244 s = [matrix selectedRow];
2245
2246 if (!_charBuffer)
2247 {
2248 _charBuffer = [characters substringToIndex: 1];
2249 RETAIN(_charBuffer);
2250 }
2251 else
2252 {
2253 if (([theEvent timestamp] - _lastKeyPressed < 2000.0)
2254 && (_alphaNumericalLastColumn == selectedColumn))
2255 {
2256 NSString *transition;
2257 transition = [_charBuffer
2258 stringByAppendingString:
2259 [characters substringToIndex: 1]];
2260 RELEASE(_charBuffer);
2261 _charBuffer = transition;
2262 RETAIN(_charBuffer);
2263 }
2264 else
2265 {
2266 RELEASE(_charBuffer);
2267 _charBuffer = [characters substringToIndex: 1];
2268 RETAIN(_charBuffer);
2269 }
2270 }
2271
2272 _alphaNumericalLastColumn = selectedColumn;
2273 _lastKeyPressed = [theEvent timestamp];
2274
2275 sv = [((*lcarc)(self, lcarcSel, s, selectedColumn))
2276 stringValue];
2277
2278 if (([sv length] > 0)
2279 && ([sv hasPrefix: _charBuffer]))
2280 return;
2281
2282 for (i = s+1; i < n; i++)
2283 {
2284 sv = [((*lcarc)(self, lcarcSel, i, selectedColumn))
2285 stringValue];
2286 if (([sv length] > 0)
2287 && ([sv hasPrefix: _charBuffer]))
2288 {
2289 [self selectRow: i
2290 inColumn: selectedColumn];
2291 [matrix scrollCellToVisibleAtRow: i column: 0];
2292 [matrix performClick: self];
2293 return;
2294 }
2295 }
2296 for (i = 0; i < s; i++)
2297 {
2298 sv = [((*lcarc)(self, lcarcSel, i, selectedColumn))
2299 stringValue];
2300 if (([sv length] > 0)
2301 && ([sv hasPrefix: _charBuffer]))
2302 {
2303 [self selectRow: i
2304 inColumn: selectedColumn];
2305 [matrix scrollCellToVisibleAtRow: i column: 0];
2306 [matrix performClick: self];
2307 return;
2308 }
2309 }
2310 }
2311 _lastKeyPressed = 0.;
2312 }
2313
2314 [super keyDown: theEvent];
2315 }
2316
2317 /*
2318 * NSCoding protocol
2319 *
2320 * We do not encode most of the instance variables except the Browser columns
2321 * because they are internal objects (though not transportable). So we just
2322 * encode enoguh information to rebuild identical columns on the decoder
2323 * side. Same for the Horizontal Scroller
2324 */
2325
2326 - (void) encodeWithCoder: (NSCoder*)aCoder
2327 {
2328 [super encodeWithCoder: aCoder];
2329
2330 // Here to keep compatibility with old version
2331 [aCoder encodeObject: nil];
2332 [aCoder encodeObject:_browserCellPrototype];
2333 [aCoder encodeObject: NSStringFromClass (_browserMatrixClass)];
2334
2335 [aCoder encodeObject:_pathSeparator];
2336 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isLoaded];
2337 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsBranchSelection];
2338 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
2339 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
2340 [aCoder encodeValueOfObjCType: @encode(int) at: &_maxVisibleColumns];
2341 [aCoder encodeValueOfObjCType: @encode(float) at: &_minColumnWidth];
2342 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_reusesColumns];
2343 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_separatesColumns];
2344 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_takesTitleFromPreviousColumn];
2345 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isTitled];
2346
2347
2348 [aCoder encodeObject:_horizontalScroller];
2349 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHorizontalScroller];
2350 [aCoder encodeRect: _scrollerRect];
2351 [aCoder encodeSize: _columnSize];
2352
2353 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_acceptsArrowKeys];
2354 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_sendsActionOnArrowKeys];
2355 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_acceptsAlphaNumericalKeys];
2356 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_sendsActionOnAlphaNumericalKeys];
2357
2358 [aCoder encodeConditionalObject:_browserDelegate];
2359
2360 [aCoder encodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
2361 [aCoder encodeConditionalObject: _target];
2362 [aCoder encodeValueOfObjCType: @encode(SEL) at: &_action];
2363
2364 [aCoder encodeObject: _browserColumns];
2365
2366 // Just encode the number of columns and the first visible
2367 // and rebuild the browser columns on the decoding side
2368 {
2369 int colCount = [_browserColumns count];
2370 [aCoder encodeValueOfObjCType: @encode(int) at: &colCount];
2371 [aCoder encodeValueOfObjCType: @encode(int) at: &_firstVisibleColumn];
2372 }
2373
2374 }
2375
2376 - (id) initWithCoder: (NSCoder*)aDecoder
2377 {
2378 int colCount;
2379 id dummy;
2380
2381 [super initWithCoder: aDecoder];
2382 // Here to keep compatibility with old version
2383 dummy = [aDecoder decodeObject];
2384 _browserCellPrototype = RETAIN([aDecoder decodeObject]);
2385 _browserMatrixClass = NSClassFromString ((NSString *)[aDecoder decodeObject]);
2386
2387 [self setPathSeparator: [aDecoder decodeObject]];
2388
2389 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isLoaded];
2390 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsBranchSelection];
2391 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
2392 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
2393 [aDecoder decodeValueOfObjCType: @encode(int) at: &_maxVisibleColumns];
2394 [aDecoder decodeValueOfObjCType: @encode(float) at: &_minColumnWidth];
2395 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_reusesColumns];
2396 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_separatesColumns];
2397 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_takesTitleFromPreviousColumn];
2398 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isTitled];
2399
2400 //NSBox *_horizontalScrollerBox;
2401 _horizontalScroller = RETAIN([aDecoder decodeObject]);
2402 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHorizontalScroller];
2403 _scrollerRect = [aDecoder decodeRect];
2404 _columnSize = [aDecoder decodeSize];
2405
2406 _skipUpdateScroller = NO;
2407 /*
2408 _horizontalScroller = [[NSScroller alloc] initWithFrame: _scrollerRect];
2409 [_horizontalScroller setTarget: self];
2410 [_horizontalScroller setAction: @selector(scrollViaScroller:)];
2411 */
2412 [self setHasHorizontalScroller: _hasHorizontalScroller];
2413
2414 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_acceptsArrowKeys];
2415 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_sendsActionOnArrowKeys];
2416 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_acceptsAlphaNumericalKeys];
2417 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_sendsActionOnAlphaNumericalKeys];
2418 _lastKeyPressed = 0;
2419 _charBuffer = nil;
2420 // Skip: int _alphaNumericalLastColumn;
2421
2422 _browserDelegate = [aDecoder decodeObject];
2423 if (_browserDelegate != nil)
2424 [self setDelegate:_browserDelegate];
2425 else
2426 _passiveDelegate = YES;
2427
2428
2429 [aDecoder decodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
2430 _target = [aDecoder decodeObject];
2431 [aDecoder decodeValueOfObjCType: @encode(SEL) at: &_action];
2432
2433
2434 // Do the minimal thing to initiate the browser...
2435 /*
2436 _lastColumnLoaded = -1;
2437 _firstVisibleColumn = 0;
2438 _lastVisibleColumn = 0;
2439 [self _createColumn];
2440 */
2441 _browserColumns = RETAIN([aDecoder decodeObject]);
2442 // ..and rebuild any existing browser columns
2443 [aDecoder decodeValueOfObjCType: @encode(int) at: &colCount];
2444 [aDecoder decodeValueOfObjCType: @encode(int) at: &_firstVisibleColumn];
2445
2446 // Display even if there isn't any column
2447 _isLoaded = NO;
2448 [self tile];
2449 return self;
2450 }
2451
2452
2453
2454 /*
2455 * Div.
2456 */
2457
2458 - (BOOL) isOpaque
2459 {
2460 return YES; // See drawRect.
2461 }
2462
2463 @end
2464
2465 @implementation NSBrowser (GNUstepExtensions)
2466 /*
2467 * Setting the behavior of arrow keys
2468 */
2469
2470 /** Returns YES if the alphanumerical keys are enabled. */
2471 - (BOOL) acceptsAlphaNumericalKeys
2472 {
2473 return _acceptsAlphaNumericalKeys;
2474 }
2475
2476 /** Enables or disables the arrow keys as used for navigating within
2477 and between browsers. */
2478 - (void) setAcceptsAlphaNumericalKeys: (BOOL)flag
2479 {
2480 _acceptsAlphaNumericalKeys = flag;
2481 }
2482
2483 /** Returns NO if pressing an arrow key only scrolls the browser, YES if
2484 it also sends the action message specified by setAction:. */
2485 - (BOOL) sendsActionOnAlphaNumericalKeys
2486 {
2487 return _sendsActionOnAlphaNumericalKeys;
2488 }
2489
2490 /** Sets whether pressing an arrow key will cause the action message
2491 to be sent (in addition to causing scrolling). */
2492 - (void) setSendsActionOnAlphaNumericalKeys: (BOOL)flag
2493 {
2494 _sendsActionOnAlphaNumericalKeys = flag;
2495 }
2496
2497 @end
2498
2499
2500 /*
2501 *
2502 * PRIVATE METHODS
2503 *
2504 */
2505 @implementation NSBrowser (Private)
2506
2507 - (void) _remapColumnSubviews: (BOOL)fromFirst
2508 {
2509 id bc, sc;
2510 int i, count;
2511 id firstResponder = nil;
2512 BOOL setFirstResponder = NO;
2513
2514 // Removes all column subviews.
2515 count = [_browserColumns count];
2516 for (i = 0; i < count; i++)
2517 {
2518 bc = [_browserColumns objectAtIndex: i];
2519 sc = [bc columnScrollView];
2520
2521 if (!firstResponder && [bc columnMatrix] == [_window firstResponder])
2522 {
2523 firstResponder = [bc columnMatrix];
2524 }
2525 if (sc)
2526 {
2527 [sc removeFromSuperviewWithoutNeedingDisplay];
2528 }
2529 }
2530
2531 if (_firstVisibleColumn > _lastVisibleColumn)
2532 return;
2533
2534 // Sets columns subviews order according to fromFirst (display order...).
2535 // All added subviews are automaticaly marked as needing display (->
2536 // NSView).
2537 if (fromFirst)
2538 {
2539 for (i = _firstVisibleColumn; i <= _lastVisibleColumn; i++)
2540 {
2541 bc = [_browserColumns objectAtIndex: i];
2542 sc = [bc columnScrollView];
2543 [self addSubview: sc];
2544
2545 if ([bc columnMatrix] == firstResponder)
2546 {
2547 [_window makeFirstResponder: firstResponder];
2548 setFirstResponder = YES;
2549 }
2550 }
2551
2552 if (firstResponder && setFirstResponder == NO)
2553 {
2554 [_window makeFirstResponder:
2555 [[_browserColumns objectAtIndex: _firstVisibleColumn]
2556 columnMatrix]];
2557 }
2558 }
2559 else
2560 {
2561 for (i = _lastVisibleColumn; i >= _firstVisibleColumn; i--)
2562 {
2563 bc = [_browserColumns objectAtIndex: i];
2564 sc = [bc columnScrollView];
2565 [self addSubview: sc];
2566
2567 if ([bc columnMatrix] == firstResponder)
2568 {
2569 [_window makeFirstResponder: firstResponder];
2570 setFirstResponder = YES;
2571 }
2572 }
2573
2574 if (firstResponder && setFirstResponder == NO)
2575 {
2576 [_window makeFirstResponder:
2577 [[_browserColumns objectAtIndex: _lastVisibleColumn]
2578 columnMatrix]];
2579 }
2580 }
2581 }
2582
2583 /* Loads column 'column' (asking the delegate). */
2584 - (void) _performLoadOfColumn: (int)column
2585 {
2586 id bc, sc, matrix;
2587 int i, rows, cols;
2588
2589 if (_passiveDelegate)
2590 {
2591 // Ask the delegate for the number of rows
2592 rows = [_browserDelegate browser: self numberOfRowsInColumn: column];
2593 cols = 1;
2594 }
2595 else
2596 {
2597 rows = 0;
2598 cols = 0;
2599 }
2600
2601 bc = [_browserColumns objectAtIndex: column];
2602
2603 if (!(sc = [bc columnScrollView]))
2604 return;
2605
2606 matrix = [bc columnMatrix];
2607
2608 if (_reusesColumns && matrix)
2609 {
2610 [matrix renewRows: rows columns: cols];
2611
2612 // Mark all the cells as unloaded
2613 for (i = 0; i < rows; i++)
2614 {
2615 [[matrix cellAtRow: i column: 0] setLoaded: NO];
2616 }
2617 }
2618 else
2619 {
2620 NSRect matrixRect = {{0, 0}, {100, 100}};
2621 NSSize matrixIntercellSpace = {0, 0};
2622
2623 // create a new col matrix
2624 matrix = [[_browserMatrixClass alloc]
2625 initWithFrame: matrixRect
2626 mode: NSListModeMatrix
2627 prototype: _browserCellPrototype
2628 numberOfRows: rows
2629 numberOfColumns: cols];
2630 [matrix setIntercellSpacing: matrixIntercellSpace];
2631 [matrix setAllowsEmptySelection: _allowsEmptySelection];
2632 [matrix setAutoscroll: YES];
2633 if (!_allowsMultipleSelection)
2634 {
2635 [matrix setMode: NSRadioModeMatrix];
2636 }
2637 [matrix setTarget: self];
2638 [matrix setAction: @selector(doClick:)];
2639 [matrix setDoubleAction: @selector(doDoubleClick:)];
2640
2641 // set new col matrix and release old
2642 [bc setColumnMatrix: matrix];
2643 RELEASE (matrix);
2644 }
2645 [sc setDocumentView: matrix];
2646
2647 // Loading is different based upon passive/active delegate
2648 if (_passiveDelegate)
2649 {
2650 // Now loop through the cells and load each one
2651 id aCell;
2652 SEL sel1 = @selector(browser:willDisplayCell:atRow:column:);
2653 IMP imp1 = [_browserDelegate methodForSelector: sel1];
2654 SEL sel2 = @selector(cellAtRow:column:);
2655 IMP imp2 = [matrix methodForSelector: sel2];
2656
2657 for (i = 0; i < rows; i++)
2658 {
2659 aCell = (*imp2)(matrix, sel2, i, 0);
2660 if (![aCell isLoaded])
2661 {
2662 (*imp1)(_browserDelegate, sel1, self, aCell, i,
2663 column);
2664 [aCell setLoaded: YES];
2665 }
2666 }
2667 }
2668 else
2669 {
2670 // Tell the delegate to create the rows
2671 [_browserDelegate browser: self
2672 createRowsForColumn: column
2673 inMatrix: matrix];
2674 }
2675
2676 [sc setNeedsDisplay: YES];
2677 [bc setIsLoaded: YES];
2678
2679 if (column > _lastColumnLoaded)
2680 {
2681 _lastColumnLoaded = column;
2682 }
2683
2684 /* Determine the height of a cell in the matrix, and set that as the
2685 cellSize of the matrix. */
2686 {
2687 NSSize cs, ms;
2688 NSBrowserCell *b = [matrix cellAtRow: 0 column: 0];
2689
2690 if (b != nil)
2691 {
2692 ms = [b cellSize];
2693 }
2694 else
2695 {
2696 ms = [matrix cellSize];
2697 }
2698 cs = [sc contentSize];
2699 ms.width = cs.width;
2700 [matrix setCellSize: ms];
2701 }
2702
2703 // Get the title even when untitled, as this may change later.
2704 [self setTitle: [self _getTitleOfColumn: column] ofColumn: column];
2705 }
2706
2707 /* Get the title of a column. */
2708 - (NSString *) _getTitleOfColumn: (int)column
2709 {
2710 // Ask the delegate for the column title
2711 if ([_browserDelegate respondsToSelector:
2712 @selector(browser:titleOfColumn:)])
2713 {
2714 return [_browserDelegate browser: self titleOfColumn: column];
2715 }
2716
2717
2718 // Check if we take title from previous column
2719 if (_takesTitleFromPreviousColumn)
2720 {
2721 id c;
2722
2723 // If first column then use the path separator
2724 if (column == 0)
2725 {
2726 return _pathSeparator;
2727 }
2728
2729 // Get the selected cell
2730 // Use its string value as the title
2731 // Only if it is not a leaf
2732 if(_allowsMultipleSelection == NO)
2733 {
2734 c = [self selectedCellInColumn: column - 1];
2735 }
2736 else
2737 {
2738 NSMatrix *matrix;
2739 NSArray *selectedCells;
2740
2741 if (!(matrix = [self matrixInColumn: column - 1]))
2742 return @"";
2743
2744 selectedCells = [matrix selectedCells];
2745
2746 if([selectedCells count] == 1)
2747 {
2748 c = [selectedCells objectAtIndex:0];
2749 }
2750 else
2751 {
2752 return @"";
2753 }
2754 }
2755
2756 if ([c isLeaf])
2757 {
2758 return @"";
2759 }
2760 else
2761 {
2762 NSString *value = [c stringValue];
2763
2764 if (value != nil)
2765 {
2766 return value;
2767 }
2768 else
2769 {
2770 return @"";
2771 }
2772 }
2773 }
2774 return @"";
2775 }
2776
2777 /* Marks all titles as needing to be redrawn. */
2778 - (void) _setColumnTitlesNeedDisplay
2779 {
2780 if (_isTitled)
2781 {
2782 NSRect r = [self titleFrameOfColumn: _firstVisibleColumn];
2783
2784 r.size.width = _frame.size.width;
2785 [self setNeedsDisplayInRect: r];
2786 }
2787 }
2788
2789 @end

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26