Libreoffice Calc run macro with HYPERLINK - macros

I'm trying to use hyperlinks instead of buttons to run Basic macros. It seems to be more natural to me because hyperlinks are directly connected to a cell and buttons are not.
I'm using the following Formula:
=HYPERLINK("vnd.sun.star.script:Standard.Module1.Test?language=Basic&location=document";"Check")
It should call the Subroutine Test placed in the document's macros under Standard.Module1 and display the Text 'Check' in the Cell it is written.
This works absolutely fine with libreoffice 3.6.1.2 but it doesn't work at all with version 4.1.4.2. I can't see any errors it just happens nothing at all. I tried to simply click the Hyperlink and also to hold CTRL and click it. Same result - nothing.
When I use a button the macro works as expected.
Does anyone know how to solve this problem?

This seems to be a bug in Calc. The protocol vnd.sun.star.script runs in hyperlink URLs in Writer still in version 4.2. But in Calc it runs not.
As a workaround you could have the following function attached to the sheet event "Double click". Then the macro runs if you double click the cell with the =HYPERLINK formula.
The last two versions are the results of my first ideas. I will let them in the answer because of comprehensibility reasons. But this last version is the best workaround in my opinion. It will closest work like the original vnd.sun.star.script: URL.
public function Doubelclicked(target) as Boolean
if left(target.formula, 32) = "=HYPERLINK(""vnd.sun.star.script:" then
sFormulaHyperlink = target.formula
sMacroURLRaw = mid(sFormulaHyperlink, 13, instr(13, sFormulaHyperlink, ";") - 13)
target.formula = "=""" & sMacroURLRaw
sMacroURL = target.string
target.formula = sFormulaHyperlink
oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
dim args(0) as new com.sun.star.beans.PropertyValue
args(0).Name = "URL"
args(0).Value = sMacroURL
oFrame = ThisComponent.CurrentController.Frame
oDisp.executeDispatch(oFrame, sMacroURL, "", 0, args)
end if
Doubelclicked = false
end function
Here are the previous versions:
public function Doubelclicked(target) as Boolean
if left(target.formula, 32) = "=HYPERLINK(""vnd.sun.star.script:" then
sMacroURL = mid(target.formula, 13, instr(13, target.formula, chr(34))-13)
oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
oFrame = ThisComponent.CurrentController.Frame
oDisp.executeDispatch(oFrame, sMacroURL, "", 0, Array())
end if
Doubelclicked = false
end function
With this it is not possible to pass parameters in the macro URL. But if it only is the goal to get the address of the cell from which the macro was called, then this is possible because we have the target of the double click. So i have updated my workaround.
public function Doubelclicked(target) as Boolean
if left(target.formula, 32) = "=HYPERLINK(""vnd.sun.star.script:" then
lStartLocation = instr(13, target.formula,"&location=")
if lStartLocation > 0 then
lEndLocation = instr(lStartLocation + 1, target.formula,"&")
if lEndLocation = 0 then lEndLocation = instr(lStartLocation + 1, target.formula,"""")
sMacroURL = mid(target.formula, 13, lEndLocation - 13)
'msgbox sMacroURL
oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
dim args(2) as new com.sun.star.beans.PropertyValue
args(0).Name = "TargetAddress"
args(0).Value = target.AbsoluteName
oFrame = ThisComponent.CurrentController.Frame
oDisp.executeDispatch(oFrame, sMacroURL, "", 0, args)
end if
end if
Doubelclicked = false
end function
Greetings
Axel

Related

Updated Yahoo Weather API - .NET

I'm trying to implement the newest yahoo weather API in a .NET application (the one we were using was discontinued in favor of this new one): https://developer.yahoo.com/weather/documentation.html#commercial
Their only examples are in PHP and Java. I've done my best to convert the Java example to .NET but I still am getting a "401 - Unauthorized" response. I've gone over my code several times and cannot find any problems so I'm hoping someone else will be able to see where I went wrong. Code is below.
Private Sub _WeatherLoader_DoWork(sender As Object, e As DoWorkEventArgs)
Try
Dim oauth As New OAuth.OAuthBase
Dim forecastRssResponse As query
Dim appId As String = My.Settings.YahooAppID
Dim consumerKey As String = My.Settings.YahooAPIConsumerKey
Dim yahooUri As String = String.Format("{0}?location=billings,mt&format=xml", YAHOO_WEATHER_API_BASE_ENDPOINT)
Dim oAuthTimestamp As Integer = oauth.GenerateTimeStamp()
Dim oAuthNonce As String = oauth.GenerateNonce()
Dim parameters As New List(Of String)
Try
parameters.Add(String.Format("oauth_consumer_key={0}", consumerKey))
parameters.Add(String.Format("oauth_nonce={0}", oAuthNonce))
parameters.Add("oauth_signature_method=HMAC-SHA1")
parameters.Add(String.Format("oauth_timestamp={0}", oAuthTimestamp.ToString()))
parameters.Add("oauth_version=1.0")
' Encode the location
parameters.Add(String.Format("location={0}", HttpUtility.UrlEncode("billings,mt", Encoding.UTF8)))
parameters.Add("format=xml")
' Sort parameters ascending
parameters = parameters.OrderBy(Function(item) item).ToList()
Dim i As Integer = 0
Dim builder As New StringBuilder()
Do While (i < parameters.Count())
builder.Append(String.Format("{0}{1}", If(i > 0, "&", String.Empty), parameters(i)))
i += 1
Loop
Dim signatureString As String = String.Format("GET&{0}&{1}", HttpUtility.UrlEncode(YAHOO_WEATHER_API_BASE_ENDPOINT, Encoding.UTF8), HttpUtility.UrlEncode(builder.ToString(), Encoding.UTF8))
Dim oAuthSignature As String = _CreateOauthSignature(signatureString)
Dim authorizationLine As String = String.Format("OAuth oauth_consumer_key={0}, oauth_nonce={1}, oauth_timestamp={2}, oauth_signature_method=HMAC-SHA1, oauth_signature={3}, oauth_version=1.0", consumerKey, oAuthNonce, oAuthTimestamp, oAuthSignature)
Dim forecastRequest As WebRequest = WebRequest.Create(yahooUri)
forecastRequest.Headers.Add("Authorization", authorizationLine)
forecastRequest.Headers.Add("Yahoo-App-Id", appId)
' Cast to HttpWebRequest to set ContentType through property
CType(forecastRequest, HttpWebRequest).ContentType = "text/xml"
Dim forecastResponse As WebResponse = forecastRequest.GetResponse()
If forecastResponse IsNot Nothing Then
Using responseStream As Stream = forecastResponse.GetResponseStream()
Dim rssDoc As New XmlDocument()
rssDoc.Load(responseStream)
forecastRssResponse = rssDoc.OuterXml().FromXml(Of query)()
End Using
e.Result = forecastRssResponse
End If
Catch ex As Exception
e.Result = Nothing
LoadingManually = False
End Try
Catch ex As Exception
modMain.SendDevErrorEmail(ex, "_WeatherLoader_DoWork in WeatherWidget", "Catch around dowork code in fired from refresh timer event in wether widget")
e.Result = Nothing
LoadingManually = False
End Try
End Sub
Private Function _CreateOauthSignature(baseInfo As String) As String
Dim secretKey As String = String.Format("{0}&", My.Settings.YahooAPIConsumerSecretKey)
Dim encoding As New System.Text.ASCIIEncoding()
Dim keyBytes As Byte() = encoding.GetBytes(secretKey)
Dim messageBytes As Byte() = encoding.GetBytes(baseInfo)
Dim hashMessage As Byte()
Using hmac As New HMACSHA1(keyBytes)
hashMessage = hmac.ComputeHash(messageBytes)
End Using
Return Convert.ToBase64String(hashMessage)
End Function
After painstakingly creating a Java app, pasting in the Java example and stepping through it I found that the issue is in a poorly implemented URL Decode function on the receiving end.
In the Java app, URL Encode uses upper case characters while in .NET HTTPUtility.URLEncode uses lower case characters. This is enough to throw off your signature and cause a 401 - Unauthorized error.
My solution was to create a string extension method that will URL Encode in upper case:
<Extension>
Public Function UppercaseURLEncode(ByVal sourceString As String) As String
Dim temp As Char() = HttpUtility.UrlEncode(sourceString).ToCharArray()
For i As Integer = 0 To temp.Length - 2
If temp(i).ToString().Equals("%", StringComparison.OrdinalIgnoreCase) Then
temp(i + 1) = Char.ToUpper(temp(i + 1))
temp(i + 2) = Char.ToUpper(temp(i + 2))
End If
Next
Return New String(temp)
End Function
Using this extension method my signature gets created exactly like the one in the Java app and I am able to retrieve the response.
Hope this helps other .net programmers with this issue!

EPPlus chart(pie,barchart) selected(B2,B36,B38) .. etc excel cells

I have similar to the link below problem.
EPPlus chart from list of single excel cells. How?
I tried the code but it shows it twice in the chart. For example:
This code show excel chart -> select data-> horizontal(category) axis labels tab you show 100,100,300,600 write. What is the reason for this? The chart is written twice the first data I did not find a solution to the problem.
I think you just discovered a bug with EPPlus. Shame on me for not noticing that with that post you reference. It seems that when using the Excel union range selector (the cell names separated by commas) the iterator for the ExcelRange class returns a double reference to the first cell, in this case B2.
A work around would be to use the other overload for Series.Add which will take two string ranges. Here is a unit test that show the problem and the workaround:
[TestMethod]
public void Chart_From_Cell_Union_Selector_Bug_Test()
{
var existingFile = new FileInfo(#"c:\temp\Chart_From_Cell_Union_Selector_Bug_Test.xlsx");
if (existingFile.Exists)
existingFile.Delete();
using (var pck = new ExcelPackage(existingFile))
{
var myWorkSheet = pck.Workbook.Worksheets.Add("Content");
var ExcelWorksheet = pck.Workbook.Worksheets.Add("Chart");
//Some data
myWorkSheet.Cells["A1"].Value = "A";
myWorkSheet.Cells["A2"].Value = 100; myWorkSheet.Cells["A3"].Value = 400; myWorkSheet.Cells["A4"].Value = 200; myWorkSheet.Cells["A5"].Value = 300; myWorkSheet.Cells["A6"].Value = 600; myWorkSheet.Cells["A7"].Value = 500;
myWorkSheet.Cells["B1"].Value = "B";
myWorkSheet.Cells["B2"].Value = 300; myWorkSheet.Cells["B3"].Value = 200; myWorkSheet.Cells["B4"].Value = 1000; myWorkSheet.Cells["B5"].Value = 600; myWorkSheet.Cells["B6"].Value = 500; myWorkSheet.Cells["B7"].Value = 200;
//Pie chart shows with EXTRA B2 entry due to problem with ExcelRange Enumerator
ExcelRange values = myWorkSheet.Cells["B2,B4,B6"]; //when the iterator is evaluated it will return the first cell twice: "B2,B2,B4,B6"
ExcelRange xvalues = myWorkSheet.Cells["A2,A4,A6"]; //when the iterator is evaluated it will return the first cell twice: "A2,A2,A4,A6"
var chartBug = ExcelWorksheet.Drawings.AddChart("Chart BAD", eChartType.Pie);
chartBug.Series.Add(values, xvalues);
chartBug.Title.Text = "Using ExcelRange";
//Pie chart shows correctly when using string addresses and avoiding ExcelRange
var chartGood = ExcelWorksheet.Drawings.AddChart("Chart GOOD", eChartType.Pie);
chartGood.SetPosition(10, 0, 0, 0);
chartGood.Series.Add("Content!B2,Content!B4,Content!B6", "Content!A2,Content!A4,Content!A6");
chartGood.Title.Text = "Using String References";
pck.Save();
}
}
Here is the output:
I will post it as an issue on their codeplex page to see if they can get it fixed for the next release.

copy only cell format (no value)

I'd like copy only format (no value) from cell range (L3:L10) to cell range (H10:H11).
With Excel is easy:
Sheets(sheet1).Range("L3:L10").Select
Selection.Copy
Sheets(sheet1).Range("H10:H11").Select
Selection.PasteSpecial Paste:=xlFormats, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
But with LibreOffice?
Can you help me?
I ran the macro recorder, and it generated this:
Sub PasteFormatting
dim document as object
dim dispatcher as object
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "ToPoint"
args1(0).Value = "$L$3:$L$10"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args1())
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())
dim args3(0) as new com.sun.star.beans.PropertyValue
args3(0).Name = "ToPoint"
args3(0).Value = "$H$10:$H$11"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args3())
dim args4(5) as new com.sun.star.beans.PropertyValue
args4(0).Name = "Flags"
args4(0).Value = "T"
args4(1).Name = "FormulaCommand"
args4(1).Value = 0
args4(2).Name = "SkipEmptyCells"
args4(2).Value = false
args4(3).Name = "Transpose"
args4(3).Value = false
args4(4).Name = "AsLink"
args4(4).Value = false
args4(5).Name = "MoveMode"
args4(5).Value = 6
dispatcher.executeDispatch(document, ".uno:InsertContents", "", 0, args4())
End Sub
It works, although the code is ugly, as is to be expected of dispatcher code. The ranges you asked for are different sizes, so it generates a warning. It would be easy to fix this by simply using "$L$3:$L$4" as the source range.
API code would be much shorter and cleaner. For an example using XTransferableSupplier, see openoffice: duplicating rows of a table in writer. However, it may not be possible to paste only formatting with XTransferable.

How to get the name of checkbox that I checked in libreoffice calc (macro)?

I have more than 40 check-boxes in a single calc sheet, and I don't want to code each one of them. I just want a clear working code to get the name of checkbox.
In this program I have to manually type the name for the check-box within the macro code:
A="CheckBox1"
This is all I have so far:
Sub Marco1
Dim ocheckbox1
Dim oForm
Dim A
A="CheckBox1"
oForm = ThisComponent.Sheets(0).DrawPage.Forms.getByIndex(0)
ocheckbox1 = oForm.getByName(A)
if ocheckbox1.State = "0" then
if MsgBox ("Are you sure ?Note: It can't be re-edited", 292) = 6 then
ocheckbox1.Label = "Checked"
ocheckbox1.Enabled="False"
else
ocheckbox1.Label = "Not Checked"
End if
End if
End Sub
Assuming the macro is triggered by interaction with the checkbox:
Sub Macro1 (oEvent As Object)
Dim oCheckbox1 As Object
Dim sCheckbox1Name As String
oCheckbox1 = oEvent.source.model
sCheckbox1Name = oCheckbox1.Name
End Sub

How do I add my shopping cart items to Transaction.item_list_.items

I am trying to pass over my shopping cart items to paypal using the .net REST API SDK
Below is the code:
Dim b As Integer = 0
Dim pCartItem As New PayPal.Api.Payments.Item
Dim pCartItemList As New PayPal.Api.Payments.ItemList
Do While b <= cart.Count - 1
pCartItem.name = cart(b).Name
pCartItem.price = cart(b).Price
pCartItem.quantity = cart(b).Quantity
pCartItem.currency = "USD"
pCartItem.sku = cart(b).Sku
pCartItemList.items.Add(pCartItem) 'This line errors out
pCartItem = New PayPal.Api.Payments.Item
b += 1
Loop
orderTransaction.item_list = pCartItemList
The line that errors out pCartItemList.items.Add(pCartItem) throws the error "Object reference not set to the instance of an object. Try using the keyword "new" :. I don't understand why because when i hover over the pCartItem in Visual Studio I am able to see all the correct values that have been assigned from my shopping cart.
Is this even the right way to add Item objects to the Transacation.item_list ?
Any help would be greatly appreciated.
Thanks,
Tommy
I have figured out that error for anyone else who may run into this here is the code that works
Dim pCartItem As PayPal.Api.Payments.Item = New PayPal.Api.Payments.Item
orderTransaction.item_list = New PayPal.Api.Payments.ItemList
orderTransaction.item_list.items = New List(Of PayPal.Api.Payments.Item)
Do While b <= cart.Count - 1
pCartItem.name = cart(b).Name
pCartItem.price = cart(b).Price
pCartItem.quantity = cart(b).Quantity
pCartItem.currency = "USD"
pCartItem.sku = cart(b).Sku
orderTransaction.item_list.items.Add(pCartItem)
pCartItem = New PayPal.Api.Payments.Item
b += 1
Loop