RTFD format : problems with writeRTFDToFile and readRTFDFromFile

Hello everybody!

I encounter a problem for a couple of days now. I want my application to do the same thing as TextEdit, to save on “bundle/package file” (normally a “.rtfd” package) but give the package a specific extension (“.atxt”)

But

gTextView’s writeRTFDToFile_atomically_((gFolderPOSIXPath & theID as string) & “.atxt”, true)

make a standard folder, with a “TXT.rtf” file inside. And of course

set err to gTextView’s readRTFDFromFile_((gFolderPOSIXPath & theID as string) & “.atxt”, true)

cannot read the file.

If I change to :

set theData to gTextView’s RTFDFromRange_({|location|:0, |length|:gTextView’s |string|()'s |length|()})
tell theData to writeToFile_atomically_(gFolderPOSIXPath & theID as string &“.rtdf”, true)

then readRTFDFromFile can find the file but give me some format instruction and a unreadable text.

I know the solution is at hand, but there is a mistake I don’t understand. Can somebody help me?

Notes: gTextView is my text view - it has been declared in IB as allowing images, etc. The document file is specified in the project as “package” with the role of “Editor”.

:confused:
No answers… this could have three explanations:
a) the question is too obscurely formulated;
b) the answer is so trivial that nobody wants to make me feel ridiculous;
c) nobody has been confronted to the same problem.

I’ll wait – So far, I’ve always found the correct answers in this forum! :slight_smile:

a) I think it would be good for you to clearly state what you are trying to do – in the series of posts that you have made over the last few days, the “problem” has seemed to evolve. What is the overall object of your program? Is it really important that your files have a different file extension that rtfd? It seems from one of your earlier posts, that this is a non-document based application – i’m not sure what you’re trying to do is possible outside of a document based approach, but it’s hard to tell since I don’t know what your ultimate objective is. For instance, how do you want to access any files you create? If you do that through an open panel, you can probably get by with a non-document based application, but if you want to be able to double click on a file icon and have it open up the app and populate the text view, then you probably do need a document based application.

b) On the contrary, I think this is a difficult problem, if you really need an extension other than rtfd. Unless someone knows some trick to fool the read/write rtfd methods into accepting files with other extensions, I think you would have to recreate what these methods do – I’m not really sure what all they are doing. The read method, readRTFDFromFile:, must “know” that it is reading a directory, then read in the contents of all the files in it, and pass them to the NSTextView object. I don’t know how the textView knows how to properly reconstruct the view by integrating any imbedded images for instance --maybe that’s all encoded in the TXT.rtf file, in which case maybe it wouldn’t be that hard.

c) Hard to tell, it’s not something I’ve tried to do.

Ric

Ok, Ric, I’ll try to clarify my concept.

The most simple is to think to an Internet navigator. You have a single window in which you load (and interpret) one page at a time. In this page you have links to other pages: you click on one link and another page is loaded and displayed (in the same window). My application uses the same concept, but instead of HTML pages stored on a distant site, you browse files contained in a local folder.

The first version of the application, in the early '90, used the resource fork of a unique file to store texts, images and sounds separately. The app loaded these resources (sharing the same ID) and displayed them – the text in a text view, the image at the top of the text, and played the sound.

Resource-based “objects” (records, in fact) were easy to store, retrieve and modify, but of course there were some limitations due to the architecture of the Resource Manager. My first idea was to reproduce narrowly this concept by using the object storage provided by Cocoa – one file becoming the equivalent of the specific ID for a group of resources.

Then I discovered (my ApplescriptObjC knowledge is only 2 month old) that a .rtfd package could store a text with embedded images and sound, in a much simpler way that having to load a image, creating a view programmatically and displaying this image with some specifications (alignment, size percentage and so on). It would be very easy (and more natural) for a user to assemble his page by drag and drop or copying/pasting, too. (There is not particular need to change the .rtfd extension, it was just to attribute it an Icon showing it was a package made by my application).

So that’s why my concept evolved: my basic plan was a strict adaptation of an outdated software – but the discovery that Interface Builder was far from a 2011 version of Resedit, and the ready-to-use editing capacities of Cocoa’s TextView made me change my plans to something more flexible (but keeping the original idea).

I hope this description will answer your pertinent questions. If not, I’m ready to tell you more.

Regards,
Bernard.

