Vuo  2.3.2
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 @synthesize editClearMenuItem;
37 
43 - (id)init
44 {
45  NSRect frame = NSMakeRect(0, 0, 400, 600);
46  NSUInteger styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
47  NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
48 
49  if (self = [super initWithContentRect:contentRect
50  styleMask:styleMask
51  backing:NSBackingStoreBuffered
52  defer:NO])
53  {
54  triggersEnabled = NO;
55  typedLine = NULL;
56  typedWord = NULL;
57  typedCharacter = NULL;
58 
59  self.delegate = self;
60  self.releasedWhenClosed = NO;
61 
62  char *title = VuoApp_getName();
63  [self setTitle:[NSString stringWithUTF8String:title]];
64  free(title);
65 
66  NSFont *_textFont = [NSFont fontWithName:@"Monaco" size:0];
67  self.textFont = _textFont;
68 
69  NSScrollView *_scrollView = [[NSScrollView alloc] initWithFrame:[[self contentView] frame]];
70  self.scrollView = _scrollView;
71  [_scrollView release];
72  [scrollView setHasVerticalScroller:YES];
73  [scrollView setAutohidesScrollers:NO];
74  [scrollView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
75  [self setContentView:scrollView];
76 
77  NSTextView *_textView = [[NSTextView alloc] initWithFrame:[[scrollView contentView] frame]];
78  self.textView = _textView;
79  [_textView release];
80  [textView setFont:textFont];
81  [textView setEditable:YES];
82  [textView setAllowsUndo:NO];
83  [textView setAutoresizingMask:NSViewWidthSizable];
84  [textView setAutomaticDashSubstitutionEnabled:NO];
85  [textView setAutomaticDataDetectionEnabled:NO];
86  [textView setAutomaticLinkDetectionEnabled:NO];
87  [textView setAutomaticQuoteSubstitutionEnabled:NO];
88  [textView setAutomaticSpellingCorrectionEnabled:NO];
89  [textView setAutomaticTextReplacementEnabled:NO];
90  [scrollView setDocumentView:textView];
91 
92  // Remove the "Cut" option from the context menu.
93  NSMenu *textViewContextMenu = [textView menu];
94  int cutItemIndex = [textViewContextMenu indexOfItemWithTitle: @"Cut"];
95  if (cutItemIndex >= 0)
96  [textViewContextMenu removeItemAtIndex: cutItemIndex];
97 
98  // Remove the "Paste" option from the context menu.
99  int pasteItemIndex = [textViewContextMenu indexOfItemWithTitle: @"Paste"];
100  if (pasteItemIndex >= 0)
101  [textViewContextMenu removeItemAtIndex: pasteItemIndex];
102 
103  textView.delegate = self;
104  }
105  return self;
106 }
107 
115 - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
116 {
117  // Allow inserting multi-keystroke (e.g. accented) characters.
118  NSUInteger markedTextLength = [aTextView markedRange].length;
119 
120  // Disallow inserting characters anywhere but at the end.
121  if (affectedCharRange.location + markedTextLength != [[textView string] length])
122  return NO;
123 
124  if (! triggersEnabled)
125  return YES;
126 
127  // Don't fire events for partial multi-keystroke characters.
128  if ([aTextView hasMarkedText] && markedTextLength == 0)
129  return YES;
130 
131  for (NSUInteger i = 0; i < [replacementString length]; ++i)
132  {
133  unichar charTyped = [replacementString characterAtIndex:i];
134 
135  // Fire event for character.
136  VuoText charTypedAsText = VuoText_make([[NSString stringWithCharacters:&charTyped length:1] UTF8String]);
137  typedCharacter(charTypedAsText);
138 
139  // If user typed whitespace...
140  if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charTyped])
141  {
142  // ... and if it ends a word, then fire event for word.
143  NSRange previousWhitespaceOrNewline = [[textView string] rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]
144  options:NSBackwardsSearch];
145  NSUInteger previousWordStart = (previousWhitespaceOrNewline.location != NSNotFound ?
146  previousWhitespaceOrNewline.location + 1 :
147  0);
148  if (previousWordStart < [[textView string] length])
149  {
150  NSString *wordTyped = [[textView string] substringFromIndex:previousWordStart];
151  VuoText wordTypedAsText = VuoText_make([wordTyped UTF8String]);
152  typedWord(wordTypedAsText);
153  }
154  }
155 
156  // If user typed a newline...
157  if ([[NSCharacterSet newlineCharacterSet] characterIsMember:charTyped])
158  {
159  // ... and if it ends a line, then fire event for line.
160  NSRange previousNewline = [[textView string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]
161  options:NSBackwardsSearch];
162  NSUInteger previousLineStart = (previousNewline.location != NSNotFound ?
163  previousNewline.location + 1 :
164  0);
165  if (previousLineStart < [[textView string] length])
166  {
167  NSString *lineTyped = [[textView string] substringFromIndex:previousLineStart];
168  VuoText lineTypedAsText = VuoText_make([lineTyped UTF8String]);
169  typedLine(lineTypedAsText);
170  }
171  }
172  }
173 
174  return YES;
175 }
176 
180 - (void)becomeMainWindow
181 {
182  [super becomeMainWindow];
183 
184  NSMenu *editMenu = [[[NSMenu alloc] initWithTitle:@"Edit"] autorelease];
185 
186  // "Edit > Copy"
187  self.editCopyMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyText) keyEquivalent:@"c"] autorelease];
188  [self.editCopyMenuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
189  [editMenu addItem:self.editCopyMenuItem];
190 
191  // "Edit > Select All"
192  self.editSelectAllMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAllText) keyEquivalent:@"a"] autorelease];
193  [self.editSelectAllMenuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
194  [editMenu addItem:self.editSelectAllMenuItem];
195 
196  // "Edit > Clear"
197  self.editClearMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Clear" action:@selector(clear) keyEquivalent:@"k"] autorelease];
198  [self.editClearMenuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
199  [editMenu addItem:self.editClearMenuItem];
200 
201  NSMenuItem *editMenuItem = [[NSMenuItem new] autorelease];
202  [editMenuItem setSubmenu:editMenu];
203 
204  NSMutableArray *windowMenuItems = [NSMutableArray arrayWithCapacity:2];
205  [windowMenuItems addObject:editMenuItem];
206  oldMenu = [(NSMenu *)VuoApp_setMenuItems(windowMenuItems) retain];
207 }
208 
212 - (void)resignMainWindow
213 {
214  [super resignMainWindow];
215 
217  [oldMenu release];
218  oldMenu = nil;
219 }
220 
224 - (void)windowWillClose:(NSNotification *)notification
225 {
226  [super resignMainWindow];
227 
229  [oldMenu release];
230  oldMenu = nil;
231 }
232 
236 - (void)enableTriggersWithTypedLine:(void (*)(VuoText))_typedLine
237  typedWord:(void (*)(VuoText))_typedWord
238  typedCharacter:(void (*)(VuoText))_typedCharacter
239 {
240  triggersEnabled = YES;
241  typedLine = _typedLine;
242  typedWord = _typedWord;
243  typedCharacter = _typedCharacter;
244 }
245 
249 - (void)disableTriggers
250 {
251  triggersEnabled = NO;
252  typedLine = NULL;
253  typedWord = NULL;
254  typedCharacter = NULL;
255 }
256 
260 - (void)appendLine:(const char *)text
261 {
262  // Autoscroll the window only if the viewport was already at the end.
263  BOOL autoscroll = fabs(NSMaxY(textView.visibleRect) - NSMaxY(textView.bounds)) < 1;
264 
265  NSString *line = [[NSString stringWithUTF8String:text] stringByAppendingString:@"\n"];
266  if (!line)
267  {
268  VUserLog("Warning: Attempting to write non-UTF-8 text to a console window. Ignoring.");
269  return;
270  }
271 
272  NSDictionary *attributes = @{
273  NSFontAttributeName: textFont,
274  NSForegroundColorAttributeName: NSColor.textColor,
275  };
276  NSAttributedString *attributedLine = [[NSAttributedString alloc] initWithString:line attributes:attributes];
277  [[textView textStorage] appendAttributedString:attributedLine];
278  [attributedLine release];
279 
280  NSTextContainer *textContainer = [textView textContainer];
281  NSLayoutManager *layoutManager = [textView layoutManager];
282  [layoutManager ensureLayoutForTextContainer:textContainer];
283  NSRect frameToFitText = [layoutManager usedRectForTextContainer:textContainer];
284  [textView setFrame:frameToFitText];
285 
286  if (autoscroll)
287  [textView scrollRangeToVisible:NSMakeRange([[textView string] length], 0)];
288 }
289 
293 - (void)clear
294 {
295  [textView.textStorage deleteCharactersInRange:NSMakeRange(0, textView.textStorage.length)];
296 }
297 
301 - (void)copyText
302 {
303  bool hasSelectedText = ([textView selectedRange].length > 0);
304  if (hasSelectedText)
305  [self.textView copy:self];
306 }
307 
311 - (void)selectAllText
312 {
313  [self.textView selectAll:self];
314 }
315 
320 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
321 {
322  SEL theAction = [anItem action];
323  if (theAction == @selector(copyText)) {
324 
325  bool hasSelectedText = ([textView selectedRange].length > 0);
326  return hasSelectedText;
327  }
328 
329  return [super validateUserInterfaceItem:anItem];
330 }
331 
332 @end