1/** <title>NSFontPanel</title> 2 3 <abstract>System generic panel for selecting and previewing fonts</abstract> 4 5 Copyright (C) 1996 Free Software Foundation, Inc. 6 7 Author: Fred Kiefer <FredKiefer@gmx.de> 8 Date: Febuary 2000 9 Author: Nicola Pero <n.pero@mi.flashnet.it> 10 Date: January 2001 - sizings and resizings 11 12 This file is part of the GNUstep GUI Library. 13 14 This library is free software; you can redistribute it and/or 15 modify it under the terms of the GNU Lesser General Public 16 License as published by the Free Software Foundation; either 17 version 2 of the License, or (at your option) any later version. 18 19 This library is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 Lesser General Public License for more details. 23 24 You should have received a copy of the GNU Lesser General Public 25 License along with this library; see the file COPYING.LIB. 26 If not, see <http://www.gnu.org/licenses/> or write to the 27 Free Software Foundation, 51 Franklin Street, Fifth Floor, 28 Boston, MA 02110-1301, USA. 29*/ 30 31#include "config.h" 32#import <Foundation/NSDebug.h> 33#import <Foundation/NSValue.h> 34#import "AppKit/NSDragging.h" 35#import "AppKit/NSFont.h" 36#import "AppKit/NSFontPanel.h" 37#import "AppKit/NSFontManager.h" 38#import "AppKit/NSApplication.h" 39#import "AppKit/NSSplitView.h" 40#import "AppKit/NSScrollView.h" 41#import "AppKit/NSBrowser.h" 42#import "AppKit/NSBrowserCell.h" 43#import "AppKit/NSTextView.h" 44#import "AppKit/NSTextField.h" 45#import "AppKit/NSTextFieldCell.h" 46#import "AppKit/NSColor.h" 47#import "AppKit/NSPanel.h" 48#import "AppKit/NSButton.h" 49#import "AppKit/NSBox.h" 50#import "GNUstepGUI/GSCharacterPanel.h" 51 52#import "GSGuiPrivate.h" 53 54#define _SAVE_PANEL_X_PAD 5 55#define _SAVE_PANEL_Y_PAD 4 56 57static inline void _setFloatValue (NSTextField *field, float size) 58{ 59 /* If casting size to int and then back to float we get no change, 60 it means it's an integer */ 61 if ((float)((int)size) == size) 62 { 63 /* We prefer using this if it's an int, so that it's 64 displayed like in `8' rather than like in 65 `8.0000000000'. Yes - when NSCell's formatters are 66 finished we won't need this. */ 67 [field setIntValue: (int)size]; 68 } 69 else 70 { 71 [field setFloatValue: size]; 72 } 73} 74 75 76static NSText *sizeFieldText = nil; 77 78static float sizes[] = {4.0, 6.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 79 14.0, 16.0, 18.0, 24.0, 36.0, 48.0, 64.0}; 80 81/* Implemented in NSBrowser */ 82@interface GSBrowserTitleCell : NSTextFieldCell 83{ 84} 85@end 86 87@interface NSFontPanel (Private) 88- (NSFont*) _fontForSelection: (NSFont*) fontObject; 89 90-(void) _trySelectSize: (float)size 91 updateSizeField: (BOOL)updateSizeField; 92 93// Some action methods 94- (void) cancel: (id) sender; 95- (void) _togglePreview: (id) sender; 96- (void) _doPreview; 97- (void) ok: (id) sender; 98 99- (void) _getOriginalSize; 100- (id)_initWithoutGModel; 101 102- (BOOL) _includeFont: (NSString *)fontName delegate: (id)delegate; 103@end 104 105@implementation NSFontPanel 106 107/* 108 * Class methods 109 */ 110+ (void) initialize 111{ 112 if (self == [NSFontPanel class]) 113 { 114 [self setVersion: 1]; 115 } 116} 117 118/** <p>Creates ( if needed ) and returns the shared NSFontPanel.</p> 119 */ 120+ (NSFontPanel*) sharedFontPanel 121{ 122 NSFontManager *fm = [NSFontManager sharedFontManager]; 123 124 return [fm fontPanel: YES]; 125} 126 127+ (BOOL) sharedFontPanelExists 128{ 129 NSFontManager *fm = [NSFontManager sharedFontManager]; 130 131 return ([fm fontPanel: NO] != nil); 132} 133 134/* 135 * Instance methods 136 */ 137- (id) init 138{ 139 // if (![NSBundle loadNibNamed: @"FontPanel" owner: self]); 140 [self _initWithoutGModel]; 141 142 ASSIGN(_faceList, [NSArray array]); 143 _face = -1; 144 _family = -1; 145 [self reloadDefaultFontFamilies]; 146 [self _getOriginalSize]; 147 148 return self; 149} 150 151- (void) dealloc 152{ 153 RELEASE(_panelFont); 154 RELEASE(_familyList); 155 TEST_RELEASE(_faceList); 156 157 TEST_RELEASE(_accessoryView); 158 159 [super dealloc]; 160} 161 162/** <p>Returns whether the "set" button is enabled.</p> 163 <p>See Also: -setEnabled:</p> 164 */ 165- (BOOL) isEnabled 166{ 167 NSButton *setButton = [[self contentView] viewWithTag: NSFPSetButton]; 168 169 return [setButton isEnabled]; 170} 171 172/**<p>Sets whether the "set" button is enabled.</p> 173 <p>See Also: -isEnabled</p> 174 */ 175- (void) setEnabled: (BOOL)flag 176{ 177 NSButton *setButton = [[self contentView] viewWithTag: NSFPSetButton]; 178 179 [setButton setEnabled: flag]; 180} 181 182- (void) reloadDefaultFontFamilies 183{ 184 NSFontManager *fm = [NSFontManager sharedFontManager]; 185 id fmDelegate = [fm delegate]; 186 NSBrowser *familyBrowser = [[self contentView] viewWithTag: NSFPFamilyBrowser]; 187 NSArray *fontFamilies = [fm availableFontFamilies]; 188 unsigned int i,j; 189 NSMutableArray *familyList; 190 191 /* 192 Build an array of all families that have a font that will be included. 193 */ 194 familyList = [[NSMutableArray alloc] 195 initWithCapacity: [fontFamilies count]]; 196 197 for (i = 0; i < [fontFamilies count]; i++) 198 { 199 NSArray *familyMembers; 200 familyMembers = [fm availableMembersOfFontFamily: 201 [fontFamilies objectAtIndex: i]]; 202 203 for (j = 0; j < [familyMembers count]; j++) 204 { 205 if ([self _includeFont: [[familyMembers objectAtIndex: j] objectAtIndex: 0] 206 delegate: fmDelegate]) 207 { 208 [familyList addObject: [fontFamilies objectAtIndex: i]]; 209 break; 210 } 211 } 212 } 213 214 DESTROY(_familyList); 215 _familyList = familyList; 216 // Reload the display. 217 [familyBrowser loadColumnZero]; 218 // Reselect the current font. (Hopefully still there) 219 [self setPanelFont: [fm selectedFont] 220 isMultiple: [fm isMultiple]]; 221} 222 223- (void) setPanelFont: (NSFont *)fontObject 224 isMultiple: (BOOL)flag 225{ 226 NSTextField *previewArea; 227 228 previewArea = [[self contentView] viewWithTag: NSFPPreviewField]; 229 230 ASSIGN(_panelFont, fontObject); 231 _multiple = flag; 232 233 if (fontObject == nil) 234 { 235 return; 236 } 237 238 if (flag) 239 { 240 // TODO: Unselect all items and show a message 241 [previewArea setStringValue: _(@"Multiple fonts selected")]; 242 _family = -1; 243 _face = -1; 244 } 245 else 246 { 247 NSFontManager *fm = [NSFontManager sharedFontManager]; 248 NSString *family = [fontObject familyName]; 249 NSString *fontName = [fontObject fontName]; 250 float size = [fontObject pointSize]; 251 NSBrowser *familyBrowser = [[self contentView] viewWithTag: NSFPFamilyBrowser]; 252 NSBrowser *faceBrowser = [[self contentView] viewWithTag: NSFPFaceBrowser]; 253 NSString *face = @""; 254 unsigned int i; 255 256 // Store style information for font 257 _traits = [fm traitsOfFont: fontObject]; 258 _weight = [fm weightOfFont: fontObject]; 259 260 // Select the row for the font family 261 for (i = 0; i < [_familyList count]; i++) 262 { 263 if ([[_familyList objectAtIndex: i] isEqualToString: family]) 264 break; 265 } 266 if (i < [_familyList count]) 267 { 268 [familyBrowser selectRow: i inColumn: 0]; 269 _family = i; 270 ASSIGN(_faceList, [fm availableMembersOfFontFamily: family]); 271 [faceBrowser loadColumnZero]; 272 _face = -1; 273 } 274 275 // Select the row for the font face 276 for (i = 0; i < [_faceList count]; i++) 277 { 278 if ([[[_faceList objectAtIndex: i] objectAtIndex: 0] 279 isEqualToString: fontName]) 280 break; 281 } 282 if (i < [_faceList count]) 283 { 284 [faceBrowser selectRow: i inColumn: 0]; 285 _face = i; 286 face = [[_faceList objectAtIndex: i] objectAtIndex: 1]; 287 } 288 289 // show point size and select the row if there is one 290 [self _trySelectSize: size updateSizeField: YES]; 291 292 // Use in preview 293 [previewArea setFont: fontObject]; 294 if (_previewString == nil) 295 { 296 [previewArea setStringValue: [NSString stringWithFormat: @"%@ %@ %d PT", 297 family, face, (int)size]]; 298 } 299 } 300} 301 302/**<p>Converts the NSFont <var>fontObject</var></p> 303 */ 304- (NSFont *) panelConvertFont: (NSFont *)fontObject 305{ 306 NSFont *newFont; 307 308 if (_multiple) 309 { 310 //TODO: We go over every item in the panel and check if a 311 // value is selected. If so we send it on to the manager 312 // newFont = [fm convertFont: fontObject toHaveTrait: NSItalicFontMask]; 313 NSLog(@"Multiple font conversion not implemented in NSFontPanel"); 314 newFont = [self _fontForSelection: fontObject]; 315 } 316 else 317 { 318 newFont = [self _fontForSelection: fontObject]; 319 } 320 321 if (newFont == nil) 322 { 323 newFont = fontObject; 324 } 325 326 return newFont; 327} 328 329/**<p>Overides the NSPanel/NSWindow method to always returns YES</p> 330 */ 331- (BOOL) worksWhenModal 332{ 333 return YES; 334} 335 336/** <p>Returns the NSFontPanel's accessory view.</p> 337 <p>See Also: -setAccessoryView:</p> 338 */ 339- (NSView*) accessoryView 340{ 341 return _accessoryView; 342} 343 344/** <p>Sets the NSFontPanel's accessory view to <var>aView</var></p> 345 <p>See Also: -accessoryView</p> 346 */ 347- (void) setAccessoryView: (NSView*)aView 348{ 349 NSRect accessoryViewFrame, bottomFrame; 350 NSRect tmpRect; 351 NSSize contentSize, contentMinSize; 352 float addedHeight, accessoryWidth; 353 354 if (aView == _accessoryView) 355 return; 356 357 /* The following code is very tricky. Please think and test a lot 358 before changing it. */ 359 360 /* Remove old accessory view if any */ 361 if (_accessoryView != nil) 362 { 363 /* Remove accessory view */ 364 accessoryViewFrame = [_accessoryView frame]; 365 [_accessoryView removeFromSuperview]; 366 367 /* Change the min size before doing the resizing otherwise it 368 could be a problem. */ 369 [self setMinSize: _originalMinSize]; 370 371 /* Resize the panel to the height without the accessory view. 372 This must be done with the special care of not resizing 373 the heights of the other views. */ 374 addedHeight = accessoryViewFrame.size.height + (_SAVE_PANEL_Y_PAD * 2); 375 contentSize = [[self contentView] frame].size; 376 contentSize.height -= addedHeight; 377 // Resize without modifying topView and bottomView height. 378 [_topView setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin]; 379 [self setContentSize: contentSize]; 380 [_topView setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; 381 } 382 383 /* Resize the panel to its original size. This resizes freely the 384 heights of the views. NB: minSize *must* come first */ 385 [self setMinSize: _originalMinSize]; 386 [self setContentSize: _originalSize]; 387 388 /* Set the new accessory view */ 389 _accessoryView = aView; 390 391 /* If there is a new accessory view, plug it in */ 392 if (_accessoryView != nil) 393 { 394 /* Make sure the new accessory view behaves - its height must be fixed 395 * and its position relative to the bottom of the superview must not 396 * change - so its position rlative to the top must be changable. */ 397 [_accessoryView setAutoresizingMask: NSViewMaxYMargin 398 | ([_accessoryView autoresizingMask] 399 & ~(NSViewHeightSizable | NSViewMinYMargin))]; 400 401 /* Compute size taken by the new accessory view */ 402 accessoryViewFrame = [_accessoryView frame]; 403 addedHeight = accessoryViewFrame.size.height + (_SAVE_PANEL_Y_PAD * 2); 404 accessoryWidth = accessoryViewFrame.size.width + (_SAVE_PANEL_X_PAD * 2); 405 406 /* Resize content size accordingly */ 407 contentSize = _originalSize; 408 contentSize.height += addedHeight; 409 if (accessoryWidth > contentSize.width) 410 { 411 contentSize.width = accessoryWidth; 412 } 413 414 /* Set new content size without resizing heights of topView, bottomView */ 415 // Our views should resize horizontally if needed, but not vertically 416 [_topView setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin]; 417 [self setContentSize: contentSize]; 418 // Restore the original autoresizing masks 419 [_topView setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; 420 421 /* Compute new min size */ 422 contentMinSize = _originalMinSize; 423 contentMinSize.height += addedHeight; 424 // width is more delicate 425 tmpRect = NSMakeRect (0, 0, contentMinSize.width, contentMinSize.height); 426 tmpRect = [NSWindow contentRectForFrameRect: tmpRect 427 styleMask: [self styleMask]]; 428 if (accessoryWidth > tmpRect.size.width) 429 { 430 contentMinSize.width += accessoryWidth - tmpRect.size.width; 431 } 432 // Set new min size 433 [self setMinSize: contentMinSize]; 434 435 /* 436 * Pack the Views 437 */ 438 439 /* BottomView is ready */ 440 bottomFrame = [_bottomView frame]; 441 442 /* AccessoryView */ 443 accessoryViewFrame.origin.x 444 = (contentSize.width - accessoryViewFrame.size.width) / 2; 445 accessoryViewFrame.origin.y = NSMaxY (bottomFrame) + _SAVE_PANEL_Y_PAD; 446 [_accessoryView setFrameOrigin: accessoryViewFrame.origin]; 447 448 /* Add the accessory view */ 449 [[self contentView] addSubview: _accessoryView]; 450 } 451} 452 453/* 454 * NSCoding protocol 455 */ 456- (void) encodeWithCoder: (NSCoder*)aCoder 457{ 458 [super encodeWithCoder: aCoder]; 459 460 [aCoder encodeObject: _panelFont]; 461 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_multiple]; 462 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_preview]; 463} 464 465- (id) initWithCoder: (NSCoder*)aDecoder 466{ 467 [super initWithCoder: aDecoder]; 468 469 _panelFont = RETAIN([aDecoder decodeObject]); 470 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_multiple]; 471 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_preview]; 472 473 return self; 474} 475 476/* 477 * Overriding fieldEditor:forObject: because we don't want the field 478 * editor (which is used when you type in the size browser) to use the 479 * font panel, otherwise typing in the size browser can modify the 480 * currently selected font in unexpected ways ! */ 481- (NSText *) fieldEditor: (BOOL)createFlag 482 forObject: (id)anObject 483{ 484 if (([anObject respondsToSelector: @selector(tag)]) 485 && ([anObject tag] == NSFPSizeField)) 486 { 487 if ((sizeFieldText == nil) && createFlag) 488 { 489 sizeFieldText = [NSText new]; 490 [sizeFieldText setUsesFontPanel: NO]; 491 [sizeFieldText setFieldEditor: YES]; 492 } 493 return sizeFieldText; 494 } 495 496 return [super fieldEditor: createFlag forObject: anObject]; 497} 498 499@end 500 501@implementation NSFontPanel (Private) 502 503- (id) _initWithoutGModel 504{ 505 NSRect contentRect = {{100, 100}, {320, 300}}; 506 NSRect topAreaRect = {{0, 42}, {320, 258}}; 507 NSRect splitViewRect = {{8, 8}, {304, 243}}; 508 NSRect topSplitRect = {{0, 0}, {304, 45}}; 509 NSRect previewAreaRect = {{0, 1}, {304, 44}}; 510 NSRect bottomSplitRect = {{0, 0}, {304, 190}}; 511 NSRect familyBrowserRect = {{0, 0}, {111, 189}}; 512 NSRect typefaceBrowserRect = {{113, 0}, {111, 189}}; 513 NSRect sizeBrowserRect = {{226, 0}, {78, 143}}; 514 NSRect sizeLabelRect = {{226, 145}, {78, 21}}; 515 NSRect sizeTitleRect = {{226, 168}, {78, 21}}; 516 NSRect bottomAreaRect = {{0, 0}, {320, 42}}; 517 NSRect slashRect = {{0, 40}, {320, 2}}; 518 NSRect revertButtonRect = {{83, 8}, {71, 24}}; 519 NSRect previewButtonRect = {{162, 8}, {71, 24}}; 520 NSRect setButtonRect = {{241, 8}, {71, 24}}; 521 NSRect characterPanelButtonRect = {{8, 8}, {24, 24}}; 522 NSView *v; 523 NSView *topArea; 524 NSView *bottomArea; 525 NSView *topSplit; 526 NSView *bottomSplit; 527 NSSplitView *splitView; 528 NSTextField *previewArea; 529 NSBrowser *sizeBrowser; 530 NSBrowser *familyBrowser; 531 NSBrowser *faceBrowser; 532 NSTextField *label; 533 NSTextField *sizeField; 534 NSButton *revertButton; 535 NSButton *previewButton; 536 NSButton *setButton; 537 NSButton *characterPanelButton; 538 NSBox *slash; 539 540 unsigned int style = NSTitledWindowMask | NSClosableWindowMask 541 | NSResizableWindowMask | NSUtilityWindowMask; 542 543 self = [super initWithContentRect: contentRect 544 styleMask: style 545 backing: NSBackingStoreRetained 546 defer: YES 547 screen: nil]; 548 if (!self) 549 { 550 return nil; 551 } 552 553 [self setTitle: _(@"Font Panel")]; 554 [self setBecomesKeyOnlyIfNeeded: YES]; 555 556 v = [self contentView]; 557 558 // preview and selection 559 topArea = [[NSView alloc] initWithFrame: topAreaRect]; 560 [topArea setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; 561 _topView = topArea; 562 563 splitView = [[NSSplitView alloc] initWithFrame: splitViewRect]; 564 [splitView setVertical: NO]; 565 [splitView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; 566 567 topSplit = [[NSView alloc] initWithFrame: topSplitRect]; 568 [topSplit setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; 569 570 // Display for the font example 571 previewArea = [[NSTextField alloc] initWithFrame: previewAreaRect]; 572 [previewArea setBackgroundColor: [NSColor textBackgroundColor]]; 573 [previewArea setDrawsBackground: YES]; 574 [previewArea setEditable: NO]; 575 [previewArea setSelectable: NO]; 576 //[previewArea setUsesFontPanel: NO]; 577 [previewArea setAlignment: NSCenterTextAlignment]; 578 [previewArea setStringValue: _(@"Font preview")]; 579 [previewArea setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)]; 580 581 [previewArea setTag: NSFPPreviewField]; 582 [topSplit addSubview: previewArea]; 583 RELEASE(previewArea); 584 585 bottomSplit = [[NSView alloc] initWithFrame: bottomSplitRect]; 586 [bottomSplit setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 587 588 // Selection of the font family 589 // We use a browser with one column to get a selection list 590 familyBrowser = [[NSBrowser alloc] initWithFrame: familyBrowserRect]; 591 [familyBrowser setDelegate: self]; 592 [familyBrowser setMaxVisibleColumns: 1]; 593 [familyBrowser setMinColumnWidth: 0]; 594 [familyBrowser setAllowsMultipleSelection: NO]; 595 [familyBrowser setAllowsEmptySelection: YES]; 596 [familyBrowser setHasHorizontalScroller: NO]; 597 [familyBrowser setTitled: YES]; 598 [familyBrowser setTakesTitleFromPreviousColumn: NO]; 599 [familyBrowser setTarget: self]; 600 [familyBrowser setDoubleAction: @selector(familySelected:)]; 601 [familyBrowser setAction: @selector(_familySelectionChanged:)]; 602 [familyBrowser setAutoresizingMask: (NSViewWidthSizable 603 | NSViewMaxXMargin 604 | NSViewHeightSizable)]; 605 [familyBrowser setTag: NSFPFamilyBrowser]; 606 [bottomSplit addSubview: familyBrowser]; 607 RELEASE(familyBrowser); 608 609 // selection of type face 610 // We use a browser with one column to get a selection list 611 faceBrowser = [[NSBrowser alloc] initWithFrame: typefaceBrowserRect]; 612 [faceBrowser setDelegate: self]; 613 [faceBrowser setMaxVisibleColumns: 1]; 614 [faceBrowser setMinColumnWidth: 0]; 615 [faceBrowser setAllowsMultipleSelection: NO]; 616 [faceBrowser setAllowsEmptySelection: YES]; 617 [faceBrowser setHasHorizontalScroller: NO]; 618 [faceBrowser setTitled: YES]; 619 [faceBrowser setTakesTitleFromPreviousColumn: NO]; 620 [faceBrowser setTarget: self]; 621 [faceBrowser setDoubleAction: @selector(faceSelected:)]; 622 [faceBrowser setAction: @selector(_faceSelectionChanged:)]; 623 [faceBrowser setAutoresizingMask: (NSViewWidthSizable 624 | NSViewMinXMargin 625 | NSViewHeightSizable)]; 626 [faceBrowser setTag: NSFPFaceBrowser]; 627 [bottomSplit addSubview: faceBrowser]; 628 RELEASE(faceBrowser); 629 630 // label for selection of size 631 label = [[NSTextField alloc] initWithFrame: sizeTitleRect]; 632 [label setCell: AUTORELEASE([GSBrowserTitleCell new])]; 633 [label setFont: [NSFont boldSystemFontOfSize: 0]]; 634 [label setAlignment: NSCenterTextAlignment]; 635 [label setDrawsBackground: YES]; 636 [label setEditable: NO]; 637 [label setTextColor: [NSColor windowFrameTextColor]]; 638 [label setBackgroundColor: [NSColor controlShadowColor]]; 639 [label setStringValue: _(@"Size")]; 640 [label setAutoresizingMask: NSViewMinXMargin | NSViewMinYMargin]; 641 [label setTag: NSFPSizeTitle]; 642 [bottomSplit addSubview: label]; 643 RELEASE(label); 644 645 // this is the size input field 646 sizeField = [[NSTextField alloc] initWithFrame: sizeLabelRect]; 647 [sizeField setDrawsBackground: YES]; 648 [sizeField setEditable: YES]; 649 [sizeField setAllowsEditingTextAttributes: NO]; 650 [sizeField setAlignment: NSCenterTextAlignment]; 651 [sizeField setAutoresizingMask: NSViewMinXMargin | NSViewMinYMargin]; 652 [sizeField setDelegate: self]; 653 [sizeField setTag: NSFPSizeField]; 654 [bottomSplit addSubview: sizeField]; 655 RELEASE(sizeField); 656 657 sizeBrowser = [[NSBrowser alloc] initWithFrame: sizeBrowserRect]; 658 [sizeBrowser setDelegate: self]; 659 [sizeBrowser setMaxVisibleColumns: 1]; 660 [sizeBrowser setAllowsMultipleSelection: NO]; 661 [sizeBrowser setAllowsEmptySelection: YES]; 662 [sizeBrowser setHasHorizontalScroller: NO]; 663 [sizeBrowser setTitled: NO]; 664 [sizeBrowser setTakesTitleFromPreviousColumn: NO]; 665 [sizeBrowser setTarget: self]; 666 [sizeBrowser setDoubleAction: @selector(sizeSelected:)]; 667 [sizeBrowser setAction: @selector(_sizeSelectionChanged:)]; 668 [sizeBrowser setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable]; 669 [sizeBrowser setTag: NSFPSizeBrowser]; 670 [bottomSplit addSubview: sizeBrowser]; 671 RELEASE(sizeBrowser); 672 673 [splitView addSubview: topSplit]; 674 RELEASE(topSplit); 675 [splitView addSubview: bottomSplit]; 676 RELEASE(bottomSplit); 677 678 [splitView setDelegate: self]; 679 680 [topArea addSubview: splitView]; 681 RELEASE(splitView); 682 683 // action buttons 684 bottomArea = [[NSView alloc] initWithFrame: bottomAreaRect]; 685 _bottomView = bottomArea; 686 687 slash = [[NSBox alloc] initWithFrame: slashRect]; 688 [slash setBorderType: NSGrooveBorder]; 689 [slash setTitlePosition: NSNoTitle]; 690 [slash setAutoresizingMask: NSViewWidthSizable]; 691 [bottomArea addSubview: slash]; 692 RELEASE(slash); 693 694 // cancel button 695 revertButton = [[NSButton alloc] initWithFrame: revertButtonRect]; 696 [revertButton setTitle: _(@"Revert")]; 697 [revertButton setAction: @selector(cancel:)]; 698 [revertButton setTarget: self]; 699 [revertButton setTag: NSFPRevertButton]; 700 [revertButton setAutoresizingMask: NSViewMinXMargin]; 701 [bottomArea addSubview: revertButton]; 702 RELEASE(revertButton); 703 704 // toggle button for preview 705 previewButton = [[NSButton alloc] initWithFrame: previewButtonRect]; 706 [previewButton setTitle: _(@"Preview")]; 707 [previewButton setButtonType: NSOnOffButton]; 708 [previewButton setAction: @selector(_togglePreview:)]; 709 [previewButton setTarget: self]; 710 [previewButton setTag: NSFPPreviewButton]; 711 [previewButton setAutoresizingMask: NSViewMinXMargin]; 712 [previewButton setState: NSOnState]; 713 _preview = YES; 714 [bottomArea addSubview: previewButton]; 715 RELEASE(previewButton); 716 717 // button to set the font 718 setButton = [[NSButton alloc] initWithFrame: setButtonRect]; 719 [setButton setTitle: _(@"Set")]; 720 [setButton setAction: @selector(ok:)]; 721 [setButton setTarget: self]; 722 [setButton setTag: NSFPSetButton]; 723 [setButton setAutoresizingMask: NSViewMinXMargin]; 724 [bottomArea addSubview: setButton]; 725 // make it the default button 726 [self setDefaultButtonCell: [setButton cell]]; 727 RELEASE(setButton); 728 729 // Character Panel button 730 { 731 NSString *label; 732 unichar labelchars[2] = {0x03b1, 0x03b2}; // alpha, beta 733 label = [[[NSString alloc] initWithCharacters: labelchars 734 length: 2] autorelease]; 735 736 characterPanelButton = [[NSButton alloc] initWithFrame: characterPanelButtonRect]; 737 [characterPanelButton setTitle: label]; 738 [characterPanelButton setToolTip: _(@"Character Panel")]; 739 [characterPanelButton setAction: @selector(characterPanel:)]; 740 [characterPanelButton setTarget: self]; 741 [bottomArea addSubview: characterPanelButton]; 742 RELEASE(characterPanelButton); 743 } 744 745 // set up the next key view chain 746 [familyBrowser setNextKeyView: faceBrowser]; 747 [faceBrowser setNextKeyView: sizeField]; 748 [sizeField setNextKeyView: sizeBrowser]; 749 [sizeBrowser setNextKeyView: revertButton]; 750 [revertButton setNextKeyView: previewButton]; 751 [previewButton setNextKeyView: setButton]; 752 [setButton setNextKeyView: familyBrowser]; 753 754 [v addSubview: topArea]; 755 RELEASE(topArea); 756 757 // Add the accessory view, if there is one 758 if (_accessoryView != nil) 759 { 760 [v addSubview: _accessoryView]; 761 } 762 763 [bottomArea setAutoresizingMask: NSViewWidthSizable]; 764 [v addSubview: bottomArea]; 765 RELEASE(bottomArea); 766 767 [self setMinSize: [self frame].size]; 768 769 [self setInitialFirstResponder: setButton]; 770 [self setBecomesKeyOnlyIfNeeded: YES]; 771 772 return self; 773} 774 775 776- (void) _togglePreview: (id)sender 777{ 778 _preview = (sender == nil) ? YES : [sender state]; 779 [self _doPreview]; 780} 781 782- (void) _doPreview 783{ 784 NSFont *font = nil; 785 NSTextField *previewArea = [[self contentView] viewWithTag: NSFPPreviewField]; 786 787 if (_preview) 788 { 789 font = [self _fontForSelection: _panelFont]; 790 // build up a font and use it in the preview area 791 if (font != nil) 792 { 793 [previewArea setFont: font]; 794 } 795 } 796 797 if (_previewString == nil) 798 { 799 NSTextField *sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 800 float size = [sizeField floatValue]; 801 NSString *faceName; 802 NSString *familyName; 803 804 if (size == 0 && font != nil) 805 { 806 size = [font pointSize]; 807 } 808 if (_family == -1) 809 { 810 familyName = _(@"NoFamily"); 811 } 812 else 813 { 814 familyName = [_familyList objectAtIndex: _family]; 815 } 816 if (_face == -1 || ![_faceList count]) 817 { 818 faceName = _(@"NoFace"); 819 } 820 else 821 { 822 faceName = [[_faceList objectAtIndex: _face] objectAtIndex: 1]; 823 } 824 [previewArea setStringValue: [NSString stringWithFormat: @"%@ %@ %d PT", 825 familyName, faceName, (int)size]]; 826 } 827} 828 829- (void) ok: (id)sender 830{ 831 // The set button has been pushed 832 NSFontManager *fm = [NSFontManager sharedFontManager]; 833 834 [fm modifyFontViaPanel: self]; 835} 836 837- (void) cancel: (id)sender 838{ 839 // FIXME/TODO 840 841 /* 842 * The cancel button has been pushed 843 * we should reset the items in the panel 844 */ 845 [self setPanelFont: _panelFont 846 isMultiple: _multiple]; 847} 848 849- (void) characterPanel: (id)sender 850{ 851 [[NSApplication sharedApplication] orderFrontCharacterPalette: sender]; 852} 853 854- (NSFont *) _fontForSelection: (NSFont *)fontObject 855{ 856 float size; 857 NSString *fontName; 858 NSTextField *sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 859 unsigned i = [_faceList count]; 860 861 size = [sizeField floatValue]; 862 if (size == 0.0) 863 { 864 if (fontObject == nil) 865 { 866 size = 12.0; 867 } 868 else 869 { 870 size = [fontObject pointSize]; 871 } 872 } 873 if (_face < 0) 874 { 875 876 if (i == 0) 877 { 878 return nil; /* Nothing available */ 879 } 880 // FIXME - just uses first face 881 fontName = [[_faceList objectAtIndex: 0] objectAtIndex: 0]; 882 } 883 else 884 { 885 /* 886 i really should be > 0 here, except for the very obscure case where 887 the delegate has refused all fonts (so that our family and face lists 888 are completely empty). 889 */ 890 if (i) 891 fontName = [[_faceList objectAtIndex: _face] objectAtIndex: 0]; 892 else 893 return nil; 894 } 895 896 // FIXME: We should check if the font is correct 897 return [NSFont fontWithName: fontName size: size]; 898} 899 900 901-(void) _trySelectSize: (float)size 902 updateSizeField: (BOOL)updateSizeField 903{ 904 unsigned int i; 905 NSBrowser *sizeBrowser = [[self contentView] viewWithTag: NSFPSizeBrowser]; 906 NSTextField *sizeField; 907 908 if (updateSizeField) 909 { 910 /* Make sure our sizeField is updated. */ 911 sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 912 _setFloatValue (sizeField, size); 913 } 914 915 /* Make sure our column is loaded. */ 916 [sizeBrowser loadColumnZero]; 917 918 for (i = 0; i < sizeof(sizes) / sizeof(float); i++) 919 { 920 if (size == sizes[i]) 921 { 922 /* select the cell */ 923 [sizeBrowser selectRow: i inColumn: 0]; 924 break; 925 } 926 } 927 if (i == sizeof(sizes) / sizeof(float)) 928 { 929 /* TODO: No matching size found in the list. We should deselect 930 everything. */ 931 } 932} 933 934- (void) controlTextDidChange: (NSNotification *)n 935{ 936 NSTextField *sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 937 float size = [sizeField floatValue]; 938 [self _trySelectSize: size updateSizeField: NO]; 939 [self _doPreview]; 940} 941 942/* 943Ask the NSFontManager:s delegate if a font should be included. For speed, 944the delegate is an argument; a repeat-caller can then cache it. 945*/ 946- (BOOL) _includeFont: (NSString*)fontName delegate: (id)fmDelegate 947{ 948 if (fmDelegate != nil 949 && [fmDelegate respondsToSelector: @selector(fontManager:willIncludeFont:)]) 950 { 951 return [fmDelegate fontManager: [NSFontManager sharedFontManager] 952 willIncludeFont: fontName]; 953 } 954 else 955 return YES; 956} 957 958 959- (void) _getOriginalSize 960{ 961 /* Used in setMinSize: */ 962 _originalMinSize = [self minSize]; 963 /* Used in setContentSize: */ 964 _originalSize = [[self contentView] frame].size; 965} 966 967@end 968 969 970@implementation NSFontPanel (NSBrowserDelegate) 971 972 973static int score_difference(int weight1, int traits1, 974 int weight2, int traits2) 975{ 976 int score, t; 977 978 score = (weight1 - weight2); 979 score = 10 * score * score; 980 981 t = traits1 ^ traits2; 982 983 if (t & NSFixedPitchFontMask) score += 1000; 984 if (t & NSCompressedFontMask) score += 150; 985 if (t & NSPosterFontMask) score += 200; 986 if (t & NSSmallCapsFontMask) score += 200; 987 if (t & NSCondensedFontMask) score += 150; 988 if (t & NSExpandedFontMask) score += 150; 989 if (t & NSNarrowFontMask) score += 150; 990 if (t & NSBoldFontMask) score += 20; 991 if (t & NSItalicFontMask) score += 45; 992 993 return score; 994} 995 996 997- (void) _familySelectionChanged: (id)sender 998{ 999 NSFontManager *fm = [NSFontManager sharedFontManager]; 1000 id fmDelegate = [fm delegate]; 1001 1002 NSBrowser *faceBrowser = [[self contentView] viewWithTag: NSFPFaceBrowser]; 1003 NSBrowser *familyBrowser = [[self contentView] viewWithTag: NSFPFamilyBrowser]; 1004 int row = [familyBrowser selectedRowInColumn: 0]; 1005 1006 unsigned int i; 1007 NSArray *entireFaceList; 1008 NSMutableArray *faceList; 1009 1010 entireFaceList = [fm availableMembersOfFontFamily: 1011 [_familyList objectAtIndex: row]]; 1012 1013 faceList = [[NSMutableArray alloc] initWithCapacity: [entireFaceList count]]; 1014 1015 for (i = 0; i < [entireFaceList count]; i++) 1016 { 1017 id aFace = [entireFaceList objectAtIndex:i]; 1018 if ([self _includeFont: [aFace objectAtIndex:0] delegate: fmDelegate]) 1019 { 1020 [faceList addObject: aFace]; 1021 } 1022 } 1023 1024 DESTROY(_faceList); 1025 _faceList = faceList; 1026 _family = row; 1027 1028 // Select a face with the same properties 1029 for (i = 0; i < [_faceList count]; i++) 1030 { 1031 NSArray *font_info = [_faceList objectAtIndex: i]; 1032 1033 if (([[font_info objectAtIndex: 2] intValue] == _weight) 1034 && ([[font_info objectAtIndex: 3] unsignedIntValue] == _traits)) 1035 break; 1036 } 1037 1038 // Find the face that differs the least from what we want 1039 if (i == [_faceList count]) 1040 { 1041 int best, best_score, score; 1042 1043 best_score = 1e6; 1044 best = -1; 1045 1046 for (i = 0; i < [_faceList count]; i++) 1047 { 1048 NSArray *font_info = [_faceList objectAtIndex: i]; 1049 score = score_difference(_weight, _traits, 1050 [[font_info objectAtIndex: 2] intValue], 1051 [[font_info objectAtIndex: 3] unsignedIntValue]); 1052 if (score < best_score) 1053 { 1054 best = i; 1055 best_score = score; 1056 } 1057 } 1058 if (best != -1) 1059 i = best; 1060 } 1061 1062 if (i == [_faceList count]) 1063 i = 0; 1064 1065 _face = i; 1066 [faceBrowser loadColumnZero]; 1067 [faceBrowser selectRow: i inColumn: 0]; 1068 1069 /* Also make sure the size column has some value */ 1070 { 1071 NSTextField *sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 1072 float size = [sizeField floatValue]; 1073 1074 if (size == 0.0) 1075 { 1076 [self _trySelectSize: 12.0 updateSizeField: YES]; 1077 } 1078 } 1079 1080 [self _doPreview]; 1081} 1082 1083- (void) _faceSelectionChanged: (id)sender 1084{ 1085 NSBrowser *faceBrowser = [[self contentView] viewWithTag: NSFPFaceBrowser]; 1086 int row = [faceBrowser selectedRowInColumn: 0]; 1087 NSArray *font_info = [_faceList objectAtIndex: row]; 1088 1089 _face = row; 1090 _weight = [[font_info objectAtIndex: 2] intValue]; 1091 _traits = [[font_info objectAtIndex: 3] unsignedIntValue]; 1092 1093 [self _doPreview]; 1094} 1095 1096- (void) _sizeSelectionChanged: (id)sender 1097{ 1098 NSBrowser *sizeBrowser = [[self contentView] viewWithTag: NSFPSizeBrowser]; 1099 int row = [sizeBrowser selectedRowInColumn: 0]; 1100 NSTextField *sizeField; 1101 1102 sizeField = [[self contentView] viewWithTag: NSFPSizeField]; 1103 _setFloatValue (sizeField, sizes[row]); 1104 1105 [self _doPreview]; 1106} 1107 1108 1109- (NSInteger) browser: (NSBrowser*)sender numberOfRowsInColumn: (NSInteger)column 1110{ 1111 switch ([sender tag]) 1112 { 1113 case NSFPFamilyBrowser: 1114 { 1115 return [_familyList count]; 1116 } 1117 case NSFPFaceBrowser: 1118 { 1119 return [_faceList count]; 1120 } 1121 case NSFPSizeBrowser: 1122 { 1123 return sizeof (sizes) / sizeof (float); 1124 } 1125 default: 1126 { 1127 return 0; 1128 } 1129 } 1130} 1131 1132- (NSString*) browser: (NSBrowser*)sender titleOfColumn: (NSInteger)column 1133{ 1134 switch ([sender tag]) 1135 { 1136 case NSFPFamilyBrowser: 1137 { 1138 return _(@"Family"); 1139 } 1140 case NSFPFaceBrowser: 1141 { 1142 return _(@"Typeface"); 1143 } 1144 default: 1145 { 1146 return @""; 1147 } 1148 } 1149} 1150 1151- (void) browser: (NSBrowser *)sender 1152 willDisplayCell: (id)cell 1153 atRow: (NSInteger)row 1154 column: (NSInteger)column 1155{ 1156 NSString *value = nil; 1157 1158 if (row < 0) 1159 return; 1160 1161 switch ([sender tag]) 1162 { 1163 case NSFPFamilyBrowser: 1164 { 1165 if ([_familyList count] > (NSUInteger)row) 1166 { 1167 value = [_familyList objectAtIndex: row]; 1168 } 1169 break; 1170 } 1171 case NSFPFaceBrowser: 1172 { 1173 if ([_faceList count] > (NSUInteger)row) 1174 { 1175 value = [[_faceList objectAtIndex: row] objectAtIndex: 1]; 1176 } 1177 break; 1178 } 1179 case NSFPSizeBrowser: 1180 default: 1181 { 1182 value = [NSString stringWithFormat: @"%d", (int) sizes[row]]; 1183 } 1184 } 1185 1186 [cell setStringValue: value]; 1187 [cell setLeaf: YES]; 1188} 1189 1190- (BOOL) browser: (NSBrowser *)sender 1191 isColumnValid: (NSInteger)column; 1192{ 1193 return NO; 1194} 1195 1196@end 1197 1198@implementation NSFontPanel (NSSplitViewDelegate) 1199 1200- (void) splitView: (NSSplitView *)splitView 1201constrainMinCoordinate: (CGFloat *)min 1202 maxCoordinate: (CGFloat *)max 1203 ofSubviewAt: (NSInteger)offset 1204{ 1205 *max = *max - 100; 1206} 1207 1208@end 1209