I’d like to point out that you posted on Friday, and you asked on Saturday why no one responds. In my case, I script at work, but rarely on the weekends for freelance. You might have more replies during a work week M-F than Saturday or Sunday.

Ok SuperMacGuy - thank you for the tip :slight_smile:

As you may have guessed, I’m a weekend and late evening programmer – the rest of the time is for my 100% job (which has nothing to do with programming). And of course I have to maintain my metabolism (sleeping for example)…

I’ll wait, the result will be worth it!

If you don’t need to change the extension from rtfd, then I’m not sure what your problem is now – the readRTFDFromFile and writeRTFDToFile:atomically: worked fine when I tried them. As far as the icon, I don’t know if it’s possible to override the default icon and replace it with your own.

The one question that I still have that you didn’t answer is how will the user access the files? By an open panel, by drag and drop, by double clicking? It seems there might be 2 times the user might access files – one to create them by dragging in image and sound files (into what? an already created text file?), and again if they want to access a file that they have already created. It seems that you have to know how these things will be done to decide if this will be done in a document based program or not.

Ric

Hello Ric, thanks for your answer! More precisions:

My “navigator” can create/delete blank “pages” (the user has the buttons |+| and |-| on the toolbar to do this). Once a blank “page” is created, it behaves like a TextEdit document: the user enters text, images and so on. That’s for the VIEW part.

The file system under these “pages”, the MODEL part, is of no concern for the user. From his point of view, he creates “pages” and navigates from one to the other by clicking buttons (|<<| |<| |>| |>>|) on the toolbar of a unique window. My app creates files and stores the contents of the “pages” into them (in fact, it saves the contents if a “page” has been modified) with writeRTFDToFile:atomically:. When a new “page” is accessed, my app replaces the content of the text view by the contents of the next file (with readRTFDFromFile).

As you say, everything should work perfectly – no doubt I made an obscure mistake somewhere. As my knowledge of XCode is improving, I shall find it one of these days… :confused:

For the time being, every passing day brings me its bunch of (good) surprises: an Applescript user can really dive into the Objective-C description and use almost everything of Cocoa… that’s fun! :slight_smile:

The same situation here. I want to add that there are also a very few ASOC programmers out there. So knowledge about ASOC is scarce if you compare it with one of world’s most popular programming language like C/C++. So don’t think that macscripter lacks in some sort of way but it is just a few fanatic forum users here that are willing to help people all over the world.

Hello again,

I started all again with a mini app to make the reading/writing thing work…

I coded
set result to textView’s writeRTFDToFile_atomically_(posixFile, true)

and got
-[NSScrollView writeRTFDToFile:atomically:]: unrecognized selector sent to instance 0x2006e18c0

The textView is a property – it is a NSScrollView;
The method writeRTFDToFile:atomically has been copied/pasted from the doc to avoid misspelling;
The posixFile is the posix version of the path (Macintosh HD/Developer/PROJECTS/newFile)
I tried to send 1 instead of “true”…

So what’s wrong?

The answer is right there in the error message — textView has to be an outlet to the text view not to the scroll view that it’s in. When you drag from your property name to the view, drag it near the top of the view and it should connect to the text view instead of the scroll view. If that doesn’t work, use the disclosure triangles next to your window to navigate down to where you can see the text view and connect it there.

Ric

Silly me. Silly, silly me.:stuck_out_tongue:

Thank you, I could have read this message one zillion times without noticing what was wrong.

OK, the writeToRTFD works. I have to append “.rtfd” to get a wrapper, else I get a normal folder. But I saved my first file! Again, thank you, this was really blocking me.

By the way, I discovered the NSMutableIndexSet class, which seems better suit my needs as NSMutableArray…

Regards,

I spent some time trying to find a solution for the problem in your original post – that is to write an rtfd file with a different extension and its own icon, so that it appears as a file rather than a folder. There might be more elegant ways to do this (maybe with NSFileWrapper?), but a simple trick worked just fine. I use the open and save panels in a different way than I normally would – with the save panel, instead of saving a file to the selected location, I use that path to create a new directory. That directory has the .atxt extension and an icon of its own (I went to project → edit active target and added a document type and the extension, atxt, changed the store type to binary, supplied an icon file and checked the package check box). I then use the file manager to set the current directory to that .atxt file package and then write the rtfd file to that location. Here is the code:

