We now have a functioning interface. All that is left is hooking it up to some code to do the work. Remember our outline? Well so far we have completed steps 1-3. The rest of our outline will be completed in this section.
We have already decided on the name of the procedure to do the work. Remember doit from the previous section? We now need to write the code to make a working doit procedure. Here is that code
figure 6-4 the working doIt code
It should be noted that since this code was inserted in the editor BEFORE the previous mainMenu code the line numbers may duplicate line numbers of the mainMenu section. This will make more sense when you view the entire finished source code. Remember that in order to call a routine you have written it must have already appeared in your source code. If we placed this definition of the doit routine after the mainMenu routine we could not call it from within the mainMenu code. The DCAL compiler works from the top down and expects to know about any code you are calling when it finds the call.
Lines 16 - 25 should be apparent based on the information in the previous section. Give yourself extra credit if you see a problem in this declaration. What is the problem? Well when we originally called doit from the mainMenu routine we simply wrote doit; We did not make any decisions about what parameters we might need to pass to doit. Now that we have worked out the doit Procedure we know that we will need to pass it both the searchString and replaceString variables from the mainMenu Procedure. To correct this simply find the line in the mainMenu Procedure that reads:
and change it to read:
doit (searchString, replaceString);
this has already been taken care of for you if you are using the available source code for our sample macro.
Now is a good time to mention that the names of the variables for the search and replacement strings do not have to be the same as the parameters used in the doit Procedure. It does make the code more readable but it isn't required. What I mean by this is that if you notice we have consistently used the name searchString to refer to the searchString. Sounds simple right? Well it is. Just keep in mind that what is called searchString in the mainMenu Procedure could have been called shoeString in the doit Procedure and it would have been perfectly legal.
Now back to our code listing.
On line 26 we get the length of the searchString. We used the DCAL routine STRLEN to determine the length of the string. When this line is run searchLenght will contain the number of characters in searchString.
What follows next is the standard loop to do something involving all the layers in the drawing file. Starting on line 27 and ending on line38 is the typical 'go thru the layers and do something loop' This is the loop structure you should always use changing only the code on line 29-36.
Here is what the code does. Starting on line 27 we get the first layer in the drawing. Note that we said layer not layer name. The layer is the entity of type layer. This entity happens to have a name. We will get the name later when needed. To get the first layer in the drawing we rely on the well named LYR_FIRST function. This function simply returns the first layer in the drawing file.
Next we enter a WHILE loop to iterate through each layer and do the actual work of changing the layer names. The WHILE statement here is worth a look. What it says is 'as long as there is a next layer keep going. How do we get that from the code? Well LYR_NIL will return true if there is no 'next layer.' The Keyword NOT simply reverses the Boolean. So when LYR_NIL returns false, indicating there is another layer the NOT transforms the false into a true and the loop continues. You could have also written
WHILE lyr_nil(lyr) = false DO
Either way is legitimate. Use whichever feels more comfortable.
So now that we are into our loop what happens next? We are only interested in the name of the layer we have so we use GETLYRNAME to check the name. We store the layer name in the variable lyrName.
On line 30 we check to see if searchString occurs within lyrName. Using STRPOS we determine if the searchString is found in lyrName. STRPOS returns the position in the string where searchString occurs. If the string is not found STRPOS returns zero. The value returned by STRPOS is stored in the variable foundIndex.
The next section is the real working portion of the code. The main objective of the macro is completed in lines 31 thru 36. First we check if foundIndex is greater than zero. Remember that STRPOS returned a zero if the searchString was not found. If foundIndex is greater than zero than we know that the searchString was found AND we know where it occurs in the lyrName.
Within lines 32-35 we change the lyrName using our replacementString. We use a FOR loop and some basic knowledge of strings to replace the searchString characters with the replacement string characters. Here is how we do it. We will use a number from 1 to searchLength to position ourselves in the strings. You should remember that on line 26 we determined the length of our searchString.
The bulk of the work is done on line 33. What this line says is replace character foundIndex + (i-1) with character replaceString[i]. This may seem like heady stuff but it is quite simple if you know something about strings.
Strings are simply collections of characters. A character is a single letter, number or symbol (A, B, c, d, 1, 2, #, $) A string is an ARRAY of characters. As you learned in Lesson 3 Types an ARRAY is a series of data that shares a common type and is indexed by integer. So logic states that if a string is an array of characters we should be able to manipulate the characters that make up the string by way of array notation such as str for the second character of the string. Logic is strong stuff and can't be argued with in this case.
So getting back to our code we see that all we do is find the first character in the lyrName that matched the searchString and replace it with the first character of the replacementString. Simple stuff once you understand the thought process. We do this until al the necessary characters have been replaced.
The last step to make sure our work sticks is to use SETLYRNAME to write the new layer name into the database. If we skipped this step our work would be lost.
Line 36 ends the IF then FOR loop we started on line 31 and line 37 grabs the next layer before the END and line 38 sends us back to the top of the WHILE loop (line 28) to repeat the process.
Once the WHILE statement evaluates to false the doit Procedure is done and control returns to the mainMenu function. The mainMenu function is still in that main REPEAT loop so you can change more layers or select Exit to end the macro.
That's it. You are now a bona fide DCAL Programmer. We have just written a complete working macro.
Some things should be noted at this point. Our macro works but it could use some more polish. It would be nice to add some display to show what the search and replace strings are and perhaps how many layers were changed. Another feature would be to add a trap door to the layers menu to work with layers from within our macro.
There is also one assumption we have made that could get us into trouble. We have based this code on the idea that the search and replacement strings are the same length. Yet nowhere in the code do we check this fact. We could get into trouble if the user enters a different length string for one of the values. If would be wise to check for this condition and handle it gracefully.
For now we have a working macro that can be useful. To hone your skills try to add some of the features we discussed above or others that you come up with yourself.