Happy #mapinfomonday
In today's article, we will continue down the path of building our first MapBasic application. You can find the earlier articles on this topic here.
Until now, we had to run our application for it to perform its task. We will now integrate it into the ribbon interface of MapInfo Pro so that we easily can use the functionality of the tool. This means that we need to restructure our code a bit again, I will show you have you easily can add buttons to the ribbon, and we will make our code a bit more clever so that it can detect if a map already exists. Finally, we will add another table that the tool can open doubling the capability of the tool.
Are you ready? Let's get started!
Restructuring the Code - again
As I mentioned in an earlier article, you will often use the Main
procedure to integrate your tool into the interface, and then let it wait for the user to request functionality to be executed by clicking on a button. That's what we will do now.
We will move the current code from the Main
procedure into a new procedure called OpenTableCustomers
. We will also make a slight change to the code to make it easier to adapt to other tables we might want to open.
The new procedure will look like this:
'******************************************************************
'Sub OpenTableCatchmentAreas: Open a specific table
'******************************************************************
Sub OpenTableCatchmentAreas
Dim nMID As Integer,
sTabFile As String
sTabFile = "C:\3. Demo\2. Maps\2023\Site Selection\Customers\Catchment Areas.TAB"
nMID = OpenTableIntoMap(sTabFile)
Call ConfigureLayer(nMID, sTabFile)
End Sub
I added a new variable, sTabFile
, to hold the name of the table to open. From the name of the variable, you can see that it is of type String
and that it holds the name of a MapInfo Native tab file.
By adding this variable and passing it to the function and the procedure, I only need to change the name of the tab file in one place.
The Main
procedure now holds no code but we will change that in the next chapter.
Adding a PushButton to the Ribbon
It's now time to add a button to the ribbon.
When you want to add a button to the ribbon, you place it in a given tab (see (1) below), and in a given tab group (see (2) below).
We want to add our button to the Home tab in the File group. When specifying these, you need to know their internal names. What you see on the ribbon is their caption. With MapBasic v2021, we have included a file containing all the names as constants. We can use these instead of the names, and this file also gives us access to finding the internal names.
To use the defines, or the constants, we need to include this file in our source code file. We will also include another file that holds constants often used, MapBasic.def.
You typically include these files at the top of your source code:
Include "MapBasic.def"
Include "RibbonControls.def"
The two constants we need to determine the name of the tab and tab group are these:
DEFINE TAB_HOME "TabHome"
DEFINE TAB_HOME_FILE_GRP "HomeFile"
Each line has three elements: 1) the Define keyword, 2) the name of the constant, and 3) the value that the constant refers to, here the name of the tab and tab group. To use such a constant, you simply insert the name of the constant. When you compile your source file, the compile will replace the constant name with the value that it refers to.
The benefit of using a constant instead of the actual value is that the value might change over time, and for some values, it can be hard to see what it actually means. This is especially the case where a constant refers to a numerical value.
You have several ways to add a button to the ribbon through MapBasic. For this simple example, we will use the Alter Buttonpad
statement to add a PushButton
. In a later article, we will use other ways when we need more control over the placement of the button or when we want to add more advanced controls such as SplitButtons
.
We use Alter Buttonpad
as we want to add the buttons to an existing group. If you want to create a new group on an existing or a new tab, you can use Create Buttonpad
to do so. This code needs to go into the Main
procedure
Alter ButtonPad TAB_HOME_FILE_GRP
Add PushButton
Calling OpenTableCustomers
Large Icon -1 File ApplicationDirectory$() & "Customers 64x64.png"
HelpMsg "Open Table Customers\nCustomers"
Tab TAB_HOME
In the code above, you can see that I add a PushButton
to the File group on the Home tab.
When the user clicks on the button, the procedure OpenTableCustomers
will get called and executed.
I have configured the button to use a large button where the icon to show on the button is stored in a file in the same folder as my application. ApplicationDirectory$()
returns the folder where the application file is located.
I have created the icon by taking a screen dump of MapInfo Pro after opening the table into a map window. I then dragged the image towards the upper left until the top left of the map window was at the upper left of the image. I then changed the size of the image to 64 by 64 pixels. You can also save the image as a 32 by 32-pixel image as that is the standard size for large icons.
The HelpMsg is divided into two parts by \n
. The first part is the tooltip that will appear when you hold the cursor on top of the button. This is often a longer description of what the button does. The second part is the caption of the button shown under the button on the ribbon.
This is how the button looks on the ribbon. The button is shown as the 6th button from the left.
When I click on the button, the table will get opened into a new map window.
Make the OpenTableIntoMap Code more Clever
Until now, the OpenTableIntoMap procedure would open the table and create a new map window for this table. We now want to change this so that it will add the table to the currently active window if it's a map window. This is probably the behavior most users would expect.
Here is the modified procedure
'******************************************************************
'Function OpenTableIntoMap: This function opens a table into a map window
' takes a name of a tab file to open
' returns the Window ID of the map now showing the table
'******************************************************************
Function OpenTableIntoMap( ByVal sTabFile As String) As Integer
Dim nMID As Integer
Open Table sTabFile Interactive
nMID = FrontWindow()
If nMID <> 0 Then
If Not WindowInfo(nMID, WIN_INFO_TYPE) = WIN_MAPPER Then
nMID = 0
End If
End If
If nMID = 0 Then
Map From PathToTableName$(sTabFile)
nMID = FrontWindow()
Else
Add Map
Window nMID Auto
Layer PathToTableName$(sTabFile)
End If
OpenTableIntoMap = nMID
End Function
I use the function FrontWindow()
to determine the ID of the active window. If the value is 0
, I know there is no active window and so also no map window.
If the value is not 0
, I need to check the type of the active window. I use the WindowInfo()
function to return the type of the map. The constant WIN_INFO_TYPE comes from the file MapBasic.def and refers to the numeric value 3
. Using this constant instead of the numeric value makes it much easier to read the code later on. If the active window isn't a map window, I set the value of the nMID
variable to 0. This value is later used to determine if the table should be added to an existing map window or to a new map window.
Finally, I check the value of the variable nMID
. If it's 0
, I create a new map window. Otherwise, I use the statement Add Map
to add the table to an existing map window. The keyword Auto
tells MapInfo Pro to automatically determine at what position to place the layer based on the objects in the table and the objects in the other layers.
I have used the If Then
statement to handle the flow of the procedure. You can use the If Then statement to check if a condition is met before performing a certain action, and you can use the Else
part of the statement to add an alternative way for your code to move if the condition isn't met. Note how I'm using indention to make it easier to follow the flow of the procedure based on the conditions.
The function now returns the value stored in the variable nMID
as this variable now holds the ID of the existing map window or the new map window.
Compile your source code and run it. Push the button to see if it still works. remember that you need to test this with no windows open, an existing map window open, and a browser window open.
Opening an Additional Table
We have now built a structure in our tool that makes it possible to add additional tables with just minor changes to our code. To test this, I will add an additional table. Just like for the first table, I open the MapBasic window and then I open a configure the second table. I can now copy the Set Map
statements that I need to be able to configure the layer as I want it to look after opening it.
Set Map Window 3010 Layer 2 Display Global Global Pen (5,6,16711680) Global Brush (1,16777215,16777215)
Set Map Window 3010 Layer 2 Selectable Off
Just like previously, I need to modify these slightly. I will get back to this as we also need to modify the structure of the ConfigureLayer
procedure.
Until now this procedure has just had to deal with one layer but now we add another. This means we need to determine what layer we are asked to configure. We can use the name of the table to do so. We just have to build a structure that can split the code based on the table name. We are looking at two tables now but that could grow when we start adding more so the best option is probably to use the Do Case
statement to handle this.
'******************************************************************
'Sub ConfigureLayer: This procedure configures the layer
' takes the Window ID of the map with the layer
' takes the name of a tab file to confire
'******************************************************************
Sub ConfigureLayer( ByVal nMID As Integer, ByVal sTabFile As String)
Dim sTab As String
sTab = PathToTableName$(sTabFile)
Do Case sTab
Case "Customers"
Set Map Window nMID Layer sTab Display Global Global Symbol (38,16754768,8,"MapInfo Symbols",256,0)
Set Map Window nMID Layer sTab Label Auto On
Set Map Window nMID Layer sTab Label With Contract
Case "Catchment_Areas"
Set Map Window nMID Layer sTab Display Global Global Pen (5,6,16711680) Global Brush (1,16777215,16777215)
Set Map Window nMID Layer sTab Selectable Off
End Case
End Sub
I create a new variable sTab
to hold the name of the table once opened into MapInfo Pro. I need this when configuring the layer through the Set Map
statement, and I now also need it to determine what table is being configured. You can also see the modified Set Map
statements for the second table above.
I use the Do Case
statement to determine what settings to use. Do Case sTab
tells MapInfo Pro to compare the value in the variable sTab
against a number of possible values. Case "Customers"
is the first of the options where "Customers"
is the value to compare against. If the value in the variable matches this value, the code after will get executed. If not, MapInfo Pro will continue comparing against the next value. Notice that only the first match will be used. Once a match has been made, no more matches can be made. Similarly to Else
for the If Then
statement, I can also use Case Else
to catch all the cases that didn't find a match. In our example, we don't need this.
Finally, I need to add an additional button to the ribbon in the Main
procedure. Notice how I just add another PushButton element to the Alter ButtonPad
statement. The Add
keyword should only be there once.
Alter ButtonPad TAB_HOME_FILE_GRP
Add PushButton
Calling OpenTableCustomers
Large Icon -1 File ApplicationDirectory$() & "Customers 64x64.png"
HelpMsg "Open Table Customers\nCustomers"
PushButton
Calling OpenTableCatchmentAreas
Large Icon -1 File ApplicationDirectory$() & "Catchment Areas 64x64.png"
HelpMsg "Open Table Catchment Areas\nCatchment Areas"
Tab TAB_HOME
And I need a dedicated procedure for this table too:
'******************************************************************
'Sub OpenTableCatchmentAreas: Open a specific table
'******************************************************************
Sub OpenTableCatchmentAreas
Dim nMID As Integer,
sTabFile As String
sTabFile = "C:\3. Demo\2. Maps\2023\Site Selection\Customers\Catchment Areas.TAB"
nMID = OpenTableIntoMap(sTabFile)
Call ConfigureLayer(nMID, sTabFile)
End Sub
Remember to add the declaration for this new procedure so that you now have all these declarations for your functions and procedures:
Declare Sub Main
Declare Sub EndHandler
Declare Sub OpenTableCustomers
Declare Sub OpenTableCatchmentAreas
Declare Function OpenTableIntoMap( ByVal sTabFile As String) As Integer
Declare Sub ConfigureLayer( ByVal nMID As Integer, ByVal sTabFile As String)
Compile and run your tool and check that you now have two buttons and that each button will open and configure the table.
Are you still here? This got a bit longer than I had expected. Next time we will keep it a bit shorter and see how you can integrate your tool better into the Tools Window.
Find the sample code attached below, including the icons I created.
------------------------------
Peter Horsbøll Møller
Principal Presales Consultant | Distinguished Engineer
Precisely | Trust in Data
------------------------------