script DirectoriesAppDelegate
	property parent : class "NSObject"
	property textView : missing value --connected to the NSTextView
	property manager : missing value
	property theWindow : missing value --connected to the window which has a text view and a button
	
	on applicationWillFinishLaunching_(aNotification)
		set manager to current application's NSFileManager's defaultManager()
		set ans to the button returned of (display dialog "Open a Document or Make a New one?" buttons {"Open", "Make New"})
		if ans is "Open" then
			set op to current application's NSOpenPanel's openPanel()
			op's runModal()
			set openPath to op's URLs()'s objectAtIndex_(0)'s relativePath() --This gets the URL and changes it to a path
			manager's changeCurrentDirectoryPath_(openPath)
			textView's readRTFDFromFile_("theFile.rtfd") --Reads the rtfd file inside the .atxt file package
		end if
		theWindow's makeKeyAndOrderFront_(me)
	end applicationWillFinishLaunching_
	
	on writeToFile_(sender) -- connected to the button
		set sp to current application's NSSavePanel's savePanel()
		sp's setAllowedFileTypes_({"atxt"})
		sp's setNameFieldStringValue_("Untitled")
		sp's runModal()
		set savePath to sp's |URL|()'s relativePath()
		manager's createDirectoryAtPath_withIntermediateDirectories_attributes_error_(savePath, 0, missing value, missing value)
		manager's changeCurrentDirectoryPath_(savePath) --Changes the directory to the .atxt package we just picked in the save panel
		textView's writeRTFDToFile_atomically_("theFile.rtfd", true) --Puts the rtfd file inside the .atxt file package 
	end writeToFile_
	
end script

Ric

Ric, thank you so much for spending time to solve my problem – and another one, by the way: does really make this line what is was precisely looking for :

       manager's changeCurrentDirectoryPath_(openPath)

that is, to set a default directory? if yes, this code is gold for me, as my app reads from and writes to the same folder as long as the user doesn’t change for another. This would just spare lots of obscure code to translate HFS/POSIX paths, as well as hazardous concatenation to set the file names!

Meanwhile, I made readRTFDFromFile_ work as well. Long live to MacScripters!

Regards,

Bernard

Hello Ric,

For your info, I wrote this :

