Tracking Mouse Clicks

I just recently received another “comment from Zack”: This time, he was wondering about how one would go about tracking mouse clicks on an image field.

I had never attempted to do that so I took it on as a challenge and thought I would share the results in this post.

I knew from the start that XFA alone wasn’t going to be able to handle this simply because (to my knowledge) it doesn’t provide any information as to the position of the mouse pointer when an event occurs. The most logical place I thought would’ve provided the information — the _Event Pseudo Model_ (the xfa.event object available in all XFA events) — didn’t live up to my expectations. Thankfully, XFA at least provides a Click event so that I could know when the image got clicked.

The next logical place to look was in Acrobat’s Scripting Object Model (in the “AcroForm Objects”: In the Acrobat Document object, I found what I was looking for: the _mouseX_ and _mouseY_ properties which provided the location of the mouse with respect to the document window.

The last thing I needed was information about the dimensions and location (within the Acrobat Document Object’s coordinate space) of the image field and the Acrobat Field object’s _rect_ property would give me just that.

The combination of the XFA Click event, the Acrobat Document object’s mouseX and mouseY properties and the Field object’s rect property was just what I needed to get this to work.

Of course, I soon discovered that I had another problem to figure-out: The behaviour of an image field in a PDF form running in Acrobat is that when clicked, it opens a browse dialog that lets you pick the content for the field. Unfortunately, there isn’t any way to suppress that dialog other than making the image field read-only or by using a static image object but then both alternatives prevent the Click event from firing. So I needed some clever way to capture a mouse click over an image (whether it was a field or a static object) and I decided to use a button with a transparent fill and no border (so it was essentially transparent). Since buttons are fields just like image fields, the mouseX, mouseY and rect properties would still be available for the button and if I sized the button to fit the image and placed it over-top, I would essentially end-up with an HTML <map>.

“Download Sample [pdf]”:

*Minimum Requirements:* Designer 7.1, Acrobat 7.0.5.

The first challenge was getting an instance of the Acrobat Field object which represents the button using thebc. That was easily accomplished by using the script I provided on my “AcroForm Field Name Generator”: article.The next problem to be solved was the fact that the default behaviour, in Acrobat, for a button when it’s clicked is to invert its content area. Since I was trying to hide the button, I needed a way to suppress the inversion so that clicking on the button would give no visual feedback to the user. That way, it would give the impression that the user was actually clicking on some sort of hyperlink on the image itself. That was easily solved by using the _highlight_ property of the Acrobat Field object representing the XFA button in its Enter event (had to be Enter and not Initialize because Initialize is too early in the form’s initialization process for the association between the XFA button and it’s Acrobat Field counterpart to be established):bc. =highlight.n;The last problem was with respect to calculating the coordinates of the hot spots on the button that would trigger a reaction (in this sample, I was just going to set the value of a text field somewhere else on the form to reflect the area that was clicked). The problem there was that while I had the mouse location and the button’s dimensions all in the same coordinate space (Acrobat Document), the coordinates were specified with (0,0) set to the document’s *lower* left corner.While this may not seem like a big deal to some of you, it really messes me up when (0,0) isn’t at the *top* left corner (with the maximum (x,y) set to the bottom right corner). I guess that’s a result of years of writing code for Windows where an MFC CWnd’s coordinate space places (0,0) at the top left corner. Anyway, after lots of hair pulling, I finally figured-out how to properly calculate the hot spots in this strange — no, alien — coordinate system.Zack, if you have any other questions, please post a comment.

11 Responses to Tracking Mouse Clicks

  1. Zack says:

    Thanks a ton Stefan,This really helps a lot. I expected that the solution would involve going through something other than the normal forms scripting.-Zack

  2. Danny says:

    Stefan,Your work is amazing!! I just checked your new work ‘flashing fields’. Its mind blowing. Can we use the same idea to track all required fields before submitting and flash only the required fields which are not filled??

  3. Danny,Thank you very much! I’m glad you appreciate my posts!As for the Flashing Fields sample, I believe you could modify it fairly easily to make all required fields flash at once.You could probably achieve that functionality by turning the moFlashTimerID and moFlashingField variables into JavaScript Array objects (one entry for each required, non-filled field), removing the (no longer needed) calls to ValidateField on the Validate events of the form fields and writing a little bit of script on the Submit button’s Click event to go looking for all required fields (check that the mandatory property is set to “error”).

  4. Zack says:

    Stefan,Thanks once again for all the help. Think you’re up for another challenging idea? This time I want to have an image field where clicking on different regions of the image actually cause the image itself to change. A good example would be an image of a traffic light. Let’s say by default all three circles are black, but if you click on the bottom circle it turns green. Clicking on it again would cause it to turn black. The middle and top circles would have similar behavior with red and yellow. This is just a simplified example; I’d like to be able to alternate (non rectangular) regions between various colors.One idea I had was to have various images that look nearly identical stored in an XML file and then loading the “right” one into the image field based on which region was clicked. So when the user clicked on the bottom circle of the traffic light for example, I’d just load in the image with the bottom light green and the rest black. The problem I’ve found with this is that it seems dynamically changing the href of the image field doesn’t actually change the picture displayed. Also, if I wanted the different circles to cycle through many different colors, or even have combinations of colors showing at once, it would be very tedious to create and store every single possible image in an XML file. (If there turns out to be a way to dynamically change the image displayed I wouldn’t mind though)My next idea was to place every single possible image on top of each other before placing the transparent button over all of them. This way I could simply set every image field to hidden except for the correct one. At first I didn’t think this was going to work, because apparently with the static image object you can only hide them, but not set the presence back to visible. It seems you can do this with image fields however. So far this is my only somewhat viable idea.An extension of this idea was to make a base image, and then put several images on top of it that were mostly transparent. This way I could drastically cut down on the total number of images I’d have to create. However, after experimenting with overlapping .png’s and .gif’s, I saw a post on the forums in which you stated that transparency is currently unsupported with image fields, but is on the table.Regardless, all of these ideas hinge on the ability to track clicks across non-rectangular regions of the image. I’m not sure that using the Math class to determine the regions is feasible, as some of them might have weird shapes. I’m wondering if there’s some type of method for doing an image map similar to HTML?Another idea is to just use an SVG image for the entire thing. However, from what I’ve seen you can’t import an SVG image into an image field. I’m thinking there might be a way to still have one on the PDF without using forms objects, but don’t know how much of the SVG added functionality would stay intact.A final brute force idea is to just store each and every pixel of the region in a 2D array and each time they click check to see if the mouse point is a hit for that region. Not sure if it’s even possible to convert this pixel data into points on the image field though.As you can see I’m pretty open to how this can be done. I really just have no clue which would be the best way to pursue, of if any of this is even possible. If you could point me in a good direction I’d be even more appreciative than I already am. Thanks a ton,-Zack

  5. Zack,You’re really pushing the limits of our image support across XFA (Designer) and PDF (Acrobat) — that’s great! I admire your efforts at coming-up with different possible solutions to your problem.I’m afraid your best bet for this kind of click tracking is to use multiple read-only (so that the user can’t click on them to pick another image) image fields with specific iterations of one image (e.g. all the different combinations of red, yellow and green lights). Note that you souldn’t use static images because they’re static (unchanging) by nature.To make an image field disabled, use the following script in the image field’s Initialize event (depending on the scripting language you’re using):$.access = “readOnly” // FormCalcthis.access = “readOnly”; // JavaScriptTo load an image at run-time (in Acrobat), just use the following script on the transparent button (depending on the scripting language you’re using):// FormCalcImageField.value.#image.href = “picture2.jpg”// JavaScriptImageField.value.resolveNode(“#image”).href = “picture2.jpg”;SVG might have been a solution but while support is planned, I don’t know when it’ll actually get into the product.Transparency would certainly cut down on the number image fields you would need (from 8 down to 4) but that won’t be available until an upcoming release (soon).As for tacking clicks on a non-rectangular region, neither XFA nor Acrobat offer the concept of an HTML <map> (to my knowledge, anyway). Therefore, your best bet is some wild math! Rectangular regions are the easiest, circular regions aren’t too bad but other regions are more difficult. For the odd shapes (neither rectangular nor circular), you could try breaking them up into multiple smaller triangular regions because hit-testing a triangle is easier than trying to hit-test something that’s not even a normal geometric shape.

  6. Zack says:

    Stefan,I was afraid that was the case. As far as image fields go, is there a way to load them from the XML file by reference? That is, by just providing the filepath and then storing the images separately? The only way I’ve gotten it to work is actually putting the base 64 image data in the XML file. I’m thinking I may need to set up the schema in a special way or something, not too sure.Also, in the code you provided:ImageField.value.resolveNode(“#image”).href = “picture2.jpg”;is “picture2.jpg” just the relative path to an image stored in the same folder as the form?Thanks again,-Zack

  7. Zack,The value of the href attribute can be either a relative or absolute path to a file on your local drive or on a server somewhere.I believe the piece you’re missing is a script which gets the right URL from your XML Data file and sets it on a certain image field’s href property (in JavaScript below):this.value.resolveNode(“#image”).value.href =data.form.image1.valueThis script implies your XML Data file looks like this:<form><image1>picture2.jpg</image1>&lt/form>You could use this script either on a button click (in the mouse tracking case) or on the Initialize event of an image field to have the image loaded when the form is opened and the data is merged into it.

  8. Susan Mortensen says:

    I have been reading your posts and with your information I have been able to solve several of my problems, and I thank you for that. The one I can’t solve is being able to change the checkbox so it will be black on the first click, RED on the second click and then off on the third click.I would appreciate any information you could give me on this problem.

  9. Susan,What I understand from your question is that you’re wanting to make the check mark (“x”) in a check box be black when its value is “neutral” (the first click), red when its value is “on” (second click) and off when its value is “off” (third click).Unfortunately, scripting of the check box’s value (check mark) when it’s in its “neutral” state (first click) is somewhat difficult because Acrobat washes-out the color of the check mark and black becomes gray (which is the default color).For instance, you could use this FormCalc script on a check box’s Click event to change the check mark’s color depending on the check box’s state:if ($.rawValue == 2) then // neutral$.font.fill.color.value = “0,0,0” // blackelseif ($.rawValue == 1) then // on$.font.fill.color.value = “255,0,0” // redendifThis would make the check mark red in the “on” state but it would still be gray in the “neutral” state. Maybe you could use “0,0,255” (blue) for the “neutral” state instead of black.

  10. ashish says:

    How do i can make a checkbox readonly using javascript

  11. Ashish,You can make a check box read-only in JavaScript using the access property:

    MyCheckBox.access = “readOnly”;