Recreating XCode run log behavior in an NSTextView

  • Hi

    I wrote a small folder scanning application to perform "deep watches"
    on changes to specified folders and their subfolders on a Windows
    server and wanted to present a simple "Run Log" like window so admins
    could see what the app is doing and spot any potential errors.

    I created a window, added an NSTextView, unchecked all options in the
    "attributes" panel except "selectable" and "uses find panel", created
    the following accessors to a mutable string property called "log" in
    my AppController class

    - (NSString *) log
    {
    return log;
    }

    - (void) setLog:(NSString *) inString
    {
    [log appendString: [inString stringByAppendingString: @"\n"]];
    }

    After that, I opened the bindings pane for the NSTextView, and set
    the following "value" bindings

    Bind to: AppController
    Controller key: <grayed out>
    Model key path: log

    Unchecked all checkboxes except

    Continuously updates value
    Raises for non applicable keys

    When I run the app, it runs fins and the Xcode run log shows that
    it's doing it's thing but nothing appears in the text view.

    When that didn't work, tried adding an NSObjectController, click-
    dragged a connection from the NSObjectController to the instantiated
    AppController and clicked "connect" on the "content field. Next, I
    defined a field called "log" in the controller, and bound the
    NSTextView value to that but that didn't work either.

    Anyone see what I'm doing wrong?

    Thanks for any help

    Ken
  • Hi Ken,
    Following code gives my application the same behavior as you need..

    -(void) printTextOnLoggingTextView:(NSString *) senderText
    {
    if (senderText)
    {
      NSRange range = NSMakeRange([[m_loggingTextView textStorage]
    length],0);
      [m_loggingTextView replaceCharactersInRange:range
    withString:senderText];
      [m_loggingTextView scrollRangeToVisible:range];
      [m_loggingTextView display];
    }
    }

    Hope it helps you...

    Ashish

    -----Original Message-----
    From: cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>
    [mailto:cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>] On
    Behalf Of Ken Tozier
    Sent: Tuesday, September 18, 2007 11:15 AM
    To: Cocoa Dev Dev
    Subject: Recreating XCode run log behavior in an NSTextView

    Hi

    I wrote a small folder scanning application to perform "deep watches"
    on changes to specified folders and their subfolders on a Windows
    server and wanted to present a simple "Run Log" like window so admins
    could see what the app is doing and spot any potential errors.

    I created a window, added an NSTextView, unchecked all options in the
    "attributes" panel except "selectable" and "uses find panel", created
    the following accessors to a mutable string property called "log" in
    my AppController class

    - (NSString *) log
    {
    return log;
    }

    - (void) setLog:(NSString *) inString
    {
    [log appendString: [inString stringByAppendingString: @"\n"]];
    }

    After that, I opened the bindings pane for the NSTextView, and set
    the following "value" bindings

    Bind to: AppController
    Controller key: <grayed out>
    Model key path: log

    Unchecked all checkboxes except

    Continuously updates value
    Raises for non applicable keys

    When I run the app, it runs fins and the Xcode run log shows that
    it's doing it's thing but nothing appears in the text view.

    When that didn't work, tried adding an NSObjectController, click-
    dragged a connection from the NSObjectController to the instantiated
    AppController and clicked "connect" on the "content field. Next, I
    defined a field called "log" in the controller, and bound the
    NSTextView value to that but that didn't work either.

    Anyone see what I'm doing wrong?

    Thanks for any help

    Ken
  • Hi Ashish

    How is that connected up in IB? Are you using bindings or IBOutlets?
    If bindings, could you flesh in the details a bit more on how the
    text view is bound to the model object?

    Thanks

    Ken

    On Sep 18, 2007, at 2:04 AM, Ashish Tiwari wrote:

    > Hi Ken,
    > Following code gives my application the same behavior as you need..
    >
    > -(void) printTextOnLoggingTextView:(NSString *) senderText
    > {
    > if (senderText)
    > {
    > NSRange range = NSMakeRange([[m_loggingTextView textStorage]
    > length],0);
    > [m_loggingTextView replaceCharactersInRange:range
    > withString:senderText];
    > [m_loggingTextView scrollRangeToVisible:range];
    > [m_loggingTextView display];
    > }
    > }
    >
    > Hope it helps you...
    >
    > Ashish
    >
    > -----Original Message-----
    > From: cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>
    > [mailto:cocoa-dev-bounces
    > +ashish_tiwari=<persistent.co.in...>] On
    > Behalf Of Ken Tozier
    > Sent: Tuesday, September 18, 2007 11:15 AM
    > To: Cocoa Dev Dev
    > Subject: Recreating XCode run log behavior in an NSTextView
    >
    > Hi
    >
    > I wrote a small folder scanning application to perform "deep watches"
    > on changes to specified folders and their subfolders on a Windows
    > server and wanted to present a simple "Run Log" like window so admins
    > could see what the app is doing and spot any potential errors.
    >
    > I created a window, added an NSTextView, unchecked all options in the
    > "attributes" panel except "selectable" and "uses find panel", created
    > the following accessors to a mutable string property called "log" in
    > my AppController class
    >
    > - (NSString *) log
    > {
    > return log;
    > }
    >
    > - (void) setLog:(NSString *) inString
    > {
    > [log appendString: [inString stringByAppendingString: @"\n"]];
    > }
    >
    > After that, I opened the bindings pane for the NSTextView, and set
    > the following "value" bindings
    >
    > Bind to: AppController
    > Controller key: <grayed out>
    > Model key path: log
    >
    > Unchecked all checkboxes except
    >
    > Continuously updates value
    > Raises for non applicable keys
    >
    > When I run the app, it runs fins and the Xcode run log shows that
    > it's doing it's thing but nothing appears in the text view.
    >
    > When that didn't work, tried adding an NSObjectController, click-
    > dragged a connection from the NSObjectController to the instantiated
    > AppController and clicked "connect" on the "content field. Next, I
    > defined a field called "log" in the controller, and bound the
    > NSTextView value to that but that didn't work either.
    >
    > Anyone see what I'm doing wrong?
    >
    > Thanks for any help
    >
    > Ken
    >
  • I used IBOutlets...

    As per my understanding Textview should be updated by main thread, are you
    doing so?

    -----Original Message-----
    From: cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>
    [mailto:cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>] On
    Behalf Of Ken Tozier
    Sent: Tuesday, September 18, 2007 11:39 AM
    To: Cocoa Dev Dev
    Subject: Re: Recreating XCode run log behavior in an NSTextView

    Hi Ashish

    How is that connected up in IB? Are you using bindings or IBOutlets?
    If bindings, could you flesh in the details a bit more on how the
    text view is bound to the model object?

    Thanks

    Ken

    On Sep 18, 2007, at 2:04 AM, Ashish Tiwari wrote:

    > Hi Ken,
    > Following code gives my application the same behavior as you need..
    >
    > -(void) printTextOnLoggingTextView:(NSString *) senderText
    > {
    > if (senderText)
    > {
    > NSRange range = NSMakeRange([[m_loggingTextView textStorage]
    > length],0);
    > [m_loggingTextView replaceCharactersInRange:range
    > withString:senderText];
    > [m_loggingTextView scrollRangeToVisible:range];
    > [m_loggingTextView display];
    > }
    > }
    >
    > Hope it helps you...
    >
    > Ashish
    >
    > -----Original Message-----
    > From: cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>
    > [mailto:cocoa-dev-bounces
    > +ashish_tiwari=<persistent.co.in...>] On
    > Behalf Of Ken Tozier
    > Sent: Tuesday, September 18, 2007 11:15 AM
    > To: Cocoa Dev Dev
    > Subject: Recreating XCode run log behavior in an NSTextView
    >
    > Hi
    >
    > I wrote a small folder scanning application to perform "deep watches"
    > on changes to specified folders and their subfolders on a Windows
    > server and wanted to present a simple "Run Log" like window so admins
    > could see what the app is doing and spot any potential errors.
    >
    > I created a window, added an NSTextView, unchecked all options in the
    > "attributes" panel except "selectable" and "uses find panel", created
    > the following accessors to a mutable string property called "log" in
    > my AppController class
    >
    > - (NSString *) log
    > {
    > return log;
    > }
    >
    > - (void) setLog:(NSString *) inString
    > {
    > [log appendString: [inString stringByAppendingString: @"\n"]];
    > }
    >
    > After that, I opened the bindings pane for the NSTextView, and set
    > the following "value" bindings
    >
    > Bind to: AppController
    > Controller key: <grayed out>
    > Model key path: log
    >
    > Unchecked all checkboxes except
    >
    > Continuously updates value
    > Raises for non applicable keys
    >
    > When I run the app, it runs fins and the Xcode run log shows that
    > it's doing it's thing but nothing appears in the text view.
    >
    > When that didn't work, tried adding an NSObjectController, click-
    > dragged a connection from the NSObjectController to the instantiated
    > AppController and clicked "connect" on the "content field. Next, I
    > defined a field called "log" in the controller, and bound the
    > NSTextView value to that but that didn't work either.
    >
    > Anyone see what I'm doing wrong?
    >
    > Thanks for any help
    >
    > Ken
    >
  • I added athe following into my AppController class

    IBOutlet NSTextView            *logView;

    Connected the logView to the NSTextView in IB, redefined my "setLog"
    method to your suggetsion

    - (void) setLog:(NSString *) senderText
    {
    NSLog(@"adding text: %@", senderText);
    NSRange range = NSMakeRange([[logView textStorage] length], 0);
    [logView replaceCharactersInRange: range withString: senderText];
    [logView scrollRangeToVisible: range];
    [logView display];
    }

    To make sure it was working on the main thread (the messages are sent
    from objects running in their own threads), I wrote the following
    wrapper class function (note: gSharedWatcher is a singleton of the
    AppController class)

    + (void) logMessage:(NSString *) inString
    {
    [gSharedWatcher performSelectorOnMainThread:@selector(setLog:)
    withObject: inString waitUntilDone: NO];
    }

    Still not working. Xcode shows the app is doing what it's supposed to
    but nothing appears in the text view.

    Any obvious errors jump out at you?

    On Sep 18, 2007, at 2:26 AM, Ashish Tiwari wrote:

    > I used IBOutlets...
    >
    > As per my understanding Textview should be updated by main thread,
    > are you
    > doing so?

    >
    >
    >
    > -----Original Message-----
    > From: cocoa-dev-bounces+ashish_tiwari=<persistent.co.in...>
    > [mailto:cocoa-dev-bounces
    > +ashish_tiwari=<persistent.co.in...>] On
    > Behalf Of Ken Tozier
    > Sent: Tuesday, September 18, 2007 11:39 AM
    > To: Cocoa Dev Dev
    > Subject: Re: Recreating XCode run log behavior in an NSTextView
    >
    > Hi Ashish
    >
    > How is that connected up in IB? Are you using bindings or IBOutlets?
    > If bindings, could you flesh in the details a bit more on how the
    > text view is bound to the model object?
    >
    > Thanks
    >
    > Ken
    >
    >
    > On Sep 18, 2007, at 2:04 AM, Ashish Tiwari wrote:
    >
    >> Hi Ken,
    >> Following code gives my application the same behavior as you need..
    >>
    >> -(void) printTextOnLoggingTextView:(NSString *) senderText
    >> {
    >> if (senderText)
    >> {
    >> NSRange range = NSMakeRange([[m_loggingTextView textStorage]
    >> length],0);
    >> [m_loggingTextView replaceCharactersInRange:range
    >> withString:senderText];
    >> [m_loggingTextView scrollRangeToVisible:range];
    >> [m_loggingTextView display];
    >> }
    >> }
    >>
    >> Hope it helps you...
    >>
    >> Ashish
    >>
    >> -----Original Message-----
    >> From: cocoa-dev-bounces
    >> +ashish_tiwari=<persistent.co.in...>
    >> [mailto:cocoa-dev-bounces
    >> +ashish_tiwari=<persistent.co.in...>] On
    >> Behalf Of Ken Tozier
    >> Sent: Tuesday, September 18, 2007 11:15 AM
    >> To: Cocoa Dev Dev
    >> Subject: Recreating XCode run log behavior in an NSTextView
    >>
    >> Hi
    >>
    >> I wrote a small folder scanning application to perform "deep watches"
    >> on changes to specified folders and their subfolders on a Windows
    >> server and wanted to present a simple "Run Log" like window so admins
    >> could see what the app is doing and spot any potential errors.
    >>
    >> I created a window, added an NSTextView, unchecked all options in the
    >> "attributes" panel except "selectable" and "uses find panel", created
    >> the following accessors to a mutable string property called "log" in
    >> my AppController class
    >>
    >> - (NSString *) log
    >> {
    >> return log;
    >> }
    >>
    >> - (void) setLog:(NSString *) inString
    >> {
    >> [log appendString: [inString stringByAppendingString: @"\n"]];
    >> }
    >>
    >> After that, I opened the bindings pane for the NSTextView, and set
    >> the following "value" bindings
    >>
    >> Bind to: AppController
    >> Controller key: <grayed out>
    >> Model key path: log
    >>
    >> Unchecked all checkboxes except
    >>
    >> Continuously updates value
    >> Raises for non applicable keys
    >>
    >> When I run the app, it runs fins and the Xcode run log shows that
    >> it's doing it's thing but nothing appears in the text view.
    >>
    >> When that didn't work, tried adding an NSObjectController, click-
    >> dragged a connection from the NSObjectController to the instantiated
    >> AppController and clicked "connect" on the "content field. Next, I
    >> defined a field called "log" in the controller, and bound the
    >> NSTextView value to that but that didn't work either.
    >>
    >> Anyone see what I'm doing wrong?
    >>
    >> Thanks for any help
    >>
    >> Ken
    >>

    >
  • Bindings use KVO and you're not notifying the observer of changes.
    Try changing your setter to

    - (void) setLog:(NSString *) inString
    {
    [self willChangeValueForKey:@"log"];
    [log appendString: [inString stringByAppendingString: @"\n"]];
    [self didChangeValueForKey:@"log"];
    }

    <http://developer.apple.com/documentation/Cocoa/Conceptual/
    KeyValueObserving/index.html#//apple_ref/doc/uid/10000177i
    >

    pg

    On Sep 17, 2007, at 10:45 PM, Ken Tozier wrote:

    > Hi
    >
    > I wrote a small folder scanning application to perform "deep
    > watches" on changes to specified folders and their subfolders on a
    > Windows server and wanted to present a simple "Run Log" like window
    > so admins could see what the app is doing and spot any potential
    > errors.
    >
    > I created a window, added an NSTextView, unchecked all options in
    > the "attributes" panel except "selectable" and "uses find panel",
    > created the following accessors to a mutable string property called
    > "log" in my AppController class
    >
    > - (NSString *) log
    > {
    > return log;
    > }
    >
    > - (void) setLog:(NSString *) inString
    > {
    > [log appendString: [inString stringByAppendingString: @"\n"]];
    > }
    >
    > After that, I opened the bindings pane for the NSTextView, and set
    > the following "value" bindings
    >
    > Bind to: AppController
    > Controller key: <grayed out>
    > Model key path: log
    >
    > Unchecked all checkboxes except
    >
    > Continuously updates value
    > Raises for non applicable keys
    >
    > When I run the app, it runs fins and the Xcode run log shows that
    > it's doing it's thing but nothing appears in the text view.
    >
    > When that didn't work, tried adding an NSObjectController, click-
    > dragged a connection from the NSObjectController to the
    > instantiated AppController and clicked "connect" on the "content
    > field. Next, I defined a field called "log" in the controller, and
    > bound the NSTextView value to that but that didn't work either.
    >
    > Anyone see what I'm doing wrong?
    >
    > Thanks for any help
    >
    > Ken
  • On Sep 18, 2007, at 7:54 AM, Paul Goracke wrote:

    > Bindings use KVO and you're not notifying the observer of changes.
    > Try changing your setter to
    >
    > - (void) setLog:(NSString *) inString
    > {
    > [self willChangeValueForKey:@"log"];
    > [log appendString: [inString stringByAppendingString: @"\n"]];
    > [self didChangeValueForKey:@"log"];
    > }

    That is only need if he turned off the automatic aspects of KVO since
    he is in a method named as expected for the key he is observing.

    <http://developer.apple.com/documentation/Cocoa/Conceptual/
    KeyValueObserving/Concepts/AutoVsManual.html#//apple_ref/doc/uid/
    20001844-178255
    >

    -Shawn
  • Thanks for the link Paul

    I added the willChange/didChange lines, and retried binding directoy
    to the "log" method of my Appcontroller and when that failed, adding
    an NSObjectController control-dragging from the controller to the
    AppController, added a "log" key under attributes set the Object
    class name of the object controller to NSDictionary (also tried
    NSMUtableDictionary and AppController) and bound the text view to the
    "log" key in the controller. No luck.

    What am I missing here? This seems like it should be a piece of cake.
    The entire UI consists of one window with an one NSTextView that I
    want to display the "log" mutable string AppController property.

    On Sep 18, 2007, at 10:54 AM, Paul Goracke wrote:

    > Bindings use KVO and you're not notifying the observer of changes.
    > Try changing your setter to
    >
    > - (void) setLog:(NSString *) inString
    > {
    > [self willChangeValueForKey:@"log"];
    > [log appendString: [inString stringByAppendingString: @"\n"]];
    > [self didChangeValueForKey:@"log"];
    > }
    >
    > <http://developer.apple.com/documentation/Cocoa/Conceptual/
    > KeyValueObserving/index.html#//apple_ref/doc/uid/10000177i>
    >
    > pg
    >
    >
    > On Sep 17, 2007, at 10:45 PM, Ken Tozier wrote:
    >
    >> Hi
    >>
    >> I wrote a small folder scanning application to perform "deep
    >> watches" on changes to specified folders and their subfolders on a
    >> Windows server and wanted to present a simple "Run Log" like
    >> window so admins could see what the app is doing and spot any
    >> potential errors.
    >>
    >> I created a window, added an NSTextView, unchecked all options in
    >> the "attributes" panel except "selectable" and "uses find panel",
    >> created the following accessors to a mutable string property
    >> called "log" in my AppController class
    >>
    >> - (NSString *) log
    >> {
    >> return log;
    >> }
    >>
    >> - (void) setLog:(NSString *) inString
    >> {
    >> [log appendString: [inString stringByAppendingString: @"\n"]];
    >> }
    >>
    >> After that, I opened the bindings pane for the NSTextView, and set
    >> the following "value" bindings
    >>
    >> Bind to: AppController
    >> Controller key: <grayed out>
    >> Model key path: log
    >>
    >> Unchecked all checkboxes except
    >>
    >> Continuously updates value
    >> Raises for non applicable keys
    >>
    >> When I run the app, it runs fins and the Xcode run log shows that
    >> it's doing it's thing but nothing appears in the text view.
    >>
    >> When that didn't work, tried adding an NSObjectController, click-
    >> dragged a connection from the NSObjectController to the
    >> instantiated AppController and clicked "connect" on the "content
    >> field. Next, I defined a field called "log" in the controller, and
    >> bound the NSTextView value to that but that didn't work either.
    >>
    >> Anyone see what I'm doing wrong?
    >>
    >> Thanks for any help
    >>
    >> Ken
    >
  • Ken Tozier wrote:

    > Thanks for the link Paul
    >
    > I added the willChange/didChange lines, and retried binding
    > directoy to the "log" method of my Appcontroller and when that
    > failed, adding an NSObjectController control-dragging from the
    > controller to the AppController, added a "log" key under attributes
    > set the Object class name of the object controller to NSDictionary
    > (also tried NSMUtableDictionary and AppController) and bound the
    > text view to the "log" key in the controller. No luck.
    >
    > What am I missing here? This seems like it should be a piece of
    > cake. The entire UI consists of one window with an one NSTextView
    > that I want to display the "log" mutable string AppController
    > property.

    My suggestion was mostly of the "is it plugged in?" variety.
    Experimenting w/ your code now, I too see it not updating. On the
    other hand, removing your setLog: method and adding

    - (void) appendToLog: (NSString *) inString {
    [self setValue:[NSString stringWithFormat:@"%@%@\n", log, inString]
    forKey:@"log"];
    }

    seems to work. From this, I would conclude that the standard KVO (at
    least for NSTextView value) responds to changes _of_ the object, not
    necessarily changes _to_ the object. I think this conclusion is
    supported by the observation that disabling automatic notifications
    for 'log' and doing willChangeValueForKey, append, and
    didChangeValueForKey also shows no update. This repeated copying of
    strings is probably not optimal in your case--you may want to look at
    using NSAttributedString and see if the attributedString binding is
    better supported.

    pg
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today