At application launch :

    set my gCardIconFile to (current application's NSBundle's mainBundle's resourcePath) as text & "/Header.png"
    set my gCardIconFileImage to current application's NSImage's alloc's initWithContentsOfFile_(gCardIconFile)

At file dumping :

    set result to my gTextView's writeRTFDToFile_atomically_(theFile, true)
    current application's NSWorkspace's sharedWorkspace's setIcon_forFile_options_(gCardIconFileImage,theFile,0)

This does not change the extension (maybe not a good idea.) but put the desired icon on the file wrapper.

Pretty heavy stuff (and not intellectually satisfying) to just change an Icon, but. it works. Later, maybe.

Regards,
Bernard

If you have your image in your resources folder of your project (or anywhere else in the “Groups & Files” pane for that matter), you can refer to it more simply with:

set gCardIconFileImage to current application's NSImage's imageNamed_("Header")

Ric

set gCardIconFileImage to current application's NSImage's imageNamed_("Header")

. the master’s touch! Indeed, the better you are, the less code you write.

Thank you for helping me to keep my code low-fat!

Regards,
Bernard

The code I posted above doesn’t “change” any extensions, it just creates a folder (disguised as a file package) with a proprietary extension. One thing good about having your own extension, is that your app can be the default opener of files of that type, whereas if you use the rtfd extension, then Text Edit will be the default opener. I don’t know if there is any way around that fact. Another thing about having a unique extension is that you can search on it and find all your files.

The one other feature I wanted to try to implement was the ability to open the file by double clicking on it. This seems to come for free with document based apps (I didn’t see any method in Shane’s People Docs project that causes this to happen any way). I found the method to do this with non-document based apps with application:openFile: (which is in the NSApplicationDelegate Protocol reference). Here is the updated code that allows for double-click opening – I added a flag to check whether the app was opened by double clicking (or right clicking on a file and choosing open) and moved the code in the applicationWillFinishLaunching method to an applicationDidFinishLaunching method since that gets called after application:openFile: whereas the “will” version gets called first.

property parent : class "NSObject"
	property textView : missing value --connected to the NSTextView
	property manager : missing value
	property theWindow : missing value --connected to the window which has a text view and a button
	property openedFromFile : false
	
	on applicationWillFinishLaunching_(aNotification)
		set manager to current application's NSFileManager's defaultManager()
	end applicationWillFinishLaunching_
	
	on application_openFile_(theApp, aFile)
		if aFile is not missing value then
			manager's changeCurrentDirectoryPath_(aFile)
			textView's readRTFDFromFile_("theFile.rtfd") --Reads the rtfd file inside the .atxt file package
			theWindow's makeKeyAndOrderFront_(me)
			set openedFromFile to true
		end if
	end application_openFile_
	
	on applicationDidFinishLaunching_(aNotification)
		if openedFromFile is false then
			set ans to the button returned of (display dialog "Open a Document or Make a New one?" buttons {"Open", "Make New"})
			if ans is "Open" then
				set op to current application's NSOpenPanel's openPanel()
				op's setAllowedFileTypes_({"atxt"})
				op's runModal()
				set openPath to op's URLs()'s objectAtIndex_(0)'s relativePath() --This gets the URL and changes it to a path
				manager's changeCurrentDirectoryPath_(openPath)
				textView's readRTFDFromFile_("theFile.rtfd") --Reads the rtfd file inside the .atxt file package
			end if
			theWindow's makeKeyAndOrderFront_(me)
		end if
	end applicationDidFinishLaunching_

The writeToFile method is the same as in my previous post.

Ric

I just couldn’t quit messing with this idea, so I worked on creating a document based project that did the same thing as my previous posts. Since the reading and writing methods in a document based app expect the data to be in the form of an NSData object, I had to look at other ways to get data into and out of a text view, and in doing so, realized that there is an easy way in a non-document based app to read and write RTFD data. So here is my latest take on this project --this program saves the data from the text view (with attachments) in a regular file (not a package or directory) and allows for your own custom file extension and icon, and this time with no tricks. I bound the Data value of the text view to my App Delegate.theData to populate the text view.

script RTFD_RWAppDelegate
	property parent : class "NSObject"
	property textView : missing value --connected to the NSTextView
	property theData : missing value -- the text view's Data is bound to App Delegate.theData
	property theWindow : missing value --connected to the window which has a text view and a button (unchecked visible at launch)
	property openedFromFile : false
	
	on application_openFile_(theApp, aFile)
		if aFile is not missing value then
			setTheData_(current application's NSData's dataWithContentsOfFile_(aFile))
			theWindow's makeKeyAndOrderFront_(me)
			set openedFromFile to true
		end if
	end application_openFile_
	
	on applicationDidFinishLaunching_(aNotification)
		if openedFromFile is false then
			set ans to the button returned of (display dialog "Open a Document or Make a New one?" buttons {"Open", "Make New"})
			if ans is "Open" then
				set op to current application's NSOpenPanel's openPanel()
				op's setAllowedFileTypes_({"tvd"})
				op's runModal()
				set openURL to op's URLs()'s objectAtIndex_(0)
				setTheData_(current application's NSData's dataWithContentsOfURL_(openURL))
			end if
			theWindow's makeKeyAndOrderFront_(me)
		end if
	end applicationDidFinishLaunching_
	
	on writeToFile_(sender) -- connected to the button
		set sp to current application's NSSavePanel's savePanel()
		sp's setAllowedFileTypes_({"tvd"})
		sp's setNameFieldStringValue_("Untitled")
		sp's runModal()
		set theData to textView's RTFDFromRange_({0, textView's textStorage()'s |length|()})
		theData's writeToURL_atomically_(sp's |URL|(), 1)
	end writeToFile_
	
	on applicationShouldTerminateAfterLastWindowClosed_(sender)
		return 1
	end applicationShouldTerminateAfterLastWindowClosed_
	
	on applicationShouldTerminate_(sender)
		return 1
	end applicationShouldTerminate_
	
end script

Ric

Hello Ric,

Thanks for your precise and abundant information – as I explore these framework structures, I realize that almost every problem has a solution in a method defined (and vaguely documented) somewhere…

The problem, when you have so big object collections, is to see the interactions between them, and to decide how to integrate them harmoniously into an application (welcome to OOP…).

The way Apple documents objects is curiously lacking of illustrations (it was quite the same in “Inside Macintosh”) – for visual elements, it’s a shame… but of course they must be some good books available :confused:

I keep an eye on your solution, as well as the serialization option. Both are applicable, I’ll test both and see (I have I have a fondness for serialization, I like this concept…)

Thanks again, read you soon.

Bernard