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