// 14 april 2016 #import "uipriv_darwin.h" #import "attrstr.h" @interface uiprivFontButton : NSButton { uiFontButton *libui_b; NSFont *libui_font; } - (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b; - (void)updateFontButtonLabel; - (IBAction)fontButtonClicked:(id)sender; - (void)activateFontButton; - (void)deactivateFontButton:(BOOL)activatingAnother; - (void)deactivateOnClose:(NSNotification *)note; - (void)getfontdesc:(uiFontDescriptor *)uidesc; @end // only one may be active at one time static uiprivFontButton *activeFontButton = nil; struct uiFontButton { uiDarwinControl c; uiprivFontButton *button; void (*onChanged)(uiFontButton *, void *); void *onChangedData; }; @implementation uiprivFontButton - (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b { self = [super initWithFrame:frame]; if (self) { self->libui_b = b; // imitate a NSColorWell in appearance [self setButtonType:NSPushOnPushOffButton]; [self setBordered:YES]; [self setBezelStyle:NSShadowlessSquareBezelStyle]; // default font values according to the CTFontDescriptor reference // this is autoreleased (thanks swillits in irc.freenode.net/#macdev) self->libui_font = [[NSFont fontWithName:@"Helvetica" size:12.0] retain]; [self updateFontButtonLabel]; // for when clicked [self setTarget:self]; [self setAction:@selector(fontButtonClicked:)]; } return self; } - (void)dealloc { // clean up notifications if (activeFontButton == self) [self deactivateFontButton:NO]; [self->libui_font release]; [super dealloc]; } - (void)updateFontButtonLabel { NSString *title; title = [NSString stringWithFormat:@"%@ %g", [self->libui_font displayName], [self->libui_font pointSize]]; [self setTitle:title]; } - (IBAction)fontButtonClicked:(id)sender { if ([self state] == NSOnState) [self activateFontButton]; else [self deactivateFontButton:NO]; } - (void)activateFontButton { NSFontManager *sfm; sfm = [NSFontManager sharedFontManager]; if (activeFontButton != nil) [activeFontButton deactivateFontButton:YES]; [sfm setTarget:self]; [sfm setSelectedFont:self->libui_font isMultiple:NO]; [sfm orderFrontFontPanel:self]; activeFontButton = self; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deactivateOnClose:) name:NSWindowWillCloseNotification object:[NSFontPanel sharedFontPanel]]; [self setState:NSOnState]; } - (void)deactivateFontButton:(BOOL)activatingAnother { NSFontManager *sfm; sfm = [NSFontManager sharedFontManager]; [sfm setTarget:nil]; if (!activatingAnother) [[NSFontPanel sharedFontPanel] orderOut:self]; activeFontButton = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:[NSFontPanel sharedFontPanel]]; [self setState:NSOffState]; } - (void)deactivateOnClose:(NSNotification *)note { [self deactivateFontButton:NO]; } - (void)changeFont:(id)sender { NSFontManager *fm; NSFont *old; uiFontButton *b = self->libui_b; fm = (NSFontManager *) sender; old = self->libui_font; self->libui_font = [sender convertFont:self->libui_font]; // do this even if it returns the same; we don't own anything that isn't from a new or alloc/init [self->libui_font retain]; // do this second just in case [old release]; [self updateFontButtonLabel]; (*(b->onChanged))(b, b->onChangedData); } - (NSUInteger)validModesForFontPanel:(NSFontPanel *)panel { return NSFontPanelFaceModeMask | NSFontPanelSizeModeMask | NSFontPanelCollectionModeMask; } - (void)getfontdesc:(uiFontDescriptor *)uidesc { CTFontRef ctfont; CTFontDescriptorRef ctdesc; ctfont = (CTFontRef) (self->libui_font); ctdesc = CTFontCopyFontDescriptor(ctfont); uiprivFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); CFRelease(ctdesc); uidesc->Size = CTFontGetSize(ctfont); } @end uiDarwinControlAllDefaults(uiFontButton, button) // we do not want font change events to be sent to any controls other than the font buttons // see main.m for more details BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to) { if (sel != @selector(changeFont:)) return NO; return ![to isKindOfClass:[uiprivFontButton class]]; } // we do not want NSFontPanelValidation messages to be sent to any controls other than the font buttons when a font button is active // see main.m for more details BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { if (activeFontButton == nil) return NO; if (sel != @selector(validModesForFontPanel:)) return NO; *override = activeFontButton; return YES; } // we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that // unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves @interface uiprivNonModalFontPanel : NSFontPanel @end @implementation uiprivNonModalFontPanel - (BOOL)worksWhenModal { return NO; } @end void uiprivSetupFontPanel(void) { [NSFontManager setFontPanelFactory:[uiprivNonModalFontPanel class]]; } static void defaultOnChanged(uiFontButton *b, void *data) { // do nothing } void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { [b->button getfontdesc:desc]; } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) { b->onChanged = f; b->onChangedData = data; } uiFontButton *uiNewFontButton(void) { uiFontButton *b; uiDarwinNewControl(uiFontButton, b); b->button = [[uiprivFontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b]; uiDarwinSetControlFont(b->button, NSRegularControlSize); uiFontButtonOnChanged(b, defaultOnChanged, NULL); return b; } void uiFreeFontButtonFont(uiFontDescriptor *desc) { // TODO ensure this is synchronized with fontmatch.m uiFreeText((char *) (desc->Family)); }