388 lines
14 KiB
388 lines
14 KiB
# -*- tcl -*- |
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'dev make' or src/make.tcl to update from <pkg>-buildversion.txt |
|
# |
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
|
# (C) 2023 |
|
# |
|
# @@ Meta Begin |
|
# Application winlibreoffice 0.1.0 |
|
# Meta platform tcl |
|
# Meta license <unspecified> |
|
# @@ Meta End |
|
|
|
|
|
|
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
|
## Requirements |
|
##e.g package require frobz |
|
package require uri ;#tcllib |
|
package require punk::lib |
|
|
|
#windows? REVIEW - can we provide a common api for other platforms with only script? tcluno instead? |
|
|
|
if {"windows" eq $::tcl_platform(platform)} { |
|
if {[catch {package require twapi}]} { |
|
puts stderr "Twapi package required for winlibreoffice to function" |
|
puts stderr "Minimal functionality - only some utils may work" |
|
} |
|
} else { |
|
puts stderr "Package requires twapi. No current equivalent on non-windows platform. Try tcluno http://sf.net/projets/tcluno" |
|
puts stderr "Minimal functionality - only some utils may work" |
|
} |
|
|
|
|
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
|
namespace eval winlibreoffice { |
|
namespace export from_libre_date to_libre_date |
|
#--- |
|
#todo: investigate tcluno package http://sf.net/projects/tcluno |
|
#CPlusPlus - platforms? |
|
#--- |
|
# |
|
|
|
#enable 1 |
|
variable datebase "1899-12-30" ;#libreoffice default in options->LibreOfifce Calc->Calculate |
|
#variable datebase "1900-01-01" ;#StarCalc 1.0 |
|
#variable datebase "1904-01-01" ;# ??? |
|
|
|
#sometimes a com object may support $obj -print |
|
#see also |
|
# $obj -destroy |
|
# $obj Quit |
|
# $collection -iterate ?options? varname script |
|
|
|
variable uno "" ;# service manager object |
|
variable psm "" ;# process service manager |
|
|
|
# -- --- --- --- |
|
# libreoffice functions |
|
proc getServiceManager {} { |
|
variable uno |
|
if {$uno eq ""} { |
|
set uno [twapi::comobj com.sun.star.ServiceManager] |
|
} |
|
return $uno |
|
} |
|
#uno getAvailableServiceNames |
|
|
|
#e.g com.sun.star.beans.Introspection |
|
# com.sun.star.ucb.SimpleFileAccess |
|
proc createUnoService {objname} { |
|
[getProcessServiceManager] createInstance $objname |
|
} |
|
proc getProcessServiceManager {} { |
|
variable psm |
|
if {$psm eq ""} { |
|
set svcmgr [getServiceManager] |
|
#set psm [$svcmgr getProcessServiceManager] |
|
#seems to be same object? - it has createInstance anyway REVIEW |
|
set psm $svcmgr |
|
} |
|
return $psm |
|
} |
|
|
|
#what does libreoffice accept for this fun.. local file paths only? |
|
proc convertToUrl {fpath} { |
|
if {![string match "file:/*" $fpath]} { |
|
# this turns //server/blah to file:////server/blah - which is probably nonsense |
|
set fpath [uri::join scheme file path $fpath] |
|
} |
|
return $fpath |
|
} |
|
|
|
# |
|
proc convertFromUrl {fileuri} { |
|
if {[string match "file:/*" $fileuri]} { |
|
set finfo [uri::split $fileuri] |
|
if {"windows" eq $::tcl_platform(platform)} { |
|
if {[dict exists $finfo host]} { |
|
return "//${host}${path}" |
|
} else { |
|
#the leading slash in path indicates a local path and we strip on windows |
|
set p [dict get $finfo path] |
|
if {[string index $p 0] eq "/"} { |
|
set p [string range $p 1 end] |
|
} |
|
return $p |
|
} |
|
} else { |
|
if {[dict exists $finfo host]} { |
|
#?? review - how are file uris to other hosts handled? |
|
error "convertFromUrl doesn't handle non-local file uris on this platform" |
|
} else { |
|
return [dict get $finfo path] |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
# -- --- --- --- |
|
# custom functions |
|
proc get_desktop {} { |
|
set uno [getServiceManager] |
|
set ctx [$uno getPropertyValue "DefaultContext"] |
|
set dt [$ctx getByName /singletons/com.sun.star.frame.theDesktop] |
|
#$dt setName odk_officedev_desk |
|
#$dt getName |
|
return $dt |
|
} |
|
|
|
proc blankdoc {{type scalc}} { |
|
set known_types [list scalc swriter simpress sdraw smath] |
|
if {$type ni $known_types} { |
|
puts stderr "Warning: unknown type $type. (known types: $known_types) will try anyway - private:factory/$type" |
|
} |
|
set dt [get_desktop] |
|
set doc [$dt loadComponentFromUrl "private:factory/$type" "_blank" 0 ""] ;#doesn't work without final param - empty string seems to work |
|
puts "doc title: [$doc Title]" |
|
#title can be set with [$doc settitle "titletext"] |
|
return $doc |
|
} |
|
|
|
proc file_open_dialog {{title "pick a libreoffice file"}} { |
|
set filepicker [createUnoService "com.sun.star.ui.dialogs.FilePicker"] |
|
$filepicker Title $title |
|
set result [$filepicker Execute] |
|
if {$result} { |
|
#set files [$filepicker getSelectedFiles] |
|
# -iterate ? |
|
# return files(0) ? |
|
|
|
#e.g file:///C:/Users/sleek/test.txt |
|
return [$filepicker getFiles] |
|
} else { |
|
return "" |
|
} |
|
} |
|
|
|
#todo oo interface? |
|
proc calcdoc_sheets_by_index {doc idx} { |
|
set sheets [$doc getSheets] |
|
set s [$sheets getByIndex $idx] |
|
puts stdout "Sheet: [$s getName]" |
|
#set name with [$s setName "xxx"] |
|
return $s |
|
} |
|
proc calcsheet_cell_range_by_name {sheet rangename} { |
|
return [$sheet getCellRangeByName $rangename] ;#e.g A1 |
|
} |
|
proc calccell_setString {cell str} { |
|
$cell setString $str |
|
} |
|
proc calccell_setValue {cell value} { |
|
$cell setValue $value |
|
} |
|
proc calccell_setPropertyValue {cell propset} { |
|
$cell setPropertyValue {*}$propset |
|
#e.g "NumberFormat" 49 |
|
# YYYY-MM-DD |
|
|
|
#can also use in this case [$cell NumberFormat] |
|
} |
|
|
|
proc calccell_setCellBackColorRGB {cell rgb} { |
|
set rgb [string trim $rgb #] |
|
set dec [punk::lib::hex2dec $rgb] |
|
$cell setPropertyValue "CellBackColor" [expr {$dec}] ;#colour value must be integer - will fail if string |
|
} |
|
proc calccell_setCharColorRGB {cell rgb} { |
|
set rgb [string trim $rgb #] |
|
set dec [punk::lib::hex2dec $rgb] |
|
$cell setPropertyValue "CharColor" [expr {$dec}] |
|
} |
|
|
|
|
|
#cell charFontName |
|
|
|
#cell charWeight |
|
#com.sun.star.awt.FontWeight |
|
#https://api.libreoffice.org/docs/idl/ref/FontWeight_8idl.html |
|
# values are listed with 6 DPs - but one seems to work |
|
# only setting to normal and bold seem to result in a value (regular & bold) in the format->font style dialog for the cell. |
|
#DONTKNOW 0.0 |
|
#THIN 50.0 |
|
#ULTRALIGHT 60.0 |
|
#LIGHT 75.0 |
|
#SEMILIGHT 90.0 |
|
#NORMAL 100.0 |
|
#SEMIBOLD 110.0 |
|
#BOLD 150.0 |
|
#ULTRABOLD 175.0 |
|
#BLACK 200.0 |
|
|
|
lappend PUNKARGS [list { |
|
@id -id ::winlibreoffice::to_libre_date |
|
@cmd -name winlibreoffice::to_libre_date -help\ |
|
"Return an internal Libre Office date/time floating point |
|
number representing the number of days between 1899-12-30 |
|
and the supplied time. |
|
|
|
e.g |
|
% to_libre_date 2025-02-28T060000 |
|
45716.25 |
|
% to_libre_date 2025-01-01T060101 |
|
45658.250706018516 |
|
" |
|
@opts |
|
-timezone -default "" -help\ |
|
"If unspecified, the timezone will be the |
|
current time zone on the system" |
|
@values -min 1 -max 1 |
|
time -type string -help\ |
|
"A unix timestamp as output by 'clock seconds' |
|
or a text timestamp such as 2025-01-03T000000 |
|
parseable by 'clock scan'" |
|
}] |
|
|
|
proc to_libre_date {args} { |
|
package require punk::args |
|
set argd [punk::args::parse $args withid ::winlibreoffice::to_libre_date] |
|
lassign [dict values $argd] leaders opts values received |
|
|
|
set tz [dict get $opts -timezone] |
|
set time [dict get $values time] |
|
if {![string is integer -strict $time]} { |
|
set ts [clock scan $time -timezone $tz] |
|
} else { |
|
set ts $time |
|
} |
|
|
|
variable datebase |
|
set tbase [clock scan $datebase -timezone $tz] |
|
package require punk::timeinterval |
|
set info [punk::timeinterval::difference -maxunit days -timezone $tz $tbase $ts] |
|
lassign [dict values $info] _Y _m days h m s |
|
|
|
return [expr {$days + ((($h *3600) + ($m * 60) + $s)/86400.0)}] |
|
} |
|
|
|
lappend PUNKARGS [list { |
|
@id -id ::winlibreoffice::from_libre_date |
|
@cmd -name winlibreoffice::from_libre_date -help\ |
|
"Convert an internal Libre Office date floating point value |
|
representing the number of days since 1899-12-30 to a format |
|
understood by Tcl such as 'clock seconds', 'clock milliseconds' |
|
as specified in the -format option. |
|
" |
|
@opts |
|
-format -default "clockseconds" -choices {clockseconds clockmillis ISO8601} -choicerestricted 0 -help\ |
|
"Aside from the special values listed -format accepts a format string |
|
as accepted by the Tcl 'clock format' command's -format option." |
|
-timezone -default "" -help\ |
|
"If unspecified, the timezone will be the |
|
current time zone on the system" |
|
@values -min 1 -max 1 |
|
libredatetime -type float -help\ |
|
"Floating point number representing the number of |
|
days since 1899-12-30." |
|
}] |
|
#review - we don't expect sci notation for any float values here |
|
#but we could easily get them.. e.g 0.000000001 * 86400.0 => 8.64e-5 |
|
#todo - clockmicros ? |
|
proc from_libre_date {args} { |
|
package require punk::args |
|
set argd [punk::args::parse $args withid ::winlibreoffice::from_libre_date] |
|
lassign [dict values $argd] leaders opts values received |
|
set format [dict get $opts -format] |
|
set tz [dict get $opts -timezone] |
|
set libredatetime [dict get $values libredatetime] |
|
|
|
variable datebase |
|
set tbase [clock scan $datebase -timezone $tz] |
|
set intdays [expr {int($libredatetime)}] |
|
set fracdays [lindex [split $libredatetime .] 1] |
|
if {$fracdays ne ""} { |
|
set fracdays "0.$fracdays" |
|
set floatsecs [expr {$fracdays * 86400.0}] ;#assuming not a leap-second day |
|
if {$format eq "clockmillis"} { |
|
set wholesecs [expr {int($floatsecs)}] |
|
set msfrac [lindex [split $floatsecs .] 1] |
|
if {$msfrac ne ""} { |
|
set msfrac "0.$msfrac" ;#could also be something like 0.64e-5 which should still work |
|
set ms [expr {round(1000 * $msfrac)}] |
|
if {$ms == 1000} { |
|
set ms 0 |
|
incr wholesecs |
|
} |
|
} else { |
|
set ms 0 |
|
} |
|
} else { |
|
set wholesecs [expr {round($floatsecs)}] |
|
set ms 0 |
|
} |
|
} else { |
|
set wholesecs 0 |
|
set ms 0 |
|
} |
|
|
|
set cs [clock add $tbase +$intdays days +$wholesecs seconds -timezone $tz] |
|
switch -- $format { |
|
clockseconds { |
|
return $cs |
|
} |
|
clockmillis { |
|
return [expr {($cs * 1000) + $ms}] |
|
} |
|
ISO8601 { |
|
set format "%Y%m%dT%H%M%S" |
|
} |
|
} |
|
return [clock format $cs -format $format] |
|
} |
|
|
|
#time is represented on a scale of 0 to 1 6:00am = 0.25 (24/4) |
|
|
|
|
|
#return libreoffice date as a floating point number of days since 1899.. (1899-12-30) |
|
proc to_libre_date_from_clockseconds_gmt {cs} { |
|
return [expr {($cs/86400.0) + 25569}] |
|
} |
|
|
|
#see also: https://wiki.tcl-lang.org/page/Tcom+examples+for+Microsoft+Outlook |
|
# this also uses days since 1899 (but 31 dec?) and uses a fixed base_offset of 36526 (for 2000-01-01) - this might be a better approach than using punk::timeinterval anyway |
|
# it seems to match libreoffice very closely (if not exact?) REVIEW |
|
# wher val is days since 1899 |
|
proc msdate_to_iso {val} { |
|
set base_ticks [clock scan 20000101] |
|
set base_offset 36526;# days since 31. Dec 1899, ARRRGGHHHHH |
|
set offset [expr {int($val)-$base_offset}] |
|
set clkdate [clock scan "$offset days" -base $base_ticks] |
|
set isodate [clock format $clkdate -format %Y%m%d] |
|
set fhours [expr {24.0*($val-int($val))}] |
|
set hours [expr {int($fhours)}] |
|
set mins [expr {int(($fhours-$hours)*60)}] |
|
#date<sp>H:m is valid iso but not if space replaced with T - then would need seconds too |
|
return "${isodate} $hours:$mins" |
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
|
# register namespace(s) to have PUNKARGS,PUNKARGS_aliases variables checked |
|
# ----------------------------------------------------------------------------- |
|
# variable PUNKARGS |
|
# variable PUNKARGS_aliases |
|
namespace eval ::punk::args::register { |
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
|
lappend ::punk::args::register::NAMESPACES ::winlibreoffice |
|
} |
|
|
|
|
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
|
## Ready |
|
package provide winlibreoffice [namespace eval winlibreoffice { |
|
variable version |
|
set version 0.1.0 |
|
}] |
|
return
|
|
|