# URLS.SML - Pops up a dialog that opens the external browser to the url selected. # Which URLs get displayed is based on where the user clicked. # The URLs are stored in the reference file url.txt in the same directory as this script. # Requires TNTmips version 6.4 # Format for reference file: # [filename : description] # {element} # element is either "raster value" or a vector element # ... # of the form "(element type) (table name) (field name) (field value)" # {element} # (See sample reference file) # url # ... # url # The following symbols are predefined # class GROUP Group {use to access the group being viewed if the script is run from a group view} # class VIEW View {use to access the view the tool script is attached to} # Variable declarations class Widget listParent; class XmForm form, buttonRow; class XmRowColumn vectorModes, actions; class XmSeparator line1, line2; class XmList list; class XmLabel vectorLabel, actionLabel; class XmToggleButton pointButton, nodeButton, lineButton, polyButton; class XmToggleButton scanButton, addButton; class PushButtonItem urlButton, openButton, closeButton; class XmDrawingArea da; class GC gc; class PointTool pointTool; class MdispRegionTool polyTool; string filepath$, mode$, action$; VECTOR vv; RASTER rv; array numeric arr[1000]; numeric elemNum; numeric setDefaultWhenClose; # Callback for drawing area expose. Draws text. proc cbRedraw() { if (gc == 0) return; ActivateGC(gc); SetColorName("gray75"); FillRect(0, 0, da.width, da.height); SetColorName("black"); DrawInterfaceText("Layer: " + Group.ActiveLayer.Name + "\nURL File: " + filepath$, 0, 10); } # Callback for when the active layer changes. proc cbLayer() { if (Group.ActiveLayer.Type == "Raster") { vectorLabel.Sensitive = 0; pointButton.Sensitive = 0; nodeButton.Sensitive = 0; lineButton.Sensitive = 0; polyButton.Sensitive = 0; } else { vectorLabel.Sensitive = 1; polyButton.Sensitive = 1; lineButton.Sensitive = 1; nodeButton.Sensitive = 1; pointButton.Sensitive = 1; } cbRedraw(); } # Callback for when the open button is pressed. proc cbOpen() { filepath$ = GetInputFileName(filepath$, "Open URL file", "txt"); cbRedraw(); } # Callback for when the go button is pressed. proc cbGo() { local string url$; url$ = list.GetItemAtPos(list.GetFirstSelectedPos()); if (list.SelectedItemCount > 0 and url$ != "No URLs found!" and url$ != "Type not supported!" and url$ != "No element found!" and url$ != "File not found!") RunAssociatedApplication(url$); } # Callback for when the close button is pressed. proc cbClose() { pointTool.Managed = 0; DialogClose(form); if (setDefaultWhenClose) { setDefaultWhenClose = false; View.SetDefaultTool(); } } # Callback for when user selects a different vector selection mode. proc cbModeChanged() { if (pointButton.Set == 1) { mode$ = "point"; } else if (nodeButton.Set == 1) { mode$ = "node"; } else if (lineButton.Set == 1) { mode$ = "line"; } else mode$ = "poly"; } # Callback for when user selects a different action. proc cbActionChanged() { if (scanButton.Set == 1) { action$ = "scan"; } else action$ = "add"; } # Callback for when user clicks the right mouse button on the polygon tool # (or clicks apply). proc cbToolApply(class PointTool pointTool) { # Clear the list list.DeleteAllItems(); # Set up local variables. local string url$, layerName$, temp$, temp2$, item$, element$, table$, field$, value$; local class FILE reffile; local class GRE_LAYER layer; local numeric numTok, i, j, num, start; local class POINT2D point; local class StatusHandle status; local class StatusContext context; layer = Group.ActiveLayer; # If the layer is a raster or vector, proceed with the script. if (layer.Type == "Raster" or layer.Type == "Vector") { # Check point. point.x = pointTool.Point.x; point.y = pointTool.Point.y; # Set up layer, object, layer name, and point transformations. if (layer.Type == "Raster") { point = TransPoint2D(point, ViewGetTransLayerToScreen(View, layer, 1)); DispGetRasterFromLayer(rv, layer); layerName$ = "[" + rv.$Info.Name + " : " + rv.$Info.Desc + "]"; } else if (layer.Type == "Vector") { point = TransPoint2D(point, ViewGetTransViewToScreen(View, 1)); point = TransPoint2D(point, ViewGetTransMapToView(View, layer.Projection, 1)); local class GRE_LAYER_VECTOR vl; vl = layer; DispGetVectorFromLayer(vv, layer); layerName$ = "[" + vv.$Info.Name + " : " + vv.$Info.Desc + "]"; } reffile = fopen(filepath$); # Find file entry. start = 0; while(!feof(reffile)) { url$ = fgetline$(reffile); start += 1; if (url$ == layerName$) break; } if (url$ != layerName$) list.AddItem("File not found!"); # Read lines for this file. url$ = ""; while(!feof(reffile)) { temp$ = fgetline$(reffile); url$ = url$ + temp$ + "\n"; if (temp$ == "") break; } # Close file. fclose(reffile); # Add non-specific URLs to the list. numTok = NumberTokens(url$, "\n\r"); for i = 1 to numTok { item$ = GetToken(url$, "\n\r", i); if (left$(item$, 1) == "{") break; if (!list.ItemExists(item$)) list.AddItem(item$); start += 1; } # Remove non-specific URLs from string. temp$ = ""; for j = i to numTok temp$ = temp$ + GetToken(url$, "\n\r", j) + "\n"; url$ = temp$; # If the string is empty, break out of the if statement. if (url$ == "") break; # Set loop variables. numeric add = false; numTok = NumberTokens(url$, "\n\r"); # If the layer is vector and an appropriate element exists or the layer is a raster # and the cell value is not null, search the rest of the string. if ((layer.Type == "Vector" and ((mode$ == "point" and vv.$Info.NumPoints > 0) or (mode$ == "node" and vv.$Info.NumNodes > 0) or (mode$ == "line" and vv.$Info.NumLines > 0) or (mode$ == "poly" and vv.$Info.NumPolys > 0))) or (layer.Type == "Raster" and !IsNull(rv[point.y, point.x]))) { # If the layer is a vector, find and highlight the closest element. numeric elementNum; if (layer.Type == "Vector") { if (mode$ == "point") { elementNum = FindClosestPoint(vv, point.x, point.y, GetLastUsedGeorefObject(vv)); vl.Point.HighlightSingle(elementNum); } else if (mode$ == "node") { elementNum = FindClosestNode(vv, point.x, point.y, GetLastUsedGeorefObject(vv)); vl.Node.HighlightSingle(elementNum); } else if (mode$ == "line") { elementNum = FindClosestLine(vv, point.x, point.y, GetLastUsedGeorefObject(vv)); vl.Line.HighlightSingle(elementNum); } else { elementNum = FindClosestPoly(vv, point.x, point.y, GetLastUsedGeorefObject(vv)); vl.Poly.HighlightSingle(elementNum); } } # Create a status bar so user knows the script is doing something. status = StatusDialogCreate(form); context = StatusContextCreate(status); StatusSetMessage(context, "Scanning File..."); for i = 1 to numTok { temp$ = GetToken(url$, "\n\r", i); # If the token ends in a } it is a raster value or vector element. if (right$(temp$, 1) == "}") { arr[i - 1] = 1; # If the last element was a URL, set add to false. if (i != 1) if (arr[i - 2] == 0) add = false; # If add is false, check if this point has the current raster value or is close # to the current vector element. if (!add) { if (layer.Type == "Raster") { if (rv[point.y, point.x] == StrToNum(GetToken(temp$, "{}", 1))) { arr[i - 1] = 2; add = true; } } else { element$ = GetToken(temp$, "{ }", 1); table$ = GetToken(temp$, "{ }", 2); field$ = GetToken(temp$, "{ }", 3); value$ = ""; num = NumberTokens(temp$, "{ }"); for j = 4 to num value$ = value$ + " " + GetToken(temp$, "{ }", j); value$ = right$(value$, strlen(value$) - 1); if (element$ == mode$) { if (mode$ == "point") { if (vv.point[elementNum].(table$).(field$)$ == value$) { add = true; arr[i - 1] = 2; } } else if (mode$ == "node") { if (vv.node[elementNum].(table$).(field$)$ == value$) { add = true; arr[i - 1] = 2; } } else if (mode$ == "line") { if (vv.line[elementNum].(table$).(field$)$ == value$) { add = true; arr[i - 1] = 2; } } else { if (vv.poly[elementNum].(table$).(field$)$ == value$) { add = true; arr[i - 1] = 2; } } } } } } # Add the url to the list if add is true and it is not on the list. else { arr[i - 1] = 0; if (add) if (!list.ItemExists(temp$)) list.AddItem(temp$); } } if (list.ItemCount == 0) list.AddItem("No URLs found!"); cbRedraw(); # Destroy the status bar. StatusContextDestroy(context); StatusDialogDestroy(status); # If the action is add, get URL from user. if (action$ == "add") { local class DATABASE db; if (layer.Type == "Raster") { item$ = PopupString(sprintf("Enter a URL to add for raster value %.2f:", rv[point.y, point.x])); j = 1; } else { if (mode$ == "point") { db = OpenVectorPointDatabase(vv); j = PopupSelectTableField(db, table$, field$); value$ = vv.point[elementNum].(table$).(field$)$; } else if (mode$ == "node") { db = OpenVectorPointDatabase(vv); j = PopupSelectTableField(db, table$, field$); value$ = vv.node[elementNum].(table$).(field$)$; } else if (mode$ == "line") { db = OpenVectorLineDatabase(vv); j = PopupSelectTableField(db, table$, field$); value$ = vv.line[elementNum].(table$).(field$)$; } else { db = OpenVectorPolyDatabase(vv); j = PopupSelectTableField(db, table$, field$); value$ = vv.poly[elementNum].(table$).(field$)$; } if (j > 0) item$ = PopupString(sprintf("Enter a URL to add for:\n Table: %s\n Field: %s\n Value: %s", table$, field$, value$)); } # If there was no error getting table and field information, continue. if (j > 0) { # Read file into string. url$ = ""; reffile = fopen(filepath$); while(!feof(reffile)) { temp$ = fgetline$(reffile); if (temp$ == "") temp$ = " "; url$ = url$ + temp$ + "\n"; } url$ = left$(url$, strlen(url$) - 1); fclose(reffile); # Add the URL to the appropriate location in the reference file. if (list.GetItemAtPos(1) != "File not found!") { temp$ = ""; for j = 1 to start { temp2$ = GetToken(url$, "\n\r", j); if (temp2$ == " ") temp2$ = ""; temp$ = temp$ + temp2$ + "\n"; } for j = 1 to numTok { temp$ = temp$ + GetToken(url$, "\n\r", j + start) + "\n"; if (j > 1) { if (arr[j - 1] == 2 and arr[j] == 0 and arr[j - 2] == 0) break; } else if (arr[j - 1] == 2 and arr[j] == 0) break; } if (j > numTok) { j -= 1; if (layer.Type == "Raster") { temp$ = temp$ + "{" + NumToStr(rv[point.y, point.x]) + "}" + "\n"; } else temp$ = temp$ + "{" + mode$ + " " + table$ + " " + field$ + " " + value$ + "}" + "\n"; } temp$ = temp$ + item$ + "\n"; num = NumberTokens(url$, "\n\r"); for j = j + start + 1 to num { temp2$ = GetToken(url$, "\n\r", j); if (temp2$ == " ") temp2$ = ""; temp$ = temp$ + temp2$ + "\n"; } url$ = left$(temp$, strlen(temp$) - 1); } else { if (layer.Type == "Raster") { temp$ = "{" + NumToStr(rv[point.y, point.x]) + "}"; } else temp$ = "{" + mode$ + " " + table$ + " " + field$ + " " + value$ + "}"; url$ = url$ + "\n\n" + layerName$ + "\n" + temp$ + "\n" + item$; } # This is currently the only way to to file modification other than overwriting data. # Create a new file, write to it, and delete the old one. RenameFile(filepath$, FileNameGetPath(filepath$) + FileNameGetName(filepath$) + ".old"); # Recreate file. reffile = fopen(filepath$); # Write to the new file. fwritestring(reffile, url$); # Close file. fclose(reffile); # Delete old file. DeleteFile(FileNameGetPath(filepath$) + FileNameGetName(filepath$) + ".old"); } } } else if (list.ItemCount == 0) list.AddItem("No element found!"); list.VisibleItemCount = list.ItemCount; if (list.VisibleItemCount > 10) list.VisibleItemCount = 10; } else list.AddItem("Type not supported!"); url$ = list.GetItemAtPos(1); if (url$ != "No URLs found!" and url$ != "Type not supported!" and url$ != "No element found!" and url$ != "File not found!") list.SelectPos(1); } # Called the first time the tool is activated. # If the tool implements a dialog it should be created (but not displayed) here. func OnInitialize () { WidgetAddCallback(Group.LayerSelectedCallback, cbLayer); # Set up dialog form = CreateFormDialog("Select a URL"); form.marginHeight = 2; form.marginWidth = 2; form.ResizePolicy = "RESIZE_ANY"; WidgetAddCallback(form.Shell.PopdownCallback, cbClose); # Drawing area for displaying raster name. da = CreateDrawingArea(form, 30, 350); da.leftWidget = form; da.topWidget = form; da.rightWidget = form; WidgetAddCallback(da.ExposeCallback, cbRedraw); # List for displaying urls. list = CreateScrolledList(form); listParent = list.parent; listParent.topWidget = da; listParent.leftWidget = form; listParent.rightWidget = form; # Label for vector modes. vectorLabel = CreateLabel(form, "Vector Mode:"); vectorLabel.topWidget = list; vectorLabel.leftWidget = form; vectorLabel.topOffset = 4; # Form for toggle buttons for vector mode. vectorModes = CreateRowColumn(form, 4); vectorModes.topWidget = list; vectorModes.leftWidget = vectorLabel; vectorModes.rightWidget = form; vectorModes.RadioBehavior = 1; # Button to select points. pointButton = CreateToggleButton(vectorModes, "Points"); WidgetAddCallback(pointButton.ValueChangedCallback, cbModeChanged); # Button to select points. nodeButton = CreateToggleButton(vectorModes, "Nodes"); WidgetAddCallback(nodeButton.ValueChangedCallback, cbModeChanged); # Button to select points. lineButton = CreateToggleButton(vectorModes, "Lines"); WidgetAddCallback(lineButton.ValueChangedCallback, cbModeChanged); # Button to select points. polyButton = CreateToggleButton(vectorModes, "Polygons"); WidgetAddCallback(polyButton.ValueChangedCallback, cbModeChanged); # Separator between vector modes and actions. line1 = CreateHorizontalSeparator(form); line1.topWidget = vectorModes; line1.leftWidget = form; line1.rightWidget = form; # Label for actions. actionLabel = CreateLabel(form, "Action:"); actionLabel.topWidget = line1; actionLabel.leftWidget = form; actionLabel.topOffset = 4; # Form for toggle buttons for options. actions = CreateRowColumn(form, 3); actions.topWidget = line1; actions.leftWidget = actionLabel; actions.rightWidget = form; actions.RadioBehavior = 1; # Button to select scan. scanButton = CreateToggleButton(actions, "Scan"); WidgetAddCallback(scanButton.ValueChangedCallback, cbActionChanged); # Button to select add. addButton = CreateToggleButton(actions, "Add"); WidgetAddCallback(addButton.ValueChangedCallback, cbActionChanged); # Separator between actions and command buttons. line2 = CreateHorizontalSeparator(form); line2.topWidget = actions; line2.leftWidget = form; line2.rightWidget = form; # Button to open reference file. openButton = CreatePushButtonItem("Open File...", cbOpen); # Button to launch browser. urlButton = CreatePushButtonItem("Launch Browser", cbGo); # Button to close polyTool. closeButton = CreatePushButtonItem("Close", cbClose); # Row of buttons. buttonRow = CreateButtonRow(form, openButton, urlButton, closeButton); buttonRow.topWidget = line2; buttonRow.leftWidget = form; buttonRow.rightWidget = form; # Add point tool. pointTool = ViewCreatePointTool(View); ToolAddCallback(pointTool.ActivateCallback, cbToolApply); # Default file path. filepath$ = _context.ScriptDir + "url.txt"; } # end of OnInitialize # Called when tool is to be destroyed, will not be called if tool was never activated. # If the tool implements a dialog it should be destroyed here. func OnDestroy () { cbClose(); DestroyGC(gc); DestroyWidget(form); } # end of OnDestroy # Called when tool is activated. # If the polyTool implements a dialog it should be "managed" (displayed) here. func OnActivate () { # Reset everything to default. list.DeleteAllItems(); list.VisibleItemCount = 1; pointButton.Set = 0; nodeButton.Set = 0; lineButton.Set = 0; polyButton.Set = 1; mode$ = "poly"; scanButton.Set = 1; addButton.Set = 0; action$ = "scan"; if (Group.ActiveLayer.Type == "Vector") Group.ActiveLayer.UnhighlightAllElements(); DialogOpen(form); pointTool.Managed = 1; pointTool.HasPosition = 0; # Find reference file if (!fexists(filepath$)) cbOpen(); if (gc == 0) gc = CreateGCForDrawingArea(da); cbLayer(); setDefaultWhenClose = true; } # end of OnActivate # Called when tool is deactivated (usually when switching to another tool). # If the tool implements a dialog it should be "unmanaged" (hidden) here. func OnDeactivate () { setDefaultWhenClose = false; cbClose(); } # end of OnDeactivate