Editable images into a text view

Sounds like a path issue. I haven’t been following, but do you need setPreferredFilename_ in there?

Well, I just gave it a name rather than a path, but I’m never writing this thing out anywhere. Does it really need a path? And, if so, do I need to write it out somewhere? I guess I don’t really know how the attributed string gets the data – does it not get it directly from the file wrapper if that’s in memory, which I suppose it is at this point?

Ric

As I said, I haven’t been following, and it’s hard to test without a duplicate set-up. What happens if you comment that line out?

I get <NSTextAttachment: 0x2005394e0> “(null)” if I log newAttachment (instead of the name there), and I still get the icon in the text view.

ric

Shane,

I solved it, thanks to your suggestion – it does have to do with the preferred file name. Changing it from smallImage to smallImage.jpg fixed the problem. I’m not sure why, but it worked. I guess I’m still not really sure how file wrappers work. Do you know if the data for the image is actually stored in the attributed string, or does the string just store a reference to this data specified by the file wrapper location?

Ric

Everything’s a reference :slight_smile:

But whether in this case the reference is a pointer to a particular object, or something more indirect, I really don’t know. My guess is the former.

The problem may be solved, but here the image:

  1. totally disappear because of the tv’s replaceCharactersInRange_withString_({indx, 1}, “”)
  2. is replaced, at its very best, by a file icon, even with “.jpg” extension
  3. don’t change at all.

After some trials I had to force XCode to quit.

Why, why am I always looking for things which seem evident to me and which are so hard to implement? :rolleyes:

The problem may be solved, but here the image:

By “here” do you mean you added it to your project and it didn’t work?

Yeah, that’s what that’s supposed to do, but I took it out of my latest version, because it gets replaced by the smaller one any way (when that replacement is successful). I’m still trying to figure out how these attachments work exactly, especially how they will behave after saving and reopening – I don’t know where the actual picture data will reside.

Ric

I don’t see any replacement line in your last post. Is the last version to be combined with an older post?

What about a virtual file?

The current code is:

on textView_clickedOnCell_inRect_atIndex_(tv, clickedCell, rect, indx)
		set picData to clickedCell's |attachment|()'s fileWrapper()'s regularFileContents()
		set picImage to current application's NSImage's alloc()'s initWithData_(picData)
		set newImage to current application's NSImage's alloc()'s initWithSize_({48, 48})
		set dict to current application's NSDictionary's dictionaryWithObject_forKey_(1.0, current application's NSImageCompressionFactor)
		set newRect to {{0, 0}, {48, 48}}
		set oldRect to {{0, 0}, {0, 0}}
		newImage's lockFocus()
		picImage's drawInRect_fromRect_operation_fraction_(newRect, oldRect, current application's NSCompositeCopy, 1)
		set bm to current application's NSBitmapImageRep's alloc()'s initWithFocusedViewRect_(newRect)
		set theData to bm's representationUsingType_properties_(current application's NSJPEGFileType, dict)
		newImage's unlockFocus()
		set newFileWrapper to current application's NSFileWrapper's alloc()'s initRegularFileWithContents_(theData)
		newFileWrapper's setPreferredFilename_("smallImage.jpg")
		set newAttachment to current application's NSTextAttachment's alloc()'s initWithFileWrapper_(newFileWrapper)
		set attrib to current application's NSAttributedString's attributedStringWithAttachment_(newAttachment)
		tv's textStorage()'s replaceCharactersInRange_withAttributedString_({indx, 1}, attrib)
	end textView_clickedOnCell_inRect_atIndex_

Ric

Visually it works – but the resized picture is actually saved if and only if the text is modified. Strange.

And now imagine that after a double-click on the image, I want to present a dialog as sheet to set the new percentage - there is a lot of information gathered in the textView_doubleClickedOnCell_inRect_atIndex_ method, which will show the dialog. How to send necessary information to the handler which will remove the sheet and execute the action? Shall I use the contextInfo, and how?

This is not the first time I have to share useful info in two handlers (the one that show the sheet and the other that removes it). I don’t have to fight the framework here, it is fighting itself very well. These two-phase alerts, dialogs and windows are simply incomprehensible. And I wish to avoid to use globals or properties between two routines separated by one blank line.

I’m not sure why that should be, I’m sure there would be a work-around for it.

I’m doing it like this now. I have a window that pops up when you click on the image which has a label for the dragged-in image’s size, a text field to enter the compression factor, a 2 button radio matrix (“Preserve Aspect Ratio” and “Adjust Dimensions Independently”), two sliders for width and height and a “Finish” button to dismiss the window. With this code, I can adjust the picture’s size in real time with the sliders (it’s a little slow, but not too bad).

script TextViewImageResizeAppDelegate
	property parent : class "NSObject"
	
	--IBOutlets-------------
	property tv : missing value -- the text view
	property wSlider : missing value -- the width slider
	property hSlider : missing value -- the height slider
	property resizePrefs : missing value -- the resize window
	
	--Properties needed for passing between methods
	property picImage : missing value
	property indx : missing value
	property dict : missing value
	property aspectRatio : missing value
	
	--Bound Properties------------
	property wSliderValue : missing value -- the value of the width slider is bound to this
	property hSliderValue : missing value -- the value of the height slider is bound to this
	property pw : missing value -- width of the dragged in picture.  Width slider max value bound to this
	property ph : missing value -- height of the dragged in picture.  Height slider max value bound to this
	property retainAspectRatio : 1 -- The radio matrix's selected tag is bound to this as well as the height slider enabled
	property displayPicSize : missing value -- the value of a label is bound to this
	property cf : 1.0 -- compression factor.  This is bound to the value of a text field
	
	on applicationWillFinishLaunching_(aNotification)
	end applicationWillFinishLaunching_
	
	on textView_clickedOnCell_inRect_atIndex_(tv, clickedCell, rect, indx)
		set picData to clickedCell's |attachment|()'s fileWrapper()'s regularFileContents()
		set picImage to current application's NSImage's alloc()'s initWithData_(picData)
		setPw_(picImage's |size|'s sizeValue()'s |width| as real)
		setPh_(picImage's |size|'s sizeValue()'s height as real)
		set aspectRatio to (ph as real) / pw as real
		setDisplayPicSize_(current application's NSString's stringWithFormat_("Width:%@   Height:%@", pw, ph))
		setWSliderValue_(picImage's |size|'s sizeValue()'s |width| as real)
		setHSliderValue_(picImage's |size|'s sizeValue()'s height as real)
		resizePrefs's setAlphaValue_(0)
		resizePrefs's makeKeyAndOrderFront_(me)
		resizePrefs's animator()'s setAlphaValue_(0.8)
		set dict to current application's NSMutableDictionary's dictionaryWithObject_forKey_(cf, current application's NSImageCompressionFactor)
	end textView_clickedOnCell_inRect_atIndex_
	
	on setCf_(sender)
		set cf to sender as real
		dict's setValue_forKey_(cf, current application's NSImageCompressionFactor)
	end setCf_
	
	on resizePrefsFinished_(sender) -- triggered by button in sheet
		resizePrefs's animator()'s setAlphaValue_(0)
		--resizePrefs's orderOut_(me)
	end resizePrefsFinished_
	
	on sliderAdjust_(sender)
		if retainAspectRatio as boolean then
			set newImage to current application's NSImage's alloc()'s initWithSize_({wSliderValue as real, (wSliderValue as real) * aspectRatio})
			set newRect to {{0, 0}, {wSliderValue as real, (wSliderValue as real) * aspectRatio}}
		else
			set newImage to current application's NSImage's alloc()'s initWithSize_({wSliderValue as real, hSliderValue as real})
			set newRect to {{0, 0}, {wSliderValue as real, hSliderValue as real}}
		end if
		set oldRect to {{0, 0}, {0, 0}}
		newImage's lockFocus()
		picImage's drawInRect_fromRect_operation_fraction_(newRect, oldRect, current application's NSCompositeCopy, 1)
		set bm to current application's NSBitmapImageRep's alloc()'s initWithFocusedViewRect_(newRect)
		set theData to bm's representationUsingType_properties_(current application's NSJPEGFileType, dict)
		newImage's unlockFocus()
		set newFileWrapper to current application's NSFileWrapper's alloc()'s initRegularFileWithContents_(theData)
		newFileWrapper's setPreferredFilename_("smallImage.jpg")
		set newAttachment to current application's NSTextAttachment's alloc()'s initWithFileWrapper_(newFileWrapper)
		set attrib to current application's NSAttributedString's attributedStringWithAttachment_(newAttachment)
		tv's textStorage()'s replaceCharactersInRange_withAttributedString_({indx, 1}, attrib)
	end sliderAdjust_
end script

Ric

I’ll try this for sure – my own version had only one slider, as picture is always reduced proportionally, but yours is more complete with a bit more code.

Three questions :

  1. did you see the number of additional properties ? :wink:
  2. your preferred way to set variables is setHSliderValue_ instead of set (my) hSliderValue.
    a) Is it equivalent to set hSliderValue, or to set my hSliderValue?
    b) Is there a benefit to do so or is it just a style of programming?

Regards,

Yeah, when you do a lot of binding, you need a lot of properties

In most cases setHSliderValue_ is equivalent to “set my hSlider to”, so mostly it’s a style choice. However there is a case where they are different, and that is if you want to override the setter method for a property. That setter will only be called if you use my style, rather than the “my” style.

Ric

Ok, everything is working fine – I have disposed my own controls on a sheet finally, so the edition becomes modal for the window of the edited image.

I have my XCode frozen sometimes – but maybe (and hopefully) it’s a problem with XCode 4, because XCode 3.2 does not freeze.

Your code is working, but I don’t understand everything, for example WHERE is the picture, maybe it is stored in memory and the textStorage holds a pointer to this “fake” file wrapper? Anyway, if the textview (attached to a property of a NSDic) is dismissed from the main editor, the changes are not retained.

I’m looking for a way to “touch” the text so the changes are made permanent in the text’s textStorage. Once again, without knowing exactly what happens in the background – and fearing that the next version of MacOS or XCode will refuse the trick :frowning:

Thank you Ric, Master of the Cocoa’s Arcana.

. and found nothing. I have the feeling that everything I do in the back of NSTextView (manipulating its NSTextStorage) is ignored by the binding mechanism. The changes are all visible on screen, but not to the bindings.

If only we could violate the encapsulation of this (***) class and set a sort of “textIsDirty” flag :mad:

Does fixAttachmentAttributeInRange: do anything for you?

No, I tried fixAttachmentAttributeInRange with the range of modified the text’s cell without result. Even for the whole text :

    gTextView's textStorage()'s fixAttachmentAttributeInRange_({0, gTextView's textStorage()'s |length|()})

I get no error – but the text is still unmodified.

Which text?

I’ll be as specific as possible:

my textview is bound to my gTextView property. The attributedString of this text view is bound to another property (a key in a mutable dictionary).

When I insert or change something in the text using, for example, replaceCharactersInRange_withAttributedString_, the text is modified into the editor, but the key value is not.

When I type something into the text with the keyboard, the key value is modified.