revUp - Updates and news for the Revolution community
Issue 135 | June 15th 2012 Contact the Editor | How to Contribute

MapKit and DataGrids
More tips and tricks on using MergEXT to enhance your LiveCode experience.

by Monte Goulding

Recap

In part 1 of the series we introduced mergExt and started developing an iPhone app using mergMK (MapKit framework external) and MobGUI. We got to the point where the user could add a pin, drag and drop it to relocate and give it a title and subtitle. Last time I also had a simple implementation of an annotation note. I'm going to drop that this time and re-introduce notes later with a full screen user interface for note entry. This time we will lay the foundations for that and use the built in functions for picking a photo to be associated with the annotation. I'll also introduce a custom control that's included in the mergExt suite (mergDataGridScroller) that handles touch scrolling of a DataGrid on iOS and Android.

Map

Firstly a little bugfix

MapKit won't show the annotation callout unless there is a title so we need to check for the case where the user hasn't entered one in. All we need to do is put a space there instead of nothing. So our new closeCard script for the "Details" card is (remeber I've removed the notes from this card because I want to do something better later):

on closeCard
   if the uText of group "title" = "" then
      set the annotation["title"] of this cd to " "
   else
      set the annotation["title"] of this cd to the uText of \
            group "title"
   end if
   set the annotation["subtitle"] of this cd to the uText of \
         group "subtitle"
end closeCard

Add a DataGrid to the app using mergDataGridScroller from the DropTools palette.

datagrid

Editing the data grid template group

Turn the select grouped controls back off so you can then select and edit the template group. Resize the background graphic to 60 pixels high. Drag an image control on to the background, size it to fit and set it's properties. Relocate the "label" field to the right of the image and centered vertically. Add a line at the bottom of the group using the polygon tool to act as a separator between our documents.

Edit Properties

Improving the data structure

Before we start scripting the behavior of the docs data grid we should make some improvements to the data structure to make things simpler. As the goal is to be able to associate any number of documents with the annotation I have decided to alter the custom property set keys to title,subtitle,coordinate and docs where the docs element is a data grid type array with an index key and label and file child keys. In this way we can set the dgData of group "docs" to tAnnotationA["docs"]. The files will be saved in a folder in the documents directory using the annotation ID as the folder name and the label as the basis of the file name.

Click the Row Behavior... button to edit the template group's behavior script. In this tutorial we will make use of the mergDoc functions for returning icons for documents. In a later tutorial we will add thumbnails for photo and video documents. Change the FillInData and LayoutControl commands as follows:

global gIDs

on FillInData pDataArray
   set the text of field "Label" of me to pDataArray["label"]
   put specialFolderPath("Documents")&"/"&gIDs["setnamefromid"] \
       [the uAnnotationID of this card]&"/"&pDataArray["filename"] into tPath
   put mergDocIcon(tPath,line 1 of mergDocIconSizes(tPath)) \
         into image "thumb" of me
end FillInData


on LayoutControl pControlRect
   local theFieldRect
   set the points of graphic "line" of me to item 1 of \
         pControlRect,item 4 of pControlRect-1&cr&item 3 \
         of pControlRect,item 4 of pControlRect-1
   put the rect of field "Label" of me into theFieldRect
   put item 3 of pControlRect - 5 into item 3 of theFieldRect
   set the rect of field "Label" of me to theFieldRect
   set the rect of graphic "Background" of me to pControlRect
end LayoutControl

To handle the fact that there's no annotation id for an annotation before the initial drop we need to setup the custom property set name before we move to the "Details" card and then use that set name when we return. Change the touchEnd script of the Add Pin button on the "Map" card to:

global gIDs

on touchEnd pId
   mobGUIUntouch the long id of me
   # LC5 : new visual effect syntax
   lock screen for visual effect
   set the uAnnotationID of cd "Details" to ""
   put the long seconds into gIDs["setnamefromid"][""]
   go card "Details"
   wait for 0 millisecs with messages
   unlock screen with visual effect "push left very fast"
end touchEnd

Then change the showMap command to use it when the uAnnotationId is empty:

on showMap
   -- add or update any annotations on the map and save data etc
   switch the uAnnotationID of cd "Details"
      case "just opened"
         break
      case ""
         -- create and save a new annotation
         put gIDs["setnamefromid"][""] into tPropSet
         break
      default
         -- update existing annotation
         put gIDs["setnamefromid"][the uAnnotationID of cd "Details"] \
               into tPropSet
         -- remove the annotation from the map
         mergMKDeleteAnnotation the uAnnotationID of cd "Details"
         -- delete the old relationship (the other one is overwritten
         -- later)
         delete variable gIDs["setnamefromid"][the uAnnotationID of cd \
               "Details"]
   end switch
   put the customProperties["annotation"] of cd "Details" into \
         tAnnotationA
   set the customProperties[tPropSet] of stack "UserData" to \
         tAnnotationA
   save stack "UserData"
   mergMKSet "visible",true
   -- add to the map after it's visible so we see the animation
   if the uAnnotationID of cd "Details" <> "just opened" then
      -- add the annotation and update the globals
      put mergMKAddAnnotation(tAnnotationA["coordinate"], \
            tAnnotationA["title"],tAnnotationA["subtitle"],true,true,"red",true) \
             into gIDs["idfromsetname"][tPropSet]
      put tPropSet into \
            gIDs["setnamefromid"][gIDs["idfromsetname"][tPropSet]]
   end if
end showMap

annotation

Adding a document to the annotation

Use MobGUI to add another button and set it's label to "Add Doc".

Add Doc

Choosing the document type

In this tutorial we will just add an image but in later tutorials we will add video, audio and text/notes so we will use mergPop to choose which. Set the touchEnd handler script of the Add Doc button to:

global gIDs

on touchEnd pId
   mobGUIUntouch the long id of me
   if the environment = "mobile" then
      put "Video From Library" into tOptions
      if mobileCameraFeatures("rear") contains "video" then
         put ",Video From Camera" after tOptions
      end if
      put ",Photo From Library" after tOptions
      if mobileCameraFeatures("rear") contains "photo" then
         put ",Photo From Camera" after tOptions
      end if
      put ",Note" after tOptions
      switch mergPopSheet("What type of document do you want to \
            add?","Cancel","",tOptions,"black translucent")
         case "Video From Library"
            -- TODO
            break
         case "Video From Camera"
            -- TODO
            break
         case "Photo From Library"
            getPhoto "library"
            break
         case "Photo From Camera"
            getPhoto "camera"
            break
         case "Note"
            -- TODO
            break
      end switch
   end if
end touchEnd

command getPhoto pSource
   set the visible of the templateImage to false
   mobilePickPhoto pSource
   if the result is not "cancel" then
      ask "Enter a label for the photo:"
      if the result is not "cancel" then
         mobileBusyIndicatorStart "square","Saving..."
         put it into tDoc["label"]
         put validFileName(tDoc["label"],"png") into \
               tDoc["filename"]
         put the long id of the last image into tImage
         export tImage to url \
               ("binfile:"&specialFolderPath("Documents") \
               &"/"&gIDs["setnamefromid"][the uAnnotationID \
               of this card]&"/"&tDoc["filename"]) as PNG
         dispatch AddData to group "docs" with tDoc
         mobileBusyIndicatorStop
      end if
      delete tImage
   end if
end getPhoto

function validFileName pLabel,pExtension
   put (specialFolderPath("Documents")&"/"&gIDs["setnamefromid"] \
        [the uAnnotationID of this card]) into tFolder
   if there is not a folder tFolder then
      create folder tFolder
   end if
   set the defaultFolder to tFolder
   put replaceText(pLabel,"[^-a-zA-Z0-9_.() ]+","") into \
         validFileName
   if validFileName = "" then
      put "file" into validFileName
   end if
   if there is not a file (validFileName&"."&pExtension) then
      return (validFileName&"."&pExtension)
   else
      put 1 into tNum
      repeat
         if there is not a file \
               (validFileName&&tNum&"."&pExtension) then
            return (validFileName&&tNum&"."&pExtension)
         end if
         add 1 to tNum
      end repeat
   end if
end validFileName

on the phone

Updating the card script to handle the docs

Here we set the dgData of "docs" group to our saved data in the preOpenCard handler and store the data in the annotation custom property for the map card to use.

global gIDs

on preOpenCard
   # mobGUIStart ---> Inserted by mobGUI plugin
   if "MobGUILib-1334708811870.287842" is not among the lines \
         of the stacksInUse then \
         start using stack "MobGUILib-1334708811870.287842"
   mobGUIPreOpenCard me
   # mobGUIEnd
   if the uAnnotationID of me <> "" then
      put gIDs["setnamefromid"][the uAnnotationID of me] into \
            tPropSet
      put the customProperties[tPropSet] of stack "UserData" \
            into tAnnotationA
      set the uText of group "title" to tAnnotationA["title"]
      set the uText of group "subtitle" to \
            tAnnotationA["subtitle"]
      set the dgData of group "docs" to tAnnotationA["docs"]
   else
      set the uText of group "title" to ""
      set the uText of group "subtitle" to ""
      if the environment is "mobile" then
         put mergMKGet("center coordinate") into \
               tAnnotationA["coordinate"]
      end if
      set the dgData of group "docs" to ""
   end if
   -- save extra stuff like "coordinate"
   set the customProperties["annotation"] of this cd to \
         tAnnotationA
   pass preOpenCard
end preOpenCard

on closeCard
   if the uText of group "title" = "" then
      set the annotation["title"] of this cd to " "
   else
      set the annotation["title"] of this cd to the uText of \
            group "title"
   end if
   set the annotation["subtitle"] of this cd to the uText of \
         group "subtitle"
   set the annotation["docs"] of this cd to the dgData of group \
         "docs"
end closeCard

Presenting the document preview

The next thing we want to do is preview a selected document. For this we will use the mergDoc command mergDocShowPreview and set the script of the "docs" data grid to:

global gIDs

on selectionChanged pHilitedIndex
   put the dgDataOfIndex[pHilitedIndex] of me into tData
   mergDocShowPreview specialFolderPath("Documents") \
         &"/"&gIDs["setnamefromid"][the uAnnotationID of this card] \
         &"/"&tData["filename"]
end selectionChanged

MergeEXT

What next?

Last time I promised I would use mergAV to record and pick video. I didn't quite get there in this tutorial but it will be the first thing we do in part 3.

About the Author

Monte Goulding lives on a small farm in Tasmania, Australia where he built a straw bale house. When not working at M E R Goulding - Software development he enjoys spending time with the family, pretending to be a farmer, mountain bike riding, diving for and eating abalone.

Main Menu

What's New


Get the LiveCode Academy DVD Today