Vuo  2.0.0
VuoWindowTextInternal.m
Go to the documentation of this file.
1 
11 #import "VuoWindow.h"
12 #import "VuoApp.h"
13 
14 #include <dispatch/dispatch.h>
15 
16 #include "module.h"
17 
18 #ifdef VUO_COMPILER
20  "title" : "VuoWindowTextInternal",
21  "dependencies" : [
22  "AppKit.framework",
23  "VuoWindow"
24  ]
25  });
26 #endif
27 
28 
29 @implementation VuoWindowTextInternal
30 
31 @synthesize textView;
32 @synthesize scrollView;
33 @synthesize textFont;
34 @synthesize editCopyMenuItem;
35 @synthesize editSelectAllMenuItem;
36 
42 - (id)init
43 {
44  NSRect frame = NSMakeRect(0, 0, 400, 600);
45  NSUInteger styleMask = NSTitledWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask;
46  NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
47 
48  if (self = [super initWithContentRect:contentRect
49  styleMask:styleMask
50  backing:NSBackingStoreBuffered
51  defer:NO])
52  {
53  triggersEnabled = NO;
54  typedLine = NULL;
55  typedWord = NULL;
56  typedCharacter = NULL;
57 
58  self.delegate = self;
59  self.releasedWhenClosed = NO;
60 
61  char *title = VuoApp_getName();
62  [self setTitle:[NSString stringWithUTF8String:title]];
63  free(title);
64 
65  NSFont *_textFont = [NSFont fontWithName:@"Monaco" size:0];
66  self.textFont = _textFont;
67 
68  NSScrollView *_scrollView = [[NSScrollView alloc] initWithFrame:[[self contentView] frame]];
69  self.scrollView = _scrollView;
70  [_scrollView release];
71  [scrollView setHasVerticalScroller:YES];
72  [scrollView setAutohidesScrollers:NO];
73  [scrollView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
74  [self setContentView:scrollView];
75 
76  NSTextView *_textView = [[NSTextView alloc] initWithFrame:[[scrollView contentView] frame]];
77  self.textView = _textView;
78  [_textView release];
79  [textView setFont:textFont];
80  [textView setEditable:YES];
81  [textView setAllowsUndo:NO];
82  [textView setAutoresizingMask:NSViewWidthSizable];
83  [textView setAutomaticDashSubstitutionEnabled:NO];
84  [textView setAutomaticDataDetectionEnabled:NO];
85  [textView setAutomaticLinkDetectionEnabled:NO];
86  [textView setAutomaticQuoteSubstitutionEnabled:NO];
87  [textView setAutomaticSpellingCorrectionEnabled:NO];
88  [textView setAutomaticTextReplacementEnabled:NO];
89  [scrollView setDocumentView:textView];
90 
91  // Remove the "Cut" option from the context menu.
92  NSMenu *textViewContextMenu = [textView menu];
93  int cutItemIndex = [textViewContextMenu indexOfItemWithTitle: @"Cut"];
94  if (cutItemIndex >= 0)
95  [textViewContextMenu removeItemAtIndex: cutItemIndex];
96 
97  // Remove the "Paste" option from the context menu.
98  int pasteItemIndex = [textViewContextMenu indexOfItemWithTitle: @"Paste"];
99  if (pasteItemIndex >= 0)
100  [textViewContextMenu removeItemAtIndex: pasteItemIndex];
101 
102  textView.delegate = self;
103  }
104  return self;
105 }
106 
114 - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
115 {
116  // Allow inserting multi-keystroke (e.g. accented) characters.
117  NSUInteger markedTextLength = [aTextView markedRange].length;
118 
119  // Disallow inserting characters anywhere but at the end.
120  if (affectedCharRange.location + markedTextLength != [[textView string] length])
121  return NO;
122 
123  if (! triggersEnabled)
124  return YES;
125 
126  // Don't fire events for partial multi-keystroke characters.
127  if ([aTextView hasMarkedText] && markedTextLength == 0)
128  return YES;
129 
130  for (NSUInteger i = 0; i < [replacementString length]; ++i)
131  {
132  unichar charTyped = [replacementString characterAtIndex:i];
133 
134  // Fire event for character.
135  VuoText charTypedAsText = VuoText_make([[NSString stringWithCharacters:&charTyped length:1] UTF8String]);
136  typedCharacter(charTypedAsText);
137 
138  // If user typed whitespace...
139  if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charTyped])
140  {
141  // ... and if it ends a word, then fire event for word.
142  NSRange previousWhitespaceOrNewline = [[textView string] rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]
143  options:NSBackwardsSearch];
144  NSUInteger previousWordStart = (previousWhitespaceOrNewline.location != NSNotFound ?
145  previousWhitespaceOrNewline.location + 1 :
146  0);
147  if (previousWordStart < [[textView string] length])
148  {
149  NSString *wordTyped = [[textView string] substringFromIndex:previousWordStart];
150  VuoText wordTypedAsText = VuoText_make([wordTyped UTF8String]);
151  typedWord(wordTypedAsText);
152  }
153  }
154 
155  // If user typed a newline...
156  if ([[NSCharacterSet newlineCharacterSet] characterIsMember:charTyped])
157  {
158  // ... and if it ends a line, then fire event for line.
159  NSRange previousNewline = [[textView string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]
160  options:NSBackwardsSearch];
161  NSUInteger previousLineStart = (previousNewline.location != NSNotFound ?
162  previousNewline.location + 1 :
163  0);
164  if (previousLineStart < [[textView string] length])
165  {
166  NSString *lineTyped = [[textView string] substringFromIndex:previousLineStart];
167  VuoText lineTypedAsText = VuoText_make([lineTyped UTF8String]);
168  typedLine(lineTypedAsText);
169  }
170  }
171  }
172 
173  return YES;
174 }
175 
179 - (void)becomeMainWindow
180 {
181  [super becomeMainWindow];
182 
183  NSMenu *editMenu = [[[NSMenu alloc] initWithTitle:@"Edit"] autorelease];
184 
185  // "Edit > Copy"
186  self.editCopyMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyText) keyEquivalent:@"c"] autorelease];
187  [self.editCopyMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
188  [editMenu addItem:self.editCopyMenuItem];
189 
190  // "Edit > Select All"
191  self.editSelectAllMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAllText) keyEquivalent:@"a"] autorelease];
192  [self.editSelectAllMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
193  [editMenu addItem:self.editSelectAllMenuItem];
194 
195  NSMenuItem *editMenuItem = [[NSMenuItem new] autorelease];
196  [editMenuItem setSubmenu:editMenu];
197 
198  NSMutableArray *windowMenuItems = [NSMutableArray arrayWithCapacity:2];
199  [windowMenuItems addObject:editMenuItem];
200  oldMenu = [(NSMenu *)VuoApp_setMenuItems(windowMenuItems) retain];
201 }
202 
206 - (void)resignMainWindow
207 {
208  [super resignMainWindow];
209 
211  [oldMenu release];
212  oldMenu = nil;
213 }
214 
218 - (void)windowWillClose:(NSNotification *)notification
219 {
220  [super resignMainWindow];
221 
223  [oldMenu release];
224  oldMenu = nil;
225 }
226 
230 - (void)enableTriggersWithTypedLine:(void (*)(VuoText))_typedLine
231  typedWord:(void (*)(VuoText))_typedWord
232  typedCharacter:(void (*)(VuoText))_typedCharacter
233 {
234  triggersEnabled = YES;
235  typedLine = _typedLine;
236  typedWord = _typedWord;
237  typedCharacter = _typedCharacter;
238 }
239 
244 {
245  triggersEnabled = NO;
246  typedLine = NULL;
247  typedWord = NULL;
248  typedCharacter = NULL;
249 }
250 
254 - (void)appendLine:(const char *)text
255 {
256  // Autoscroll the window only if the viewport was already at the end.
257  BOOL autoscroll = abs(NSMaxY(textView.visibleRect) - NSMaxY(textView.bounds)) < 1;
258 
259  NSString *line = [[NSString stringWithUTF8String:text] stringByAppendingString:@"\n"];
260  NSDictionary *attributes = @{
261  NSFontAttributeName: textFont,
262  NSForegroundColorAttributeName: NSColor.textColor,
263  };
264  NSAttributedString *attributedLine = [[NSAttributedString alloc] initWithString:line attributes:attributes];
265  [[textView textStorage] appendAttributedString:attributedLine];
266  [attributedLine release];
267 
268  NSTextContainer *textContainer = [textView textContainer];
269  NSLayoutManager *layoutManager = [textView layoutManager];
270  [layoutManager ensureLayoutForTextContainer:textContainer];
271  NSRect frameToFitText = [layoutManager usedRectForTextContainer:textContainer];
272  [textView setFrame:frameToFitText];
273 
274  if (autoscroll)
275  [textView scrollRangeToVisible:NSMakeRange([[textView string] length], 0)];
276 }
277 
281 - (void)copyText
282 {
283  bool hasSelectedText = ([textView selectedRange].length > 0);
284  if (hasSelectedText)
285  [self.textView copy:self];
286 }
287 
291 - (void)selectAllText
292 {
293  [self.textView selectAll:self];
294 }
295 
300 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
301 {
302  SEL theAction = [anItem action];
303  if (theAction == @selector(copyText)) {
304 
305  bool hasSelectedText = ([textView selectedRange].length > 0);
306  return hasSelectedText;
307  }
308 
309  return [super validateUserInterfaceItem:anItem];
310 }
311 
312 @end