xref: /trunk/test/odf-validate/fixtures/calc/make.py (revision 0e16a31d78a652c56c5c0fc79c752f48b9048df9)
1*0e16a31dSJim Jagielski#**************************************************************
2*0e16a31dSJim Jagielski#
3*0e16a31dSJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*0e16a31dSJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*0e16a31dSJim Jagielski#  distributed with this work for additional information
6*0e16a31dSJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*0e16a31dSJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*0e16a31dSJim Jagielski#  "License"); you may not use this file except in compliance
9*0e16a31dSJim Jagielski#  with the License.  You may obtain a copy of the License at
10*0e16a31dSJim Jagielski#
11*0e16a31dSJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*0e16a31dSJim Jagielski#
13*0e16a31dSJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*0e16a31dSJim Jagielski#  software distributed under the License is distributed on an
15*0e16a31dSJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*0e16a31dSJim Jagielski#  KIND, either express or implied.  See the License for the
17*0e16a31dSJim Jagielski#  specific language governing permissions and limitations
18*0e16a31dSJim Jagielski#  under the License.
19*0e16a31dSJim Jagielski#
20*0e16a31dSJim Jagielski# usage: runpy.sh fixtures/calc/make.py <port> <outdir>
21*0e16a31dSJim Jagielski# Calc documents for the Task 12d gap list: validations of every type and operator, and the other table: constructs.
22*0e16a31dSJim Jagielski# Expectations for fixtures/calc/verify.py go to <outdir>/../work/ (the checkers treat every file of <outdir> as a package).
23*0e16a31dSJim Jagielskiimport sys, os, json, uno
24*0e16a31dSJim Jagielskifrom unohelp import connect, desktop, pv
25*0e16a31dSJim Jagielski
26*0e16a31dSJim Jagielskiport, out = int(sys.argv[1]), os.path.abspath(sys.argv[2])
27*0e16a31dSJim Jagielskios.makedirs(out, exist_ok=True)
28*0e16a31dSJim Jagielskiwork = os.path.join(os.path.dirname(out), "work")
29*0e16a31dSJim Jagielskios.makedirs(work, exist_ok=True)
30*0e16a31dSJim JagielskiCTX = connect(port)
31*0e16a31dSJim JagielskiSM = CTX.ServiceManager
32*0e16a31dSJim Jagielskidesk = desktop(CTX)
33*0e16a31dSJim Jagielskifailed = []
34*0e16a31dSJim Jagielskiexpect = {}
35*0e16a31dSJim Jagielski
36*0e16a31dSJim Jagielski
37*0e16a31dSJim Jagielskidef E(t, n):
38*0e16a31dSJim Jagielski    return uno.Enum("com.sun.star.sheet." + t, n)
39*0e16a31dSJim Jagielski
40*0e16a31dSJim Jagielski
41*0e16a31dSJim Jagielskidef new():
42*0e16a31dSJim Jagielski    return desk.loadComponentFromURL("private:factory/scalc", "_blank", 0, (pv("Hidden", True),))
43*0e16a31dSJim Jagielski
44*0e16a31dSJim Jagielski
45*0e16a31dSJim Jagielskidef save(doc, name, filt="calc8"):
46*0e16a31dSJim Jagielski    try:
47*0e16a31dSJim Jagielski        doc.storeToURL(uno.systemPathToFileUrl(os.path.join(out, name)), (pv("FilterName", filt),))
48*0e16a31dSJim Jagielski        print("saved", name, filt)
49*0e16a31dSJim Jagielski        return True
50*0e16a31dSJim Jagielski    except Exception as e:
51*0e16a31dSJim Jagielski        failed.append(name)
52*0e16a31dSJim Jagielski        print("FAIL", name, filt, repr(e)[:200])
53*0e16a31dSJim Jagielski        return False
54*0e16a31dSJim Jagielski
55*0e16a31dSJim Jagielski
56*0e16a31dSJim Jagielskidef step(label, fn, *a, **kw):
57*0e16a31dSJim Jagielski    if os.environ.get("CALC_TRACE"):
58*0e16a31dSJim Jagielski        print("step", label, flush=True)
59*0e16a31dSJim Jagielski    try:
60*0e16a31dSJim Jagielski        return fn(*a, **kw)
61*0e16a31dSJim Jagielski    except Exception as e:
62*0e16a31dSJim Jagielski        failed.append(label)
63*0e16a31dSJim Jagielski        print("FAIL", label, type(e).__name__, str(e)[:300])
64*0e16a31dSJim Jagielski
65*0e16a31dSJim Jagielski
66*0e16a31dSJim Jagielskidef cellrange(sh, addr):
67*0e16a31dSJim Jagielski    return sh.getCellRangeByName(addr)
68*0e16a31dSJim Jagielski
69*0e16a31dSJim Jagielski
70*0e16a31dSJim JagielskiNUMTYPES = ("WHOLE", "DECIMAL", "DATE", "TIME", "TEXT_LEN")
71*0e16a31dSJim JagielskiOPS = ("NONE", "EQUAL", "NOT_EQUAL", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL", "BETWEEN", "NOT_BETWEEN", "FORMULA")
72*0e16a31dSJim JagielskiSAMPLE = {"WHOLE": ("5", "10"), "DECIMAL": ("1.5", "9.25"), "DATE": ("DATE(2020;1;31)", "43831"),
73*0e16a31dSJim Jagielski          "TIME": ("TIME(8;30;0)", "0.75"), "TEXT_LEN": ("3", "12"), "LIST": ('"a";"b";"c"', ""), "CUSTOM": ("$Sheet1.$B$1>0", "")}
74*0e16a31dSJim Jagielski
75*0e16a31dSJim Jagielski
76*0e16a31dSJim Jagielskidef setval(cell, typ, op, f1, f2, alert="STOP", show_list=1, ignore=True, inp=("", ""), err=("", ""), show_inp=False, show_err=True):
77*0e16a31dSJim Jagielski    v = cell.Validation
78*0e16a31dSJim Jagielski    v.Type = E("ValidationType", typ)
79*0e16a31dSJim Jagielski    v.setOperator(E("ConditionOperator", op))
80*0e16a31dSJim Jagielski    v.setFormula1(f1)
81*0e16a31dSJim Jagielski    v.setFormula2(f2)
82*0e16a31dSJim Jagielski    v.IgnoreBlankCells = ignore
83*0e16a31dSJim Jagielski    v.ShowList = show_list
84*0e16a31dSJim Jagielski    v.ErrorAlertStyle = E("ValidationAlertStyle", alert)
85*0e16a31dSJim Jagielski    v.InputTitle, v.InputMessage = inp
86*0e16a31dSJim Jagielski    v.ErrorTitle, v.ErrorMessage = err
87*0e16a31dSJim Jagielski    v.ShowInputMessage = show_inp
88*0e16a31dSJim Jagielski    v.ShowErrorMessage = show_err
89*0e16a31dSJim Jagielski    cell.Validation = v
90*0e16a31dSJim Jagielski    r = cell.Validation
91*0e16a31dSJim Jagielski    return {"type": r.Type.value, "op": r.getOperator().value, "f1": r.getFormula1(), "f2": r.getFormula2(),
92*0e16a31dSJim Jagielski            "alert": r.ErrorAlertStyle.value, "list": r.ShowList, "ignore": r.IgnoreBlankCells,
93*0e16a31dSJim Jagielski            "inp": [r.InputTitle, r.InputMessage], "err": [r.ErrorTitle, r.ErrorMessage],
94*0e16a31dSJim Jagielski            "showinp": r.ShowInputMessage, "showerr": r.ShowErrorMessage}
95*0e16a31dSJim Jagielski
96*0e16a31dSJim Jagielski
97*0e16a31dSJim Jagielskidef validation_doc(name, cases):
98*0e16a31dSJim Jagielski    doc = new()
99*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
100*0e16a31dSJim Jagielski    for i in range(1, 6):
101*0e16a31dSJim Jagielski        sh.getCellByPosition(1, i - 1).Value = i
102*0e16a31dSJim Jagielski    rows = []
103*0e16a31dSJim Jagielski    for i, (label, args, kw) in enumerate(cases):
104*0e16a31dSJim Jagielski        c = sh.getCellByPosition(0, i)
105*0e16a31dSJim Jagielski        try:
106*0e16a31dSJim Jagielski            exp = setval(c, *args, **kw)
107*0e16a31dSJim Jagielski        except Exception as e:
108*0e16a31dSJim Jagielski            failed.append(name + ":" + label)
109*0e16a31dSJim Jagielski            print("FAIL", name, label, repr(e)[:200])
110*0e16a31dSJim Jagielski            continue
111*0e16a31dSJim Jagielski        c.Value = 1
112*0e16a31dSJim Jagielski        rows.append({"cell": "A%d" % (i + 1), "label": label, "set": [a for a in args], "kw": {k: (list(v) if isinstance(v, tuple) else v) for k, v in kw.items()}, "readback": exp})
113*0e16a31dSJim Jagielski    if save(doc, name):
114*0e16a31dSJim Jagielski        expect[name] = rows
115*0e16a31dSJim Jagielski    doc.close(True)
116*0e16a31dSJim Jagielski
117*0e16a31dSJim Jagielski
118*0e16a31dSJim Jagielskidef validations():
119*0e16a31dSJim Jagielski    cases = []
120*0e16a31dSJim Jagielski    for t in NUMTYPES:
121*0e16a31dSJim Jagielski        for op in OPS:
122*0e16a31dSJim Jagielski            f1, f2 = SAMPLE[t]
123*0e16a31dSJim Jagielski            cases.append(("%s/%s" % (t, op), (t, op, f1, f2 if op in ("BETWEEN", "NOT_BETWEEN") else ""), {}))
124*0e16a31dSJim Jagielski    validation_doc("validation-types.ods", cases)
125*0e16a31dSJim Jagielski    cases = []
126*0e16a31dSJim Jagielski    for op in OPS:
127*0e16a31dSJim Jagielski        cases.append(("CUSTOM/%s" % op, ("CUSTOM", op, "$Sheet1.$B$1>0", ""), {}))
128*0e16a31dSJim Jagielski        cases.append(("LIST/%s" % op, ("LIST", op, '"a";"b";"c"', ""), {}))
129*0e16a31dSJim Jagielski        cases.append(("ANY/%s" % op, ("ANY", op, "", ""), {"err": ("t", "m")}))
130*0e16a31dSJim Jagielski    for t, f1, f2 in (("LIST", "$Sheet1.$B$1:$B$5", ""), ("LIST", '"one";"two"', ""), ("CUSTOM", "ISNUMBER(A1)", ""),
131*0e16a31dSJim Jagielski                      ("CUSTOM", "AND(A1>0;A1<10)", ""), ("CUSTOM", 'LEN(A1)=3', ""), ("CUSTOM", '"abc"', "")):
132*0e16a31dSJim Jagielski        cases.append(("%s/f1=%s" % (t, f1), (t, "FORMULA" if t == "CUSTOM" else "EQUAL", f1, f2), {}))
133*0e16a31dSJim Jagielski    validation_doc("validation-custom-list.ods", cases)
134*0e16a31dSJim Jagielski    cases = []
135*0e16a31dSJim Jagielski    for alert in ("STOP", "WARNING", "INFO", "MACRO"):
136*0e16a31dSJim Jagielski        for show_err in (True, False):
137*0e16a31dSJim Jagielski            for ign in (True, False):
138*0e16a31dSJim Jagielski                cases.append(("WHOLE/%s/err=%s/ign=%s" % (alert, show_err, ign),
139*0e16a31dSJim Jagielski                              ("WHOLE", "BETWEEN", "1", "10"),
140*0e16a31dSJim Jagielski                              {"alert": alert, "show_err": show_err, "ignore": ign,
141*0e16a31dSJim Jagielski                               "err": ("Macro.Lib.Mod.Fn" if alert == "MACRO" else "Err title", "Err line 1\nErr line 2"),
142*0e16a31dSJim Jagielski                               "inp": ("In title", "In text"), "show_inp": ign}))
143*0e16a31dSJim Jagielski    for sl in (0, 1, 2):
144*0e16a31dSJim Jagielski        for ign in (True, False):
145*0e16a31dSJim Jagielski            cases.append(("LIST/showlist=%d/ign=%s" % (sl, ign), ("LIST", "EQUAL", '"x";"y"', ""), {"show_list": sl, "ignore": ign}))
146*0e16a31dSJim Jagielski    for sl in (0, 1, 2):
147*0e16a31dSJim Jagielski        cases.append(("LIST/distinct/showlist=%d" % sl, ("LIST", "EQUAL", '"x";"y"' + ';"z"' * sl, ""), {"show_list": sl}))
148*0e16a31dSJim Jagielski    cases.append(("DATE/msg-only-input", ("DATE", "GREATER", "1", ""), {"inp": ("", "only text"), "show_inp": True, "show_err": False}))
149*0e16a31dSJim Jagielski    cases.append(("ANY/input-only", ("ANY", "NONE", "", ""), {"inp": ("Hint", "Type a number"), "show_inp": True, "show_err": False}))
150*0e16a31dSJim Jagielski    cases.append(("ANY/error-only", ("ANY", "NONE", "", ""), {"err": ("Bad", "No"), "show_inp": False, "show_err": True}))
151*0e16a31dSJim Jagielski    cases.append(("ANY/error-title-only", ("ANY", "NONE", "", ""), {"err": ("Only a title", ""), "alert": "WARNING"}))
152*0e16a31dSJim Jagielski    cases.append(("TEXT_LEN/utf8", ("TEXT_LEN", "LESS", "5", ""), {"err": ("Fehler äöü", "Zu lang – <&> \"q\""), "alert": "INFO"}))
153*0e16a31dSJim Jagielski    validation_doc("validation-alerts.ods", cases)
154*0e16a31dSJim Jagielski
155*0e16a31dSJim Jagielski
156*0e16a31dSJim Jagielskidef validation_edges():
157*0e16a31dSJim Jagielski    cases = []
158*0e16a31dSJim Jagielski    for t in ("WHOLE", "DECIMAL", "TEXT_LEN", "LIST", "CUSTOM", "DATE", "TIME"):
159*0e16a31dSJim Jagielski        for op in ("EQUAL", "BETWEEN", "NOT_BETWEEN"):
160*0e16a31dSJim Jagielski            cases.append(("%s/%s/f1-empty" % (t, op), (t, op, "", "7"), {}))
161*0e16a31dSJim Jagielski            cases.append(("%s/%s/f2-empty" % (t, op), (t, op, "3", ""), {}))
162*0e16a31dSJim Jagielski            cases.append(("%s/%s/both-empty" % (t, op), (t, op, "", ""), {}))
163*0e16a31dSJim Jagielski    validation_doc("validation-edges.ods", cases)
164*0e16a31dSJim Jagielski
165*0e16a31dSJim Jagielski
166*0e16a31dSJim Jagielskidef struct(name, *a):
167*0e16a31dSJim Jagielski    return uno.createUnoStruct(name, *a)
168*0e16a31dSJim Jagielski
169*0e16a31dSJim Jagielski
170*0e16a31dSJim Jagielskidef rng_addr(sheet, c1, r1, c2, r2):
171*0e16a31dSJim Jagielski    return struct("com.sun.star.table.CellRangeAddress", sheet, c1, r1, c2, r2)
172*0e16a31dSJim Jagielski
173*0e16a31dSJim Jagielski
174*0e16a31dSJim Jagielskidef cell_addr(sheet, c, r):
175*0e16a31dSJim Jagielski    return struct("com.sun.star.table.CellAddress", sheet, c, r)
176*0e16a31dSJim Jagielski
177*0e16a31dSJim Jagielski
178*0e16a31dSJim Jagielskidef fill_table(sh, rows=12):
179*0e16a31dSJim Jagielski    heads = ("Region", "Product", "Qty", "Price", "Day")
180*0e16a31dSJim Jagielski    for c, h in enumerate(heads):
181*0e16a31dSJim Jagielski        sh.getCellByPosition(c, 0).String = h
182*0e16a31dSJim Jagielski    regions = ("North", "South", "East", "West")
183*0e16a31dSJim Jagielski    prods = ("Apple", "Pear", "Plum")
184*0e16a31dSJim Jagielski    for r in range(1, rows):
185*0e16a31dSJim Jagielski        sh.getCellByPosition(0, r).String = regions[r % 4]
186*0e16a31dSJim Jagielski        sh.getCellByPosition(1, r).String = prods[r % 3]
187*0e16a31dSJim Jagielski        sh.getCellByPosition(2, r).Value = r * 3 % 17
188*0e16a31dSJim Jagielski        sh.getCellByPosition(3, r).Value = 1.25 * r
189*0e16a31dSJim Jagielski        sh.getCellByPosition(4, r).Value = 43831 + r
190*0e16a31dSJim Jagielski    return sh
191*0e16a31dSJim Jagielski
192*0e16a31dSJim Jagielski
193*0e16a31dSJim Jagielskidef pvs(**kw):
194*0e16a31dSJim Jagielski    return tuple(pv(k, v) for k, v in kw.items())
195*0e16a31dSJim Jagielski
196*0e16a31dSJim Jagielski
197*0e16a31dSJim Jagielskidef sheets_doc(n):
198*0e16a31dSJim Jagielski    doc = new()
199*0e16a31dSJim Jagielski    for i in range(1, n):
200*0e16a31dSJim Jagielski        doc.Sheets.insertNewByName("Sh%d" % (i + 1), i)
201*0e16a31dSJim Jagielski    return doc
202*0e16a31dSJim Jagielski
203*0e16a31dSJim Jagielski
204*0e16a31dSJim Jagielskidef filter_field(field, op, value, numeric, conn="AND"):
205*0e16a31dSJim Jagielski    f = struct("com.sun.star.sheet.TableFilterField2")
206*0e16a31dSJim Jagielski    f.Field = field
207*0e16a31dSJim Jagielski    f.IsNumeric = numeric
208*0e16a31dSJim Jagielski    f.NumericValue = value if numeric else 0.0
209*0e16a31dSJim Jagielski    f.StringValue = "" if numeric else value
210*0e16a31dSJim Jagielski    f.Operator = uno.getConstantByName("com.sun.star.sheet.FilterOperator2." + op)
211*0e16a31dSJim Jagielski    f.Connection = E("FilterConnection", conn)
212*0e16a31dSJim Jagielski    return f
213*0e16a31dSJim Jagielski
214*0e16a31dSJim Jagielski
215*0e16a31dSJim Jagielskidef add_db(doc, i, name, rows=12):
216*0e16a31dSJim Jagielski    fill_table(doc.Sheets.getByIndex(i), rows)
217*0e16a31dSJim Jagielski    doc.DatabaseRanges.addNewByName(name, rng_addr(i, 0, 0, 4, rows - 1))
218*0e16a31dSJim Jagielski    return doc.DatabaseRanges.getByName(name)
219*0e16a31dSJim Jagielski
220*0e16a31dSJim Jagielski
221*0e16a31dSJim Jagielskidef dbranges():
222*0e16a31dSJim Jagielski    ops = ["EQUAL", "NOT_EQUAL", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL", "BOTTOM_PERCENT", "BOTTOM_VALUES", "EMPTY",
223*0e16a31dSJim Jagielski           "NOT_EMPTY", "TOP_PERCENT", "TOP_VALUES", "CONTAINS", "DOES_NOT_CONTAIN", "BEGINS_WITH", "DOES_NOT_BEGIN_WITH",
224*0e16a31dSJim Jagielski           "ENDS_WITH", "DOES_NOT_END_WITH"]
225*0e16a31dSJim Jagielski    patterns = {"single": [None], "and2": [None, "AND"], "or2": [None, "OR"], "and3": [None, "AND", "AND"],
226*0e16a31dSJim Jagielski                "or3": [None, "OR", "OR"], "and-or": [None, "AND", "OR"], "or-and": [None, "OR", "AND"],
227*0e16a31dSJim Jagielski                "and-or-and": [None, "AND", "OR", "AND"], "or-and-and": [None, "OR", "AND", "AND"],
228*0e16a31dSJim Jagielski                "and-and-or": [None, "AND", "AND", "OR"], "or-or-and": [None, "OR", "OR", "AND"]}
229*0e16a31dSJim Jagielski    doc = sheets_doc(len(patterns))
230*0e16a31dSJim Jagielski
231*0e16a31dSJim Jagielski    def one(i, name, conns):
232*0e16a31dSJim Jagielski        db = add_db(doc, i, name)
233*0e16a31dSJim Jagielski        fd = db.getFilterDescriptor()
234*0e16a31dSJim Jagielski        fd.setFilterFields2(tuple(filter_field(k % 5, "EQUAL", 5.5 + k if k % 5 in (2, 3) else "North", k % 5 in (2, 3), cn or "AND")
235*0e16a31dSJim Jagielski                                  for k, cn in enumerate(conns)))
236*0e16a31dSJim Jagielski        fd.IsCaseSensitive = i % 2 == 0
237*0e16a31dSJim Jagielski        fd.SkipDuplicates = i % 3 == 0
238*0e16a31dSJim Jagielski        fd.UseRegularExpressions = i % 4 == 0
239*0e16a31dSJim Jagielski        if i % 5 == 0:
240*0e16a31dSJim Jagielski            fd.CopyOutputData = True
241*0e16a31dSJim Jagielski            fd.OutputPosition = cell_addr(i, 7, 14)
242*0e16a31dSJim Jagielski
243*0e16a31dSJim Jagielski    for i, (name, conns) in enumerate(patterns.items()):
244*0e16a31dSJim Jagielski        step("filter-" + name, one, i, "f_" + name, conns)
245*0e16a31dSJim Jagielski    save(doc, "filter-connections.ods")
246*0e16a31dSJim Jagielski    doc.close(True)
247*0e16a31dSJim Jagielski
248*0e16a31dSJim Jagielski    doc = sheets_doc(len(ops) * 2)
249*0e16a31dSJim Jagielski    for oi, o in enumerate(ops):
250*0e16a31dSJim Jagielski        for rx in (False, True):
251*0e16a31dSJim Jagielski            def mk(o=o, rx=rx, i=oi * 2 + rx):
252*0e16a31dSJim Jagielski                db = add_db(doc, i, "f_%s%s" % (o.lower(), "_rx" if rx else ""))
253*0e16a31dSJim Jagielski                num = o in ("BOTTOM_PERCENT", "BOTTOM_VALUES", "TOP_PERCENT", "TOP_VALUES", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL")
254*0e16a31dSJim Jagielski                fd = db.getFilterDescriptor()
255*0e16a31dSJim Jagielski                fd.setFilterFields2((filter_field(2 if num else 0, o, 3.0 if num else "o", num),))
256*0e16a31dSJim Jagielski                fd.UseRegularExpressions = rx
257*0e16a31dSJim Jagielski            step("op-" + o, mk)
258*0e16a31dSJim Jagielski    save(doc, "filter-operators.ods")
259*0e16a31dSJim Jagielski    doc.close(True)
260*0e16a31dSJim Jagielski
261*0e16a31dSJim Jagielski    doc = sheets_doc(8)
262*0e16a31dSJim Jagielski    sort_fields = lambda *fs: uno.Any("[]com.sun.star.table.TableSortField", tuple(
263*0e16a31dSJim Jagielski        struct("com.sun.star.table.TableSortField", f, asc, cs, uno.Enum("com.sun.star.table.TableSortFieldType", ft), struct("com.sun.star.lang.Locale", "en", "US", ""), "")
264*0e16a31dSJim Jagielski        for f, asc, cs, ft in fs))
265*0e16a31dSJim Jagielski
266*0e16a31dSJim Jagielski    def sorts():
267*0e16a31dSJim Jagielski        for i, (cs, bind, copy, uls) in enumerate(((False, True, False, False), (True, False, True, False), (False, True, False, True), (True, True, True, False))):
268*0e16a31dSJim Jagielski            db = add_db(doc, i, "sort%d" % i)
269*0e16a31dSJim Jagielski            sd = db.getSortDescriptor()
270*0e16a31dSJim Jagielski            sd2 = []
271*0e16a31dSJim Jagielski            for p in sd:
272*0e16a31dSJim Jagielski                v = p.Value
273*0e16a31dSJim Jagielski                if p.Name == "SortFields":
274*0e16a31dSJim Jagielski                    v = sort_fields((0, True, cs, "ALPHANUMERIC"), (2, False, cs, "NUMERIC"), (3, True, cs, "AUTOMATIC"))
275*0e16a31dSJim Jagielski                elif p.Name == "BindFormatsToContent":
276*0e16a31dSJim Jagielski                    v = bind
277*0e16a31dSJim Jagielski                elif p.Name == "CopyOutputData":
278*0e16a31dSJim Jagielski                    v = copy
279*0e16a31dSJim Jagielski                elif p.Name == "OutputPosition":
280*0e16a31dSJim Jagielski                    v = cell_addr(i, 7, 14)
281*0e16a31dSJim Jagielski                elif p.Name == "IsUserListEnabled":
282*0e16a31dSJim Jagielski                    v = uls
283*0e16a31dSJim Jagielski                elif p.Name == "UserListIndex":
284*0e16a31dSJim Jagielski                    v = 1
285*0e16a31dSJim Jagielski                elif p.Name == "ContainsHeader":
286*0e16a31dSJim Jagielski                    v = i != 1
287*0e16a31dSJim Jagielski                sd2.append(pv(p.Name, v))
288*0e16a31dSJim Jagielski            doc.Sheets.getByIndex(i).getCellRangeByName("A1:E12").sort(tuple(sd2))
289*0e16a31dSJim Jagielski    step("sorts", sorts)
290*0e16a31dSJim Jagielski
291*0e16a31dSJim Jagielski    def subtotals():
292*0e16a31dSJim Jagielski        for i in (4, 5):
293*0e16a31dSJim Jagielski            db = add_db(doc, i, "sub%d" % i)
294*0e16a31dSJim Jagielski            st = db.getSubTotalDescriptor()
295*0e16a31dSJim Jagielski            st.InsertPageBreaks = i == 5
296*0e16a31dSJim Jagielski            st.IsCaseSensitive = i == 5
297*0e16a31dSJim Jagielski            st.BindFormatsToContent = i == 4
298*0e16a31dSJim Jagielski            st.EnableSort = True
299*0e16a31dSJim Jagielski            st.SortAscending = i == 4
300*0e16a31dSJim Jagielski            if i == 5:
301*0e16a31dSJim Jagielski                st.EnableUserSortList = True
302*0e16a31dSJim Jagielski                st.UserSortListIndex = 1
303*0e16a31dSJim Jagielski            funcs = ["SUM", "COUNT", "AVERAGE", "MAX", "MIN", "PRODUCT", "COUNTNUMS", "STDEV", "STDEVP", "VAR", "VARP"]
304*0e16a31dSJim Jagielski            f0 = st.addNew((), 0)
305*0e16a31dSJim Jagielski            cols = tuple(struct("com.sun.star.sheet.SubTotalColumn", 2 + (k % 2), uno.Enum("com.sun.star.sheet.GeneralFunction", fn)) for k, fn in enumerate(funcs))
306*0e16a31dSJim Jagielski            sf = st.getByIndex(0)
307*0e16a31dSJim Jagielski            sf.setSubTotalColumns(cols)
308*0e16a31dSJim Jagielski            st.addNew((struct("com.sun.star.sheet.SubTotalColumn", 3, uno.Enum("com.sun.star.sheet.GeneralFunction", "SUM")),), 1)
309*0e16a31dSJim Jagielski    step("subtotals", subtotals)
310*0e16a31dSJim Jagielski
311*0e16a31dSJim Jagielski    def attrs():
312*0e16a31dSJim Jagielski        for i, (keepf, mov, strip, auto, refresh) in enumerate(((True, True, True, True, 60), (False, False, False, False, 0))):
313*0e16a31dSJim Jagielski            db = add_db(doc, 6 + i, "attr%d" % i)
314*0e16a31dSJim Jagielski            db.KeepFormats, db.MoveCells, db.StripData, db.AutoFilter, db.RefreshPeriod = keepf, mov, strip, auto, refresh
315*0e16a31dSJim Jagielski            fd = db.getFilterDescriptor()
316*0e16a31dSJim Jagielski            fd.ContainsHeader = i == 1
317*0e16a31dSJim Jagielski            sd = tuple(pv(p.Name, (i == 0) if p.Name == "IsSortColumns" else p.Value) for p in db.getSortDescriptor())
318*0e16a31dSJim Jagielski            doc.Sheets.getByIndex(6 + i).getCellRangeByName("A1:E12").sort(sd)
319*0e16a31dSJim Jagielski    step("dbattrs", attrs)
320*0e16a31dSJim Jagielski    save(doc, "dbranges-sort-subtotal.ods")
321*0e16a31dSJim Jagielski    doc.close(True)
322*0e16a31dSJim Jagielski
323*0e16a31dSJim Jagielski
324*0e16a31dSJim Jagielskistep("dbranges", dbranges)
325*0e16a31dSJim Jagielski
326*0e16a31dSJim Jagielski
327*0e16a31dSJim Jagielskidef pivot_source(doc, sh, rows=40):
328*0e16a31dSJim Jagielski    heads = ("Region", "Product", "Qty", "Price", "Day", "Hour")
329*0e16a31dSJim Jagielski    for c, h in enumerate(heads):
330*0e16a31dSJim Jagielski        sh.getCellByPosition(c, 0).String = h
331*0e16a31dSJim Jagielski    regions = ("North", "South", "East", "West")
332*0e16a31dSJim Jagielski    prods = ("Apple", "Pear", "Plum")
333*0e16a31dSJim Jagielski    nf = doc.NumberFormats
334*0e16a31dSJim Jagielski    loc = struct("com.sun.star.lang.Locale", "en", "US", "")
335*0e16a31dSJim Jagielski    dfmt = nf.queryKey("YYYY-MM-DD", loc, False)
336*0e16a31dSJim Jagielski    if dfmt == -1:
337*0e16a31dSJim Jagielski        dfmt = nf.addNew("YYYY-MM-DD", loc)
338*0e16a31dSJim Jagielski    tfmt = nf.queryKey("HH:MM:SS", loc, False)
339*0e16a31dSJim Jagielski    if tfmt == -1:
340*0e16a31dSJim Jagielski        tfmt = nf.addNew("HH:MM:SS", loc)
341*0e16a31dSJim Jagielski    for r in range(1, rows):
342*0e16a31dSJim Jagielski        sh.getCellByPosition(0, r).String = regions[r % 4]
343*0e16a31dSJim Jagielski        sh.getCellByPosition(1, r).String = prods[r % 3]
344*0e16a31dSJim Jagielski        sh.getCellByPosition(2, r).Value = (r * 7) % 23
345*0e16a31dSJim Jagielski        sh.getCellByPosition(3, r).Value = 1.25 * r
346*0e16a31dSJim Jagielski        c = sh.getCellByPosition(4, r)
347*0e16a31dSJim Jagielski        c.Value = 43831 + r * 37
348*0e16a31dSJim Jagielski        c.NumberFormat = dfmt
349*0e16a31dSJim Jagielski        c = sh.getCellByPosition(5, r)
350*0e16a31dSJim Jagielski        c.Value = (r * 3671 % 86400) / 86400.0
351*0e16a31dSJim Jagielski        c.NumberFormat = tfmt
352*0e16a31dSJim Jagielski    return rng_addr(sh.RangeAddress.Sheet, 0, 0, 5, rows - 1)
353*0e16a31dSJim Jagielski
354*0e16a31dSJim Jagielski
355*0e16a31dSJim Jagielskidef new_pivot(doc, sheetname, name, src, at=(8, 2), fields=None, **props):
356*0e16a31dSJim Jagielski    sh = doc.Sheets.getByName(sheetname)
357*0e16a31dSJim Jagielski    dps = sh.DataPilotTables
358*0e16a31dSJim Jagielski    desc = dps.createDataPilotDescriptor()
359*0e16a31dSJim Jagielski    desc.setSourceRange(src)
360*0e16a31dSJim Jagielski    fl = desc.getDataPilotFields()
361*0e16a31dSJim Jagielski    for fname, fp in (fields or {}).items():
362*0e16a31dSJim Jagielski        f = fl.getByName(fname) if hasattr(fl, "getByName") else None
363*0e16a31dSJim Jagielski        for k, v in fp.items():
364*0e16a31dSJim Jagielski            setattr(f, k, v) if not isinstance(v, uno.Enum) else f.setPropertyValue(k, v)
365*0e16a31dSJim Jagielski    for k, v in props.items():
366*0e16a31dSJim Jagielski        desc.setPropertyValue(k, v)
367*0e16a31dSJim Jagielski    dps.insertNewByName(name, cell_addr(sh.RangeAddress.Sheet, at[0], at[1]), desc)
368*0e16a31dSJim Jagielski    return dps.getByName(name)
369*0e16a31dSJim Jagielski
370*0e16a31dSJim Jagielski
371*0e16a31dSJim Jagielskidef orient(n):
372*0e16a31dSJim Jagielski    return uno.Enum("com.sun.star.sheet.DataPilotFieldOrientation", n)
373*0e16a31dSJim Jagielski
374*0e16a31dSJim Jagielski
375*0e16a31dSJim Jagielskidef gf(n):
376*0e16a31dSJim Jagielski    return uno.Enum("com.sun.star.sheet.GeneralFunction", n)
377*0e16a31dSJim Jagielski
378*0e16a31dSJim Jagielski
379*0e16a31dSJim Jagielskidef pivots():
380*0e16a31dSJim Jagielski    doc = new()
381*0e16a31dSJim Jagielski    sheets = doc.Sheets
382*0e16a31dSJim Jagielski    names = ["Src", "Funcs", "Refs", "Sorts", "AutoShow", "Layout", "Groups", "Totals", "Filtered", "Pages"]
383*0e16a31dSJim Jagielski    for i, n in enumerate(names[1:], 1):
384*0e16a31dSJim Jagielski        sheets.insertNewByName(n, i)
385*0e16a31dSJim Jagielski    sheets.getByIndex(0).Name = "Src"
386*0e16a31dSJim Jagielski    src = pivot_source(doc, sheets.getByName("Src"))
387*0e16a31dSJim Jagielski    funcs = ["SUM", "COUNT", "AVERAGE", "MAX", "MIN", "PRODUCT", "COUNTNUMS", "STDEV", "STDEVP", "VAR", "VARP"]
388*0e16a31dSJim Jagielski    FN = "Funcs"
389*0e16a31dSJim Jagielski
390*0e16a31dSJim Jagielski    def func_pivot(k, fn):
391*0e16a31dSJim Jagielski        t = new_pivot(doc, FN, "pf_" + fn.lower(), src, at=(k * 4, 1 + 0), fields={
392*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA"), "Function": gf(fn)}})
393*0e16a31dSJim Jagielski    for k, fn in enumerate(funcs):
394*0e16a31dSJim Jagielski        step("func-" + fn, func_pivot, k, fn)
395*0e16a31dSJim Jagielski
396*0e16a31dSJim Jagielski    RT = "com.sun.star.sheet.DataPilotFieldReferenceType."
397*0e16a31dSJim Jagielski    IT = "com.sun.star.sheet.DataPilotFieldReferenceItemType."
398*0e16a31dSJim Jagielski
399*0e16a31dSJim Jagielski    def ref_pivot(k, rtype, itype):
400*0e16a31dSJim Jagielski        t = new_pivot(doc, "Refs", "pr_%s_%s" % (rtype.lower(), itype.lower()), src, at=(k * 4, 1), fields={
401*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Product": {"Orientation": orient("COLUMN")},
402*0e16a31dSJim Jagielski            "Qty": {"Orientation": orient("DATA")}})
403*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Qty")
404*0e16a31dSJim Jagielski        r = struct("com.sun.star.sheet.DataPilotFieldReference")
405*0e16a31dSJim Jagielski        r.ReferenceType = uno.getConstantByName(RT + rtype)
406*0e16a31dSJim Jagielski        r.ReferenceField = "Region"
407*0e16a31dSJim Jagielski        r.ReferenceItemType = uno.getConstantByName(IT + itype)
408*0e16a31dSJim Jagielski        r.ReferenceItemName = "North" if itype == "NAMED" else ""
409*0e16a31dSJim Jagielski        f.setPropertyValue("Reference", r)
410*0e16a31dSJim Jagielski    k = 0
411*0e16a31dSJim Jagielski    for rtype in ("NONE", "ITEM_DIFFERENCE", "ITEM_PERCENTAGE", "ITEM_PERCENTAGE_DIFFERENCE", "RUNNING_TOTAL", "ROW_PERCENTAGE",
412*0e16a31dSJim Jagielski                  "COLUMN_PERCENTAGE", "TOTAL_PERCENTAGE", "INDEX"):
413*0e16a31dSJim Jagielski        for itype in (("NAMED", "PREVIOUS", "NEXT") if rtype.startswith("ITEM") else ("NAMED",)):
414*0e16a31dSJim Jagielski            step("ref-%s-%s" % (rtype, itype), ref_pivot, k, rtype, itype)
415*0e16a31dSJim Jagielski            k += 1
416*0e16a31dSJim Jagielski
417*0e16a31dSJim Jagielski    SM = "com.sun.star.sheet.DataPilotFieldSortMode."
418*0e16a31dSJim Jagielski
419*0e16a31dSJim Jagielski    def sort_pivot(k, mode, asc):
420*0e16a31dSJim Jagielski        t = new_pivot(doc, "Sorts", "ps_%d" % k, src, at=(k * 4, 1), fields={
421*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
422*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
423*0e16a31dSJim Jagielski        si = struct("com.sun.star.sheet.DataPilotFieldSortInfo")
424*0e16a31dSJim Jagielski        si.Field = "Qty" if mode == "DATA" else ""
425*0e16a31dSJim Jagielski        si.IsAscending = asc
426*0e16a31dSJim Jagielski        si.Mode = uno.getConstantByName(SM + mode)
427*0e16a31dSJim Jagielski        f.setPropertyValue("SortInfo", si)
428*0e16a31dSJim Jagielski    k = 0
429*0e16a31dSJim Jagielski    for mode in ("NONE", "MANUAL", "NAME", "DATA"):
430*0e16a31dSJim Jagielski        for asc in (True, False):
431*0e16a31dSJim Jagielski            step("sort-%s-%s" % (mode, asc), sort_pivot, k, mode, asc)
432*0e16a31dSJim Jagielski            k += 1
433*0e16a31dSJim Jagielski
434*0e16a31dSJim Jagielski    def auto_pivot(k, mode, enabled):
435*0e16a31dSJim Jagielski        t = new_pivot(doc, "AutoShow", "pa_%d" % k, src, at=(k * 4, 1), fields={
436*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
437*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
438*0e16a31dSJim Jagielski        ai = struct("com.sun.star.sheet.DataPilotFieldAutoShowInfo")
439*0e16a31dSJim Jagielski        ai.IsEnabled = enabled
440*0e16a31dSJim Jagielski        ai.ShowItemsMode = uno.getConstantByName("com.sun.star.sheet.DataPilotFieldShowItemsMode." + mode)
441*0e16a31dSJim Jagielski        ai.ItemCount = 2
442*0e16a31dSJim Jagielski        ai.DataField = "Qty"
443*0e16a31dSJim Jagielski        f.setPropertyValue("AutoShowInfo", ai)
444*0e16a31dSJim Jagielski    k = 0
445*0e16a31dSJim Jagielski    for mode in ("FROM_TOP", "FROM_BOTTOM"):
446*0e16a31dSJim Jagielski        for en in (True, False):
447*0e16a31dSJim Jagielski            step("autoshow-%s-%s" % (mode, en), auto_pivot, k, mode, en)
448*0e16a31dSJim Jagielski            k += 1
449*0e16a31dSJim Jagielski
450*0e16a31dSJim Jagielski    def layout_pivot(k, mode, empty):
451*0e16a31dSJim Jagielski        t = new_pivot(doc, "Layout", "pl_%d" % k, src, at=(k * 5, 1), fields={
452*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Product": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
453*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
454*0e16a31dSJim Jagielski        li = struct("com.sun.star.sheet.DataPilotFieldLayoutInfo")
455*0e16a31dSJim Jagielski        li.AddEmptyLines = empty
456*0e16a31dSJim Jagielski        li.LayoutMode = uno.getConstantByName("com.sun.star.sheet.DataPilotFieldLayoutMode." + mode)
457*0e16a31dSJim Jagielski        f.setPropertyValue("LayoutInfo", li)
458*0e16a31dSJim Jagielski    k = 0
459*0e16a31dSJim Jagielski    for mode in ("TABULAR_LAYOUT", "OUTLINE_SUBTOTALS_TOP", "OUTLINE_SUBTOTALS_BOTTOM"):
460*0e16a31dSJim Jagielski        for empty in (True, False):
461*0e16a31dSJim Jagielski            step("layout-%s-%s" % (mode, empty), layout_pivot, k, mode, empty)
462*0e16a31dSJim Jagielski            k += 1
463*0e16a31dSJim Jagielski
464*0e16a31dSJim Jagielski    def totals_pivot(k, row, col, **kw):
465*0e16a31dSJim Jagielski        new_pivot(doc, "Totals", "pt_%d" % k, src, at=(k * 4, 1), fields={
466*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Product": {"Orientation": orient("COLUMN")},
467*0e16a31dSJim Jagielski            "Qty": {"Orientation": orient("DATA")}}, RowGrand=row, ColumnGrand=col, **kw)
468*0e16a31dSJim Jagielski    for k, (row, col, kw) in enumerate(((True, True, {}), (True, False, {}), (False, True, {}), (False, False, {}),
469*0e16a31dSJim Jagielski                                        (True, True, {"IgnoreEmptyRows": True}), (True, True, {"RepeatIfEmpty": True}),
470*0e16a31dSJim Jagielski                                        (True, True, {"ShowFilterButton": False}), (True, True, {"DrillDownOnDoubleClick": False}))):
471*0e16a31dSJim Jagielski        step("totals-%d" % k, totals_pivot, k, row, col, **kw)
472*0e16a31dSJim Jagielski
473*0e16a31dSJim Jagielski    def sub_pivot():
474*0e16a31dSJim Jagielski        t = new_pivot(doc, "Totals", "pt_sub", src, at=(0, 12), fields={
475*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Product": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
476*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
477*0e16a31dSJim Jagielski        uno.invoke(f, "setPropertyValue", ("Subtotals", uno.Any("[]com.sun.star.sheet.GeneralFunction", tuple(gf(n) for n in ("SUM", "COUNT", "AVERAGE", "MAX")))))
478*0e16a31dSJim Jagielski        f.setPropertyValue("ShowEmpty", True)
479*0e16a31dSJim Jagielski        f2 = t.getDataPilotFields().getByName("Product")
480*0e16a31dSJim Jagielski        uno.invoke(f2, "setPropertyValue", ("Subtotals", uno.Any("[]com.sun.star.sheet.GeneralFunction", (gf("AUTO"),))))
481*0e16a31dSJim Jagielski        items = f.getItems()
482*0e16a31dSJim Jagielski        for n in items.ElementNames[:2]:
483*0e16a31dSJim Jagielski            it = items.getByName(n)
484*0e16a31dSJim Jagielski            it.IsHidden = n == items.ElementNames[0]
485*0e16a31dSJim Jagielski            it.ShowDetail = False
486*0e16a31dSJim Jagielski    step("subtotals", sub_pivot)
487*0e16a31dSJim Jagielski
488*0e16a31dSJim Jagielski    def page_pivot():
489*0e16a31dSJim Jagielski        t = new_pivot(doc, "Pages", "pg_1", src, at=(0, 3), fields={
490*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("PAGE")}, "Product": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
491*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
492*0e16a31dSJim Jagielski        f.setPropertyValue("CurrentPage", "South")
493*0e16a31dSJim Jagielski    step("page", page_pivot)
494*0e16a31dSJim Jagielski
495*0e16a31dSJim Jagielski    def hidden_pivot():
496*0e16a31dSJim Jagielski        t = new_pivot(doc, "Pages", "pg_2", src, at=(6, 3), fields={
497*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("HIDDEN")}, "Product": {"Orientation": orient("COLUMN")},
498*0e16a31dSJim Jagielski            "Qty": {"Orientation": orient("DATA")}, "Price": {"Orientation": orient("DATA")}})
499*0e16a31dSJim Jagielski    step("two-data-fields", hidden_pivot)
500*0e16a31dSJim Jagielski
501*0e16a31dSJim Jagielski    def filtered_pivot():
502*0e16a31dSJim Jagielski        sh = doc.Sheets.getByName("Filtered")
503*0e16a31dSJim Jagielski        dps = sh.DataPilotTables
504*0e16a31dSJim Jagielski        desc = dps.createDataPilotDescriptor()
505*0e16a31dSJim Jagielski        desc.setSourceRange(src)
506*0e16a31dSJim Jagielski        fl = desc.getDataPilotFields()
507*0e16a31dSJim Jagielski        fl.getByName("Region").Orientation = orient("ROW")
508*0e16a31dSJim Jagielski        fl.getByName("Qty").Orientation = orient("DATA")
509*0e16a31dSJim Jagielski        fd = desc.getFilterDescriptor()
510*0e16a31dSJim Jagielski        fd.setFilterFields2((filter_field(1, "EQUAL", "Apple", False, "AND"), filter_field(2, "GREATER", 2.0, True, "AND"),
511*0e16a31dSJim Jagielski                             filter_field(0, "NOT_EQUAL", "East", False, "OR")))
512*0e16a31dSJim Jagielski        fd.IsCaseSensitive = True
513*0e16a31dSJim Jagielski        fd.SkipDuplicates = True
514*0e16a31dSJim Jagielski        fd.UseRegularExpressions = False
515*0e16a31dSJim Jagielski        dps.insertNewByName("pfil_1", cell_addr(doc.Sheets.getByName("Filtered").RangeAddress.Sheet, 0, 0), desc)
516*0e16a31dSJim Jagielski        desc2 = dps.createDataPilotDescriptor()
517*0e16a31dSJim Jagielski        desc2.setSourceRange(src)
518*0e16a31dSJim Jagielski        desc2.getDataPilotFields().getByName("Product").Orientation = orient("ROW")
519*0e16a31dSJim Jagielski        desc2.getDataPilotFields().getByName("Price").Orientation = orient("DATA")
520*0e16a31dSJim Jagielski        fd = desc2.getFilterDescriptor()
521*0e16a31dSJim Jagielski        fd.setFilterFields2((filter_field(0, "EQUAL", "N.*", False, "OR"), filter_field(1, "CONTAINS", "ea", False, "OR")))
522*0e16a31dSJim Jagielski        fd.UseRegularExpressions = True
523*0e16a31dSJim Jagielski        dps.insertNewByName("pfil_2", cell_addr(doc.Sheets.getByName("Filtered").RangeAddress.Sheet, 6, 0), desc2)
524*0e16a31dSJim Jagielski    step("filtered", filtered_pivot)
525*0e16a31dSJim Jagielski
526*0e16a31dSJim Jagielski    GB = "com.sun.star.sheet.DataPilotFieldGroupBy."
527*0e16a31dSJim Jagielski
528*0e16a31dSJim Jagielski    def date_group(k, part, auto, fname="Day"):
529*0e16a31dSJim Jagielski        t = new_pivot(doc, "Groups", "pd_%d" % k, src, at=(k * 4, 1), fields={
530*0e16a31dSJim Jagielski            fname: {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
531*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName(fname)
532*0e16a31dSJim Jagielski        gi = struct("com.sun.star.sheet.DataPilotFieldGroupInfo")
533*0e16a31dSJim Jagielski        gi.HasAutoStart = auto
534*0e16a31dSJim Jagielski        gi.HasAutoEnd = auto
535*0e16a31dSJim Jagielski        gi.HasDateValues = True
536*0e16a31dSJim Jagielski        gi.Start = 43831.0 if fname == "Day" else 0.0
537*0e16a31dSJim Jagielski        gi.End = 50000.0 if fname == "Day" else 0.99
538*0e16a31dSJim Jagielski        gi.Step = 1.0 if part == "DAYS" else 0.0
539*0e16a31dSJim Jagielski        gi.GroupBy = uno.getConstantByName(GB + part)
540*0e16a31dSJim Jagielski        f.createDateGroup(gi)
541*0e16a31dSJim Jagielski    k = 0
542*0e16a31dSJim Jagielski    for part, fname in (("YEARS", "Day"), ("QUARTERS", "Day"), ("MONTHS", "Day"), ("DAYS", "Day"), ("HOURS", "Hour"),
543*0e16a31dSJim Jagielski                        ("MINUTES", "Hour"), ("SECONDS", "Hour")):
544*0e16a31dSJim Jagielski        for auto in (True, False):
545*0e16a31dSJim Jagielski            step("dategroup-%s-%s" % (part, auto), date_group, k, part, auto, fname)
546*0e16a31dSJim Jagielski            k += 1
547*0e16a31dSJim Jagielski
548*0e16a31dSJim Jagielski    def num_group(k, auto):
549*0e16a31dSJim Jagielski        t = new_pivot(doc, "Groups", "pn_%d" % k, src, at=(60 + k * 4, 1), fields={
550*0e16a31dSJim Jagielski            "Day": {"Orientation": orient("ROW")}, "Price": {"Orientation": orient("DATA")}})
551*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Day")
552*0e16a31dSJim Jagielski        gi = struct("com.sun.star.sheet.DataPilotFieldGroupInfo")
553*0e16a31dSJim Jagielski        gi.HasAutoStart = auto
554*0e16a31dSJim Jagielski        gi.HasAutoEnd = auto
555*0e16a31dSJim Jagielski        gi.HasDateValues = True
556*0e16a31dSJim Jagielski        gi.Start = 43831.0
557*0e16a31dSJim Jagielski        gi.End = 45000.0
558*0e16a31dSJim Jagielski        gi.Step = 7.0
559*0e16a31dSJim Jagielski        gi.GroupBy = uno.getConstantByName(GB + "DAYS")
560*0e16a31dSJim Jagielski        f.createDateGroup(gi)
561*0e16a31dSJim Jagielski    for k, auto in enumerate((True, False)):
562*0e16a31dSJim Jagielski        step("dayrange-%s" % auto, num_group, k, auto)
563*0e16a31dSJim Jagielski
564*0e16a31dSJim Jagielski    def name_group():
565*0e16a31dSJim Jagielski        t = new_pivot(doc, "Groups", "pg_names", src, at=(80, 1), fields={
566*0e16a31dSJim Jagielski            "Region": {"Orientation": orient("ROW")}, "Qty": {"Orientation": orient("DATA")}})
567*0e16a31dSJim Jagielski        f = t.getDataPilotFields().getByName("Region")
568*0e16a31dSJim Jagielski        f.createNameGroup(("North", "South"))
569*0e16a31dSJim Jagielski        f.createNameGroup(("East",))
570*0e16a31dSJim Jagielski    step("namegroup", name_group)
571*0e16a31dSJim Jagielski    save(doc, "pivot-tables.ods")
572*0e16a31dSJim Jagielski    doc.close(True)
573*0e16a31dSJim Jagielski
574*0e16a31dSJim Jagielski
575*0e16a31dSJim Jagielskistep("pivots", pivots)
576*0e16a31dSJim Jagielski
577*0e16a31dSJim Jagielski
578*0e16a31dSJim Jagielskidef tracked_doc(name, ops):
579*0e16a31dSJim Jagielski    doc = new()
580*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
581*0e16a31dSJim Jagielski    doc.Sheets.insertNewByName("Other", 1)
582*0e16a31dSJim Jagielski    for r in range(8):
583*0e16a31dSJim Jagielski        for c in range(4):
584*0e16a31dSJim Jagielski            sh.getCellByPosition(c, r).Value = r * 10 + c
585*0e16a31dSJim Jagielski        sh.getCellByPosition(4, r).Formula = "=SUM(A%d:D%d)" % (r + 1, r + 1)
586*0e16a31dSJim Jagielski    fr = doc.CurrentController.Frame
587*0e16a31dSJim Jagielski    disp = SM.createInstanceWithContext("com.sun.star.frame.DispatchHelper", CTX)
588*0e16a31dSJim Jagielski    disp.executeDispatch(fr, ".uno:TraceChangeMode", "", 0, (pv("TraceChangeMode", True),))
589*0e16a31dSJim Jagielski    for label, fn in ops:
590*0e16a31dSJim Jagielski        step(name + ":" + label, fn, doc, sh)
591*0e16a31dSJim Jagielski    save(doc, name)
592*0e16a31dSJim Jagielski    doc.close(True)
593*0e16a31dSJim Jagielski
594*0e16a31dSJim Jagielski
595*0e16a31dSJim Jagielskidef tracked_changes():
596*0e16a31dSJim Jagielski    def a(doc, sh):
597*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).Value = 111
598*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).Value = 112
599*0e16a31dSJim Jagielski        sh.getCellByPosition(1, 0).String = "text"
600*0e16a31dSJim Jagielski        sh.getCellByPosition(5, 5).Formula = "=A1+1"
601*0e16a31dSJim Jagielski        sh.getCellByPosition(5, 5).Value = 3
602*0e16a31dSJim Jagielski        sh.getCellRangeByName("A2:B3").clearContents(1 | 2 | 4 | 16)
603*0e16a31dSJim Jagielski    def b(doc, sh):
604*0e16a31dSJim Jagielski        sh.Rows.insertByIndex(2, 2)
605*0e16a31dSJim Jagielski        sh.Rows.insertByIndex(3, 1)
606*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(2, 3)
607*0e16a31dSJim Jagielski        sh.Columns.insertByIndex(1, 2)
608*0e16a31dSJim Jagielski        sh.Columns.removeByIndex(2, 1)
609*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).Value = 7
610*0e16a31dSJim Jagielski    def c(doc, sh):
611*0e16a31dSJim Jagielski        sh.Rows.insertByIndex(4, 3)
612*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 5).Value = 1
613*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(5, 2)
614*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(3, 1)
615*0e16a31dSJim Jagielski    def d(doc, sh):
616*0e16a31dSJim Jagielski        sh.moveRange(sh.getCellByPosition(7, 0).CellAddress, sh.getCellRangeByName("A1:B2").RangeAddress)
617*0e16a31dSJim Jagielski        sh.getCellByPosition(7, 0).Value = 5
618*0e16a31dSJim Jagielski        sh.moveRange(sh.getCellByPosition(9, 3).CellAddress, sh.getCellRangeByName("H1:I2").RangeAddress)
619*0e16a31dSJim Jagielski        sh.copyRange(sh.getCellByPosition(0, 12).CellAddress, sh.getCellRangeByName("C1:D4").RangeAddress)
620*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(0, 1)
621*0e16a31dSJim Jagielski    def e(doc, sh):
622*0e16a31dSJim Jagielski        o = doc.Sheets.getByName("Other")
623*0e16a31dSJim Jagielski        o.getCellByPosition(0, 0).Value = 4
624*0e16a31dSJim Jagielski        o.getCellByPosition(0, 1).Formula = "=$Sheet1.A1+A1"
625*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).Value = 8
626*0e16a31dSJim Jagielski        sh.Columns.removeByIndex(0, 1)
627*0e16a31dSJim Jagielski        doc.Sheets.removeByName("Other")
628*0e16a31dSJim Jagielski    def f(doc, sh):
629*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).String = "multi\nline"
630*0e16a31dSJim Jagielski        sh.getCellByPosition(1, 0).Value = 1.5
631*0e16a31dSJim Jagielski        sh.getCellByPosition(2, 0).Formula = "=1+1"
632*0e16a31dSJim Jagielski        sh.getCellByPosition(3, 0).Formula = '="s"&"t"'
633*0e16a31dSJim Jagielski        sh.getCellByPosition(0, 0).Value = 3
634*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(0, 1)
635*0e16a31dSJim Jagielski        sh.Rows.removeByIndex(0, 4)
636*0e16a31dSJim Jagielski    tracked_doc("tracked-content.ods", [("a", a)])
637*0e16a31dSJim Jagielski    tracked_doc("tracked-insdel.ods", [("b", b)])
638*0e16a31dSJim Jagielski    tracked_doc("tracked-cutoffs.ods", [("c", c)])
639*0e16a31dSJim Jagielski    tracked_doc("tracked-move.ods", [("d", d)])
640*0e16a31dSJim Jagielski    tracked_doc("tracked-sheets.ods", [("e", e)])
641*0e16a31dSJim Jagielski    tracked_doc("tracked-deleted-content.ods", [("f", f)])
642*0e16a31dSJim Jagielski    tracked_doc("tracked-all.ods", [("a", a), ("b", b), ("c", c), ("d", d), ("f", f)])
643*0e16a31dSJim Jagielski
644*0e16a31dSJim Jagielski
645*0e16a31dSJim Jagielskistep("tracked", tracked_changes)
646*0e16a31dSJim Jagielski
647*0e16a31dSJim Jagielski
648*0e16a31dSJim Jagielskidef link_source():
649*0e16a31dSJim Jagielski    path = os.path.join(work, "link-source.ods")
650*0e16a31dSJim Jagielski    doc = new()
651*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
652*0e16a31dSJim Jagielski    sh.Name = "SrcSheet"
653*0e16a31dSJim Jagielski    for r in range(5):
654*0e16a31dSJim Jagielski        for c in range(4):
655*0e16a31dSJim Jagielski            sh.getCellByPosition(c, r).Value = r * 4 + c
656*0e16a31dSJim Jagielski    doc.storeToURL(uno.systemPathToFileUrl(path), (pv("FilterName", "calc8"),))
657*0e16a31dSJim Jagielski    doc.close(True)
658*0e16a31dSJim Jagielski    return uno.systemPathToFileUrl(path)
659*0e16a31dSJim Jagielski
660*0e16a31dSJim Jagielski
661*0e16a31dSJim Jagielskidef links():
662*0e16a31dSJim Jagielski    url = link_source()
663*0e16a31dSJim Jagielski    doc = sheets_doc(4)
664*0e16a31dSJim Jagielski
665*0e16a31dSJim Jagielski    def arealinks():
666*0e16a31dSJim Jagielski        sh = doc.Sheets.getByIndex(0)
667*0e16a31dSJim Jagielski        doc.AreaLinks.insertAtPosition(cell_addr(0, 0, 0), url, "SrcSheet.A1:D5", "calc8", "")
668*0e16a31dSJim Jagielski        doc.AreaLinks.insertAtPosition(cell_addr(0, 6, 0), url, "SrcSheet.B2:C4", "calc8", "")
669*0e16a31dSJim Jagielski        al = doc.AreaLinks.getByIndex(1)
670*0e16a31dSJim Jagielski        al.RefreshDelay = 90
671*0e16a31dSJim Jagielski    step("arealinks", arealinks)
672*0e16a31dSJim Jagielski
673*0e16a31dSJim Jagielski    def sheetlinks():
674*0e16a31dSJim Jagielski        for i, mode in ((1, "NORMAL"), (2, "VALUE")):
675*0e16a31dSJim Jagielski            sh = doc.Sheets.getByIndex(i)
676*0e16a31dSJim Jagielski            sh.link(url, "SrcSheet", "calc8", "", E("SheetLinkMode", mode))
677*0e16a31dSJim Jagielski        sl = doc.SheetLinks
678*0e16a31dSJim Jagielski        for n in sl.ElementNames:
679*0e16a31dSJim Jagielski            l = sl.getByName(n)
680*0e16a31dSJim Jagielski            l.RefreshDelay = 120
681*0e16a31dSJim Jagielski            break
682*0e16a31dSJim Jagielski    step("sheetlinks", sheetlinks)
683*0e16a31dSJim Jagielski    save(doc, "links.ods")
684*0e16a31dSJim Jagielski    doc.close(True)
685*0e16a31dSJim Jagielski
686*0e16a31dSJim Jagielski
687*0e16a31dSJim Jagielskidef scenarios():
688*0e16a31dSJim Jagielski    doc = sheets_doc(1)
689*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
690*0e16a31dSJim Jagielski    for r in range(6):
691*0e16a31dSJim Jagielski        for c in range(4):
692*0e16a31dSJim Jagielski            sh.getCellByPosition(c, r).Value = r * 4 + c
693*0e16a31dSJim Jagielski
694*0e16a31dSJim Jagielski    def mk(name, ranges, comment):
695*0e16a31dSJim Jagielski        doc.Sheets.getByIndex(0).Scenarios.addNewByName(name, ranges, comment)
696*0e16a31dSJim Jagielski        scn = doc.Sheets.getByName(name)
697*0e16a31dSJim Jagielski        return scn
698*0e16a31dSJim Jagielski
699*0e16a31dSJim Jagielski    def sc1():
700*0e16a31dSJim Jagielski        scn = mk("scn_one", (rng_addr(0, 0, 0, 1, 1),), "first scenario")
701*0e16a31dSJim Jagielski        scn.getCellByPosition(0, 0).Value = 100
702*0e16a31dSJim Jagielski        scn.ShowBorder = True
703*0e16a31dSJim Jagielski        scn.PrintBorder = True
704*0e16a31dSJim Jagielski        scn.CopyBack = True
705*0e16a31dSJim Jagielski        scn.CopyStyles = True
706*0e16a31dSJim Jagielski        scn.CopyFormulas = True
707*0e16a31dSJim Jagielski        scn.BorderColor = 0x00FF8000
708*0e16a31dSJim Jagielski        scn.IsActive = True
709*0e16a31dSJim Jagielski    step("scenario-1", sc1)
710*0e16a31dSJim Jagielski
711*0e16a31dSJim Jagielski    def sc2():
712*0e16a31dSJim Jagielski        scn = mk("scn_two", (rng_addr(0, 2, 2, 3, 3), rng_addr(0, 0, 5, 0, 5)), "")
713*0e16a31dSJim Jagielski        scn.ShowBorder = False
714*0e16a31dSJim Jagielski        scn.PrintBorder = False
715*0e16a31dSJim Jagielski        scn.CopyBack = False
716*0e16a31dSJim Jagielski        scn.CopyStyles = False
717*0e16a31dSJim Jagielski        scn.CopyFormulas = False
718*0e16a31dSJim Jagielski        scn.Protected = True
719*0e16a31dSJim Jagielski        scn.IsActive = False
720*0e16a31dSJim Jagielski    step("scenario-2", sc2)
721*0e16a31dSJim Jagielski
722*0e16a31dSJim Jagielski    def sc3():
723*0e16a31dSJim Jagielski        scn = mk("scn_three", (rng_addr(0, 1, 1, 2, 2),), "third \u00e4 & <b>")
724*0e16a31dSJim Jagielski        scn.CopyFormulas = True
725*0e16a31dSJim Jagielski    step("scenario-3", sc3)
726*0e16a31dSJim Jagielski    save(doc, "scenarios.ods")
727*0e16a31dSJim Jagielski    doc.close(True)
728*0e16a31dSJim Jagielski
729*0e16a31dSJim Jagielski
730*0e16a31dSJim Jagielskidef consolidation():
731*0e16a31dSJim Jagielski    doc = sheets_doc(3)
732*0e16a31dSJim Jagielski    for i in range(2):
733*0e16a31dSJim Jagielski        sh = doc.Sheets.getByIndex(i)
734*0e16a31dSJim Jagielski        for r in range(4):
735*0e16a31dSJim Jagielski            sh.getCellByPosition(0, r).String = "k%d" % r
736*0e16a31dSJim Jagielski            for c in range(1, 3):
737*0e16a31dSJim Jagielski                sh.getCellByPosition(c, r).Value = r + c + i
738*0e16a31dSJim Jagielski        sh.getCellByPosition(1, 0).String = "h1"
739*0e16a31dSJim Jagielski        sh.getCellByPosition(2, 0).String = "h2"
740*0e16a31dSJim Jagielski    fns = ["SUM", "COUNT", "AVERAGE", "MAX", "MIN", "PRODUCT", "COUNTNUMS", "STDEV", "STDEVP", "VAR", "VARP"]
741*0e16a31dSJim Jagielski    for k, fn in enumerate(fns[:6]):
742*0e16a31dSJim Jagielski        def one(k=k, fn=fn):
743*0e16a31dSJim Jagielski            cd = doc.createConsolidationDescriptor(True)
744*0e16a31dSJim Jagielski            cd.Function = gf(fn)
745*0e16a31dSJim Jagielski            cd.UseColumnHeaders = k % 2 == 0
746*0e16a31dSJim Jagielski            cd.UseRowHeaders = k % 3 != 0
747*0e16a31dSJim Jagielski            cd.Sources = (rng_addr(0, 0, 0, 2, 3), rng_addr(1, 0, 0, 2, 3))
748*0e16a31dSJim Jagielski            cd.StartOutputPosition = cell_addr(2, 0, k * 6)
749*0e16a31dSJim Jagielski            cd.InsertLinks = k % 2 == 1
750*0e16a31dSJim Jagielski            doc.consolidate(cd)
751*0e16a31dSJim Jagielski        step("consolidate-" + fn, one)
752*0e16a31dSJim Jagielski        if k == 5:
753*0e16a31dSJim Jagielski            break
754*0e16a31dSJim Jagielski    save(doc, "consolidation.ods")
755*0e16a31dSJim Jagielski    doc.close(True)
756*0e16a31dSJim Jagielski
757*0e16a31dSJim Jagielski    for fn, rows, cols, links in (("SUM", False, False, False), ("MAX", True, True, True), ("MIN", False, True, False)):
758*0e16a31dSJim Jagielski        doc = sheets_doc(3)
759*0e16a31dSJim Jagielski        for i in range(2):
760*0e16a31dSJim Jagielski            sh = doc.Sheets.getByIndex(i)
761*0e16a31dSJim Jagielski            for r in range(4):
762*0e16a31dSJim Jagielski                sh.getCellByPosition(0, r).String = "k%d" % r
763*0e16a31dSJim Jagielski                for c in range(1, 3):
764*0e16a31dSJim Jagielski                    sh.getCellByPosition(c, r).Value = r + c + i
765*0e16a31dSJim Jagielski
766*0e16a31dSJim Jagielski        def one():
767*0e16a31dSJim Jagielski            cd = doc.createConsolidationDescriptor(True)
768*0e16a31dSJim Jagielski            cd.Function = gf(fn)
769*0e16a31dSJim Jagielski            cd.UseColumnHeaders = cols
770*0e16a31dSJim Jagielski            cd.UseRowHeaders = rows
771*0e16a31dSJim Jagielski            cd.Sources = (rng_addr(0, 0, 0, 2, 3), rng_addr(1, 0, 0, 2, 3))
772*0e16a31dSJim Jagielski            cd.StartOutputPosition = cell_addr(2, 0, 0)
773*0e16a31dSJim Jagielski            cd.InsertLinks = links
774*0e16a31dSJim Jagielski            doc.consolidate(cd)
775*0e16a31dSJim Jagielski        step("consolidate-%s-%s%s" % (fn, rows, cols), one)
776*0e16a31dSJim Jagielski        save(doc, "consolidation-%s.ods" % fn.lower())
777*0e16a31dSJim Jagielski        doc.close(True)
778*0e16a31dSJim Jagielski
779*0e16a31dSJim Jagielski
780*0e16a31dSJim Jagielskidef detective():
781*0e16a31dSJim Jagielski    doc = new()
782*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
783*0e16a31dSJim Jagielski    for r in range(4):
784*0e16a31dSJim Jagielski        sh.getCellByPosition(0, r).Value = r + 1
785*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 0).Formula = "=SUM(A1:A4)"
786*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 1).Formula = "=B1*2"
787*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 2).Formula = "=1/0"
788*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 3).Formula = "=B3+1"
789*0e16a31dSJim Jagielski    v = sh.getCellByPosition(2, 0).Validation
790*0e16a31dSJim Jagielski    v.Type = E("ValidationType", "WHOLE")
791*0e16a31dSJim Jagielski    v.setOperator(E("ConditionOperator", "BETWEEN"))
792*0e16a31dSJim Jagielski    v.setFormula1("1")
793*0e16a31dSJim Jagielski    v.setFormula2("10")
794*0e16a31dSJim Jagielski    sh.getCellByPosition(2, 0).Validation = v
795*0e16a31dSJim Jagielski    sh.getCellByPosition(2, 0).Value = 99
796*0e16a31dSJim Jagielski    ctrl = doc.CurrentController
797*0e16a31dSJim Jagielski    disp = SM.createInstanceWithContext("com.sun.star.frame.DispatchHelper", CTX)
798*0e16a31dSJim Jagielski
799*0e16a31dSJim Jagielski    def do(cmd, addr):
800*0e16a31dSJim Jagielski        ctrl.select(sh.getCellRangeByName(addr))
801*0e16a31dSJim Jagielski        disp.executeDispatch(ctrl.Frame, ".uno:" + cmd, "", 0, ())
802*0e16a31dSJim Jagielski    for cmd, addr in (("ShowPrecedents", "B1"), ("ShowDependents", "A2"), ("ShowDependents", "B2"), ("ShowPrecedents", "B2"), ("ShowErrors", "B4"),
803*0e16a31dSJim Jagielski                      ("MarkErrors", "B3"), ("ShowInvalid", "C1"), ("AddPrecedents", "B1"), ("AddDependents", "A1"), ("ShowDependents", "A4"),
804*0e16a31dSJim Jagielski                      ("ShowPrecedents", "B4")):
805*0e16a31dSJim Jagielski        step("detective-" + cmd, do, cmd, addr)
806*0e16a31dSJim Jagielski    save(doc, "detective.ods")
807*0e16a31dSJim Jagielski    doc.close(True)
808*0e16a31dSJim Jagielski
809*0e16a31dSJim Jagielski
810*0e16a31dSJim Jagielskidef shapes_doc():
811*0e16a31dSJim Jagielski    doc = sheets_doc(2)
812*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
813*0e16a31dSJim Jagielski    for r in range(5):
814*0e16a31dSJim Jagielski        sh.getCellByPosition(0, r).Value = r
815*0e16a31dSJim Jagielski    dp = sh.DrawPage
816*0e16a31dSJim Jagielski
817*0e16a31dSJim Jagielski    def add(kind, x, y, w, h, anchor_cell=None, text=""):
818*0e16a31dSJim Jagielski        shp = doc.createInstance("com.sun.star.drawing." + kind)
819*0e16a31dSJim Jagielski        dp.add(shp)
820*0e16a31dSJim Jagielski        shp.Position = struct("com.sun.star.awt.Point", x, y)
821*0e16a31dSJim Jagielski        shp.Size = struct("com.sun.star.awt.Size", w, h)
822*0e16a31dSJim Jagielski        if text:
823*0e16a31dSJim Jagielski            shp.String = text
824*0e16a31dSJim Jagielski        shp.Anchor = sh.getCellRangeByName(anchor_cell) if anchor_cell is not None else sh
825*0e16a31dSJim Jagielski        return shp
826*0e16a31dSJim Jagielski    step("shape-page-rect", add, "RectangleShape", 3000, 1000, 3000, 2000, None, "page anchored")
827*0e16a31dSJim Jagielski    step("shape-page-ellipse", add, "EllipseShape", 7000, 1000, 2000, 2000)
828*0e16a31dSJim Jagielski    step("shape-cell-rect", add, "RectangleShape", 3000, 4000, 3000, 2000, "B3", "cell anchored")
829*0e16a31dSJim Jagielski    step("shape-cell-line", add, "LineShape", 3000, 7000, 3000, 1000, "C5")
830*0e16a31dSJim Jagielski    step("shape-cell-text", add, "TextShape", 8000, 5000, 3000, 1000, "D2", "text")
831*0e16a31dSJim Jagielski    step("shape-conn", add, "ConnectorShape", 1000, 9000, 3000, 1000)
832*0e16a31dSJim Jagielski    sh2 = doc.Sheets.getByIndex(1)
833*0e16a31dSJim Jagielski    shp = doc.createInstance("com.sun.star.drawing.RectangleShape")
834*0e16a31dSJim Jagielski    sh2.DrawPage.add(shp)
835*0e16a31dSJim Jagielski    shp.Size = struct("com.sun.star.awt.Size", 2000, 1000)
836*0e16a31dSJim Jagielski    shp.Position = struct("com.sun.star.awt.Point", 500, 500)
837*0e16a31dSJim Jagielski    save(doc, "shapes.ods")
838*0e16a31dSJim Jagielski    doc.close(True)
839*0e16a31dSJim Jagielski
840*0e16a31dSJim Jagielski
841*0e16a31dSJim Jagielskidef calc_settings():
842*0e16a31dSJim Jagielski    docs = {"calc-settings-a.ods": dict(CalcAsShown=True, IgnoreCase=True, LookUpLabels=False, MatchWholeCell=False, RegularExpressions=False,
843*0e16a31dSJim Jagielski                                        IsIterationEnabled=True, IterationCount=250, IterationEpsilon=0.0005, NullDate=struct("com.sun.star.util.Date", 1, 1, 1904)),
844*0e16a31dSJim Jagielski            "calc-settings-b.ods": dict(IterationCount=50, NullDate=struct("com.sun.star.util.Date", 31, 12, 1899)),
845*0e16a31dSJim Jagielski            "calc-settings-c.ods": dict(IsIterationEnabled=True, IterationEpsilon=0.01),
846*0e16a31dSJim Jagielski            "calc-settings-d.ods": dict(CalcAsShown=True),
847*0e16a31dSJim Jagielski            "calc-settings-e.ods": dict(MatchWholeCell=False),
848*0e16a31dSJim Jagielski            "calc-settings-f.ods": dict(LookUpLabels=False),
849*0e16a31dSJim Jagielski            "calc-settings-g.ods": dict(RegularExpressions=False),
850*0e16a31dSJim Jagielski            "calc-settings-h.ods": dict(IgnoreCase=True)}
851*0e16a31dSJim Jagielski    for name, props in docs.items():
852*0e16a31dSJim Jagielski        doc = new()
853*0e16a31dSJim Jagielski        for k, v in props.items():
854*0e16a31dSJim Jagielski            step(name + ":" + k, doc.setPropertyValue, k, v)
855*0e16a31dSJim Jagielski        doc.Sheets.getByIndex(0).getCellByPosition(0, 0).Value = 1.234
856*0e16a31dSJim Jagielski        save(doc, name)
857*0e16a31dSJim Jagielski        doc.close(True)
858*0e16a31dSJim Jagielski
859*0e16a31dSJim Jagielski
860*0e16a31dSJim Jagielskidef null_year():
861*0e16a31dSJim Jagielski    cp = SM.createInstanceWithContext("com.sun.star.configuration.ConfigurationProvider", CTX)
862*0e16a31dSJim Jagielski    ca = cp.createInstanceWithArguments("com.sun.star.configuration.ConfigurationUpdateAccess", (pv("nodepath", "/org.openoffice.Office.Common/DateFormat"),))
863*0e16a31dSJim Jagielski    old = ca.getByName("TwoDigitYear")
864*0e16a31dSJim Jagielski    try:
865*0e16a31dSJim Jagielski        ca.setPropertyValue("TwoDigitYear", 1950)
866*0e16a31dSJim Jagielski        ca.commitChanges()
867*0e16a31dSJim Jagielski        doc = new()
868*0e16a31dSJim Jagielski        doc.Sheets.getByIndex(0).getCellByPosition(0, 0).Value = 1.5
869*0e16a31dSJim Jagielski        save(doc, "calc-settings-null-year.ods")
870*0e16a31dSJim Jagielski        doc.close(True)
871*0e16a31dSJim Jagielski    finally:
872*0e16a31dSJim Jagielski        ca2 = cp.createInstanceWithArguments("com.sun.star.configuration.ConfigurationUpdateAccess", (pv("nodepath", "/org.openoffice.Office.Common/DateFormat"),))
873*0e16a31dSJim Jagielski        ca2.setPropertyValue("TwoDigitYear", old)
874*0e16a31dSJim Jagielski        ca2.commitChanges()
875*0e16a31dSJim Jagielski
876*0e16a31dSJim Jagielski
877*0e16a31dSJim Jagielskidef db_imports():
878*0e16a31dSJim Jagielski    dbc = SM.createInstance("com.sun.star.sdb.DatabaseContext")
879*0e16a31dSJim Jagielski    if "Bibliography" not in dbc.ElementNames:
880*0e16a31dSJim Jagielski        print("FAIL db_imports: no Bibliography data source")
881*0e16a31dSJim Jagielski        return
882*0e16a31dSJim Jagielski    ds = dbc.getByName("Bibliography")
883*0e16a31dSJim Jagielski    qd = ds.QueryDefinitions
884*0e16a31dSJim Jagielski    if not qd.hasByName("t12q"):
885*0e16a31dSJim Jagielski        q = qd.createInstance()
886*0e16a31dSJim Jagielski        q.Command = "SELECT * FROM biblio"
887*0e16a31dSJim Jagielski        qd.insertByName("t12q", q)
888*0e16a31dSJim Jagielski    doc = sheets_doc(6)
889*0e16a31dSJim Jagielski    DIM = "com.sun.star.sheet.DataImportMode"
890*0e16a31dSJim Jagielski    cases = [("TABLE", "biblio", False, {}), ("SQL", "SELECT * FROM biblio", False, {}), ("SQL", "SELECT * FROM biblio", True, {}),
891*0e16a31dSJim Jagielski             ("QUERY", "t12q", False, {}), ("TABLE", "biblio", False, {"StripData": True}), ("TABLE", "biblio", False, {"FromSelection": True})]
892*0e16a31dSJim Jagielski    for i, (t, obj, native, extra) in enumerate(cases):
893*0e16a31dSJim Jagielski        def one(i=i, t=t, obj=obj, native=native, extra=extra):
894*0e16a31dSJim Jagielski            doc.DatabaseRanges.addNewByName("imp%d" % i, rng_addr(i, 0, 0, 2, 3))
895*0e16a31dSJim Jagielski            doc.Sheets.getByIndex(i).getCellRangeByPosition(0, 0, 2, 3).doImport(
896*0e16a31dSJim Jagielski                (pv("DatabaseName", "Bibliography"), pv("SourceType", uno.Enum(DIM, t)), pv("SourceObject", obj), pv("IsNative", native)))
897*0e16a31dSJim Jagielski            db = doc.DatabaseRanges.getByName("imp%d" % i)
898*0e16a31dSJim Jagielski            for k, v in extra.items():
899*0e16a31dSJim Jagielski                db.setPropertyValue(k, v)
900*0e16a31dSJim Jagielski        step("import-%s-%d" % (t, i), one)
901*0e16a31dSJim Jagielski    save(doc, "dbranges-import.ods")
902*0e16a31dSJim Jagielski    doc.close(True)
903*0e16a31dSJim Jagielski
904*0e16a31dSJim Jagielski
905*0e16a31dSJim Jagielskistep("null_year", null_year)
906*0e16a31dSJim Jagielskistep("db_imports", db_imports)
907*0e16a31dSJim Jagielskistep("links", links)
908*0e16a31dSJim Jagielskistep("scenarios", scenarios)
909*0e16a31dSJim Jagielskistep("consolidation", consolidation)
910*0e16a31dSJim Jagielskistep("detective", detective)
911*0e16a31dSJim Jagielskistep("shapes", shapes_doc)
912*0e16a31dSJim Jagielskistep("calc_settings", calc_settings)
913*0e16a31dSJim Jagielski
914*0e16a31dSJim Jagielski
915*0e16a31dSJim JagielskiCATEGORIES = {1: "database", 2: "datetime", 3: "financial", 4: "information", 5: "logical", 6: "math", 7: "matrix", 8: "statistical",
916*0e16a31dSJim Jagielski              9: "spreadsheet", 10: "text", 11: "addin"}
917*0e16a31dSJim Jagielski
918*0e16a31dSJim Jagielski
919*0e16a31dSJim Jagielskidef functions():
920*0e16a31dSJim Jagielski    fdesc = SM.createInstance("com.sun.star.sheet.FunctionDescriptions")
921*0e16a31dSJim Jagielski    by_cat = {}
922*0e16a31dSJim Jagielski    for i in range(fdesc.getCount()):
923*0e16a31dSJim Jagielski        d = {p.Name: p.Value for p in fdesc.getByIndex(i)}
924*0e16a31dSJim Jagielski        by_cat.setdefault(d["Category"], []).append(d)
925*0e16a31dSJim Jagielski    print("function descriptions:", {CATEGORIES.get(k, k): len(v) for k, v in sorted(by_cat.items())})
926*0e16a31dSJim Jagielski    for cat, items in sorted(by_cat.items()):
927*0e16a31dSJim Jagielski        doc = new()
928*0e16a31dSJim Jagielski        sh = doc.Sheets.getByIndex(0)
929*0e16a31dSJim Jagielski        for r in range(1, 8):
930*0e16a31dSJim Jagielski            for c in range(4):
931*0e16a31dSJim Jagielski                sh.getCellByPosition(c, r + 20).Value = r + c
932*0e16a31dSJim Jagielski        failed_fns = []
933*0e16a31dSJim Jagielski        for k, d in enumerate(items):
934*0e16a31dSJim Jagielski            args = d.get("Arguments") or ()
935*0e16a31dSJim Jagielski            n = sum(1 for a in args if not a.IsOptional)
936*0e16a31dSJim Jagielski            if n == 0 and args:
937*0e16a31dSJim Jagielski                n = 1
938*0e16a31dSJim Jagielski            if cat == 10:
939*0e16a31dSJim Jagielski                vals = ['"abc"', "2", "1", '"b"']
940*0e16a31dSJim Jagielski            elif cat == 1:
941*0e16a31dSJim Jagielski                vals = ["$A$21:$D$27", "2", "$A$21:$A$22"]
942*0e16a31dSJim Jagielski            elif cat in (7, 9, 8):
943*0e16a31dSJim Jagielski                vals = ["$A$21:$B$24", "$C$21:$D$24", "2", "1"]
944*0e16a31dSJim Jagielski            else:
945*0e16a31dSJim Jagielski                vals = ["2", "1", "3", "4", "5", "6", "7"]
946*0e16a31dSJim Jagielski            text = "=%s(%s)" % (d["Name"], ";".join((vals + ["1"] * 8)[:n])) if n else "=%s()" % d["Name"]
947*0e16a31dSJim Jagielski            cell = sh.getCellByPosition((k // 100) * 3, k % 100)
948*0e16a31dSJim Jagielski            try:
949*0e16a31dSJim Jagielski                cell.Formula = text
950*0e16a31dSJim Jagielski            except Exception as e:
951*0e16a31dSJim Jagielski                failed_fns.append(d["Name"])
952*0e16a31dSJim Jagielski        print("functions", CATEGORIES.get(cat, cat), len(items), "unparsed:", failed_fns[:10])
953*0e16a31dSJim Jagielski        save(doc, "formulas-%02d-%s.ods" % (cat, CATEGORIES.get(cat, cat)))
954*0e16a31dSJim Jagielski        doc.close(True)
955*0e16a31dSJim Jagielski
956*0e16a31dSJim Jagielski
957*0e16a31dSJim Jagielskidef formula_forms():
958*0e16a31dSJim Jagielski    doc = sheets_doc(3)
959*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
960*0e16a31dSJim Jagielski    sh.Name = "Data sheet"
961*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(1).Name = "it's"
962*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(2).Name = "Sheet 3"
963*0e16a31dSJim Jagielski    for r in range(10):
964*0e16a31dSJim Jagielski        sh.getCellByPosition(0, r).Value = r
965*0e16a31dSJim Jagielski        sh.getCellByPosition(1, r).String = "t%d" % r
966*0e16a31dSJim Jagielski    forms = ["=1+2*3-4/5^2", "=-A1", "=A1%", "=+A2", "=A1&B1&\"x\"", "=A1=A2", "=A1<>A2", "=A1<=A2", "=A1>=A2", "=A1<A2", "=A1>A2",
967*0e16a31dSJim Jagielski             "=SUM(A1:A5 A3:A7)", "=SUM(A1:A3~A5:A7)", "=SUM($A$1:$A$3)", "=SUM(A:A)", "=SUM(1:3)", "=SUM($'Data sheet'.A1:A3)",
968*0e16a31dSJim Jagielski             "=SUM($'it''s'.A1:$'Sheet 3'.A3)", "=$'it''s'.A1", "=SUM(A1:A3,B1)" , "={1;2;3}", "={1;2|3;4}", "={\"a\";\"b\"}", "=SUM({1;2|3;4})",
969*0e16a31dSJim Jagielski             "=#N/A", "=ERROR.TYPE(#DIV/0!)", "=ISERROR(#VALUE!)", "=IF(A1;\"y\";\"n\")", "=TRUE()", "=FALSE()", "=TRUE", "=PI()", "=1E3+1.5E-3",
970*0e16a31dSJim Jagielski             "=0.5+.5", "=\"he said \"\"hi\"\"\"", "=(1+2)*(3+4)", "=SUM((A1:A3;A5:A7))", "=A1:A3*2", "=ROW(A1:A3)", "=SUM(IF(A1:A5>2;1;0))",
971*0e16a31dSJim Jagielski             "=INDIRECT(\"A1\")", "=OFFSET(A1;1;1)", "=HYPERLINK(\"http://example.org\";\"x\")", "=VLOOKUP(2;A1:B5;2;0)", "=MULTIPLE.OPERATIONS(A1;A2;A3)",
972*0e16a31dSJim Jagielski             "=TEXT(A1;\"0.00\")", "=DDE(\"soffice\";\"x\";\"y\")", "=A1+$Data sheet.A2" ]
973*0e16a31dSJim Jagielski    for i, f in enumerate(forms):
974*0e16a31dSJim Jagielski        def put(i=i, f=f):
975*0e16a31dSJim Jagielski            sh.getCellByPosition(3, i).Formula = f
976*0e16a31dSJim Jagielski        step("form-%d" % i, put)
977*0e16a31dSJim Jagielski    step("array-1", sh.getCellRangeByName("F1:G2").setArrayFormula, "=A1:B2*2")
978*0e16a31dSJim Jagielski    step("array-2", sh.getCellRangeByName("H1:H3").setArrayFormula, "=A1:A3+1")
979*0e16a31dSJim Jagielski    step("array-3", sh.getCellRangeByName("J1:K1").setArrayFormula, "=TRANSPOSE(A1:A2)")
980*0e16a31dSJim Jagielski    sh.getCellByPosition(14, 0).Formula = "=A5*2+A6"
981*0e16a31dSJim Jagielski    # setTableOperation on the Spreadsheet object itself fills the whole sheet and keeps the office busy for minutes
982*0e16a31dSJim Jagielski    step("tableop-col", sh.getCellRangeByName("O2:O5").setTableOperation, sh.getCellRangeByName("N1:O1").RangeAddress, E("TableOperationMode", "COLUMN"), cell_addr(0, 0, 4), cell_addr(0, 0, 5))
983*0e16a31dSJim Jagielski    step("tableop-row", sh.getCellRangeByName("Q2:R2").setTableOperation, sh.getCellRangeByName("P1:P2").RangeAddress, E("TableOperationMode", "ROW"), cell_addr(0, 0, 4), cell_addr(0, 0, 5))
984*0e16a31dSJim Jagielski    step("tableop-both", sh.getCellRangeByName("U2:W4").setTableOperation, sh.getCellRangeByName("T1:T1").RangeAddress, E("TableOperationMode", "BOTH"), cell_addr(0, 0, 4), cell_addr(0, 0, 5))
985*0e16a31dSJim Jagielski    save(doc, "formulas-forms.ods")
986*0e16a31dSJim Jagielski    doc.close(True)
987*0e16a31dSJim Jagielski
988*0e16a31dSJim Jagielski
989*0e16a31dSJim Jagielskidef names_and_ranges():
990*0e16a31dSJim Jagielski    doc = sheets_doc(2)
991*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
992*0e16a31dSJim Jagielski    fill_table(sh)
993*0e16a31dSJim Jagielski    nr = doc.NamedRanges
994*0e16a31dSJim Jagielski    NF = "com.sun.star.sheet.NamedRangeFlag."
995*0e16a31dSJim Jagielski    spec = [("rng_abs", "$Sheet1.$A$1:$C$5", 0), ("rng_rel", "A1:B2", 0), ("expr_const", "0.19", 0), ("expr_formula", "SUM($Sheet1.$C$2:$C$9)*2", 0),
996*0e16a31dSJim Jagielski            ("expr_rel", "$Sheet1.A2*2", 0), ("expr_text", "\"abc\"", 0), ("expr_func", "TODAY()+1", 0),
997*0e16a31dSJim Jagielski            ("print_ar", "$Sheet1.$A$1:$E$12", 2), ("crit", "$Sheet1.$A$1:$B$2", 1), ("colhdr", "$Sheet1.$A$1:$E$1", 4), ("rowhdr", "$Sheet1.$A$1:$A$12", 8),
998*0e16a31dSJim Jagielski            ("all_flags", "$Sheet1.$A$1:$E$12", 1 | 2 | 4 | 8), ("na\u00efve", "$Sheet1.$B$2", 0), ("nested", "rng_abs", 0), ("sheet2ref", "$Sheet2.$A$1", 0),
999*0e16a31dSJim Jagielski            ("array_c", "{1;2|3;4}", 0), ("err_ref", "#REF!", 0)]
1000*0e16a31dSJim Jagielski    for n, c, fl in spec:
1001*0e16a31dSJim Jagielski        step("name-" + n, nr.addNewByName, n, c, cell_addr(0, 0, 0), fl)
1002*0e16a31dSJim Jagielski    for i, rng in enumerate(("A2:A12", "B2:B12")):
1003*0e16a31dSJim Jagielski        step("collabel-%d" % i, doc.ColumnLabelRanges.addNew, rng_addr(0, i, 0, i, 0), rng_addr(0, i, 1, i, 11))
1004*0e16a31dSJim Jagielski    step("rowlabel-1", doc.RowLabelRanges.addNew, rng_addr(0, 0, 1, 0, 11), rng_addr(0, 1, 1, 4, 11))
1005*0e16a31dSJim Jagielski    step("rowlabel-2", doc.RowLabelRanges.addNew, rng_addr(1, 0, 0, 0, 3), rng_addr(1, 1, 0, 3, 3))
1006*0e16a31dSJim Jagielski    step("print-areas", sh.setPrintAreas, (rng_addr(0, 0, 0, 4, 5), rng_addr(0, 0, 8, 4, 11)))
1007*0e16a31dSJim Jagielski    step("title-rows", sh.setPrintTitleRows, True)
1008*0e16a31dSJim Jagielski    step("title-rows-r", sh.setTitleRows, rng_addr(0, 0, 0, 0, 0))
1009*0e16a31dSJim Jagielski    step("title-cols", sh.setPrintTitleColumns, True)
1010*0e16a31dSJim Jagielski    step("title-cols-r", sh.setTitleColumns, rng_addr(0, 0, 0, 0, 0))
1011*0e16a31dSJim Jagielski    save(doc, "named-ranges.ods")
1012*0e16a31dSJim Jagielski    doc.close(True)
1013*0e16a31dSJim Jagielski
1014*0e16a31dSJim Jagielski
1015*0e16a31dSJim Jagielskidef conditional_formats():
1016*0e16a31dSJim Jagielski    doc = sheets_doc(1)
1017*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
1018*0e16a31dSJim Jagielski    fill_table(sh)
1019*0e16a31dSJim Jagielski    styles = doc.StyleFamilies.getByName("CellStyles")
1020*0e16a31dSJim Jagielski    for n in ("cf_a", "cf_b", "cf_c"):
1021*0e16a31dSJim Jagielski        st = doc.createInstance("com.sun.star.style.CellStyle")
1022*0e16a31dSJim Jagielski        styles.insertByName(n, st)
1023*0e16a31dSJim Jagielski        st.CellBackColor = {"cf_a": 0xFF0000, "cf_b": 0x00FF00, "cf_c": 0x0000FF}[n]
1024*0e16a31dSJim Jagielski    ops = ["EQUAL", "NOT_EQUAL", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL", "BETWEEN", "NOT_BETWEEN", "FORMULA"]
1025*0e16a31dSJim Jagielski    for i, op in enumerate(ops):
1026*0e16a31dSJim Jagielski        def one(i=i, op=op):
1027*0e16a31dSJim Jagielski            rg = sh.getCellRangeByName("%s2:%s12" % ("ABCDEFGHI"[i], "ABCDEFGHI"[i]))
1028*0e16a31dSJim Jagielski            cf = rg.ConditionalFormat
1029*0e16a31dSJim Jagielski            f1, f2 = ("5", "9") if op in ("BETWEEN", "NOT_BETWEEN") else (("$A$2>0", "") if op == "FORMULA" else ("5", ""))
1030*0e16a31dSJim Jagielski            cf.addNew((pv("Operator", E("ConditionOperator", op)), pv("Formula1", f1), pv("Formula2", f2),
1031*0e16a31dSJim Jagielski                       pv("SourcePosition", cell_addr(0, i, 1)), pv("StyleName", "cf_a")))
1032*0e16a31dSJim Jagielski            cf.addNew((pv("Operator", E("ConditionOperator", "GREATER")), pv("Formula1", '"text"'), pv("Formula2", ""),
1033*0e16a31dSJim Jagielski                       pv("SourcePosition", cell_addr(0, i, 1)), pv("StyleName", "cf_b")))
1034*0e16a31dSJim Jagielski            cf.addNew((pv("Operator", E("ConditionOperator", "FORMULA")), pv("Formula1", "ISEVEN(A2)"), pv("Formula2", ""),
1035*0e16a31dSJim Jagielski                       pv("SourcePosition", cell_addr(0, i, 1)), pv("StyleName", "cf_c")))
1036*0e16a31dSJim Jagielski            rg.ConditionalFormat = cf
1037*0e16a31dSJim Jagielski        step("cf-" + op, one)
1038*0e16a31dSJim Jagielski    save(doc, "conditional-formats.ods")
1039*0e16a31dSJim Jagielski    doc.close(True)
1040*0e16a31dSJim Jagielski
1041*0e16a31dSJim Jagielski
1042*0e16a31dSJim Jagielskidef table_attrs():
1043*0e16a31dSJim Jagielski    doc = sheets_doc(4)
1044*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
1045*0e16a31dSJim Jagielski    fill_table(sh)
1046*0e16a31dSJim Jagielski    for r in range(8):
1047*0e16a31dSJim Jagielski        sh.getCellByPosition(8, r).Value = r / 24.0 + 0.1
1048*0e16a31dSJim Jagielski    nf = doc.NumberFormats
1049*0e16a31dSJim Jagielski    loc = struct("com.sun.star.lang.Locale", "en", "US", "")
1050*0e16a31dSJim Jagielski    tk = nf.queryKey("HH:MM:SS", loc, False)
1051*0e16a31dSJim Jagielski    if tk == -1:
1052*0e16a31dSJim Jagielski        tk = nf.addNew("HH:MM:SS", loc)
1053*0e16a31dSJim Jagielski    for r in range(8):
1054*0e16a31dSJim Jagielski        sh.getCellByPosition(8, r).NumberFormat = tk
1055*0e16a31dSJim Jagielski    dk = nf.queryKey("[HH]:MM:SS", loc, False)
1056*0e16a31dSJim Jagielski    if dk == -1:
1057*0e16a31dSJim Jagielski        dk = nf.addNew("[HH]:MM:SS", loc)
1058*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 0).Value = 2.5
1059*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 0).NumberFormat = dk
1060*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 1).Formula = "=TIME(1;2;3)"
1061*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 1).NumberFormat = tk
1062*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 2).Formula = '=TIME(1;2;3)+DATE(2020;1;1)'
1063*0e16a31dSJim Jagielski    ck = nf.queryKey('#,##0.00 [$EUR]', loc, False)
1064*0e16a31dSJim Jagielski    if ck == -1:
1065*0e16a31dSJim Jagielski        ck = nf.addNew('#,##0.00 [$EUR]', loc)
1066*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 3).Value = 12.5
1067*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 3).NumberFormat = ck
1068*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 5).Formula = "=TRUE()"
1069*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 6).Formula = '="s"'
1070*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 7).Formula = "=1/0"
1071*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 8).Formula = "=NA()"
1072*0e16a31dSJim Jagielski    sh.getCellByPosition(9, 9).String = "multi\nline  two  spaces\ttab"
1073*0e16a31dSJim Jagielski    sh.Columns.getByIndex(5).IsVisible = False
1074*0e16a31dSJim Jagielski    sh.Columns.getByIndex(7).IsVisible = False
1075*0e16a31dSJim Jagielski    sh.Rows.getByIndex(3).IsVisible = False
1076*0e16a31dSJim Jagielski    sh.Rows.getByIndex(4).IsVisible = False
1077*0e16a31dSJim Jagielski    sh.Rows.getByIndex(6).Height = 1500
1078*0e16a31dSJim Jagielski    sh.Columns.getByIndex(2).Width = 4000
1079*0e16a31dSJim Jagielski    sh.Columns.getByIndex(3).OptimalWidth = True
1080*0e16a31dSJim Jagielski    sh.Rows.getByIndex(7).OptimalHeight = True
1081*0e16a31dSJim Jagielski    sh.Columns.getByIndex(4).IsStartOfNewPage = True
1082*0e16a31dSJim Jagielski    sh.Rows.getByIndex(9).IsStartOfNewPage = True
1083*0e16a31dSJim Jagielski    step("group-rows", sh.group, rng_addr(0, 0, 14, 0, 18), uno.Enum("com.sun.star.table.TableOrientation", "ROWS"))
1084*0e16a31dSJim Jagielski    step("group-rows2", sh.group, rng_addr(0, 0, 15, 0, 16), uno.Enum("com.sun.star.table.TableOrientation", "ROWS"))
1085*0e16a31dSJim Jagielski    step("group-cols", sh.group, rng_addr(0, 11, 0, 13, 0), uno.Enum("com.sun.star.table.TableOrientation", "COLUMNS"))
1086*0e16a31dSJim Jagielski    step("group-cols2", sh.group, rng_addr(0, 20, 0, 22, 0), uno.Enum("com.sun.star.table.TableOrientation", "COLUMNS"))
1087*0e16a31dSJim Jagielski    step("hide-grp-rows", sh.hideDetail, rng_addr(0, 0, 15, 0, 16))
1088*0e16a31dSJim Jagielski    step("hide-grp-cols", sh.hideDetail, rng_addr(0, 20, 0, 22, 0))
1089*0e16a31dSJim Jagielski    m = sh.getCellRangeByName("L5:N7")
1090*0e16a31dSJim Jagielski    for c in range(11, 14):
1091*0e16a31dSJim Jagielski        for r in range(4, 7):
1092*0e16a31dSJim Jagielski            cell = sh.getCellByPosition(c, r)
1093*0e16a31dSJim Jagielski            if (c, r) == (11, 4):
1094*0e16a31dSJim Jagielski                cell.String = "top left"
1095*0e16a31dSJim Jagielski            elif (c, r) == (12, 4):
1096*0e16a31dSJim Jagielski                cell.Value = 7
1097*0e16a31dSJim Jagielski            elif (c, r) == (13, 4):
1098*0e16a31dSJim Jagielski                cell.Formula = "=1+1"
1099*0e16a31dSJim Jagielski            elif (c, r) == (11, 5):
1100*0e16a31dSJim Jagielski                cell.String = "covered text"
1101*0e16a31dSJim Jagielski                cell.NumberFormat = nf.getStandardFormat(1, loc)
1102*0e16a31dSJim Jagielski            elif (c, r) == (12, 5):
1103*0e16a31dSJim Jagielski                cell.Value = 43831
1104*0e16a31dSJim Jagielski                cell.NumberFormat = nf.getStandardFormat(2, loc)
1105*0e16a31dSJim Jagielski            elif (c, r) == (13, 5):
1106*0e16a31dSJim Jagielski                cell.Value = 0.25
1107*0e16a31dSJim Jagielski                cell.NumberFormat = tk
1108*0e16a31dSJim Jagielski            elif (c, r) == (11, 6):
1109*0e16a31dSJim Jagielski                cell.Formula = "=TRUE()"
1110*0e16a31dSJim Jagielski            elif (c, r) == (12, 6):
1111*0e16a31dSJim Jagielski                cell.Value = 5
1112*0e16a31dSJim Jagielski                cell.NumberFormat = ck
1113*0e16a31dSJim Jagielski            else:
1114*0e16a31dSJim Jagielski                cell.Formula = '="f"&"x"'
1115*0e16a31dSJim Jagielski    v = sh.getCellByPosition(12, 4).Validation
1116*0e16a31dSJim Jagielski    v.Type = E("ValidationType", "WHOLE")
1117*0e16a31dSJim Jagielski    v.setOperator(E("ConditionOperator", "GREATER"))
1118*0e16a31dSJim Jagielski    v.setFormula1("1")
1119*0e16a31dSJim Jagielski    sh.getCellByPosition(12, 4).Validation = v
1120*0e16a31dSJim Jagielski    step("merge", m.merge, True)
1121*0e16a31dSJim Jagielski    sh.getCellRangeByName("P1:Q1").merge(True)
1122*0e16a31dSJim Jagielski    sh.getCellRangeByName("P3:P5").merge(True)
1123*0e16a31dSJim Jagielski    sh.getCellRangeByName("S1:T2").setArrayFormula("=A2:B3+1")
1124*0e16a31dSJim Jagielski    sh.getCellRangeByName("V1:V3").setArrayFormula("=A2:A4*2")
1125*0e16a31dSJim Jagielski    sh.getCellRangeByName("A1:B2").CellProtection = struct("com.sun.star.util.CellProtection", True, True, True, True)
1126*0e16a31dSJim Jagielski    sh.getCellRangeByName("C1:C2").CellProtection = struct("com.sun.star.util.CellProtection", False, False, False, False)
1127*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(1).protect("pw")
1128*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(2).protect("")
1129*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(3).setPrintAreas(())
1130*0e16a31dSJim Jagielski    sh2 = doc.Sheets.getByIndex(1)
1131*0e16a31dSJim Jagielski    sh2.getCellByPosition(0, 0).Value = 1
1132*0e16a31dSJim Jagielski    sh2.setPrintAreas((rng_addr(1, 0, 0, 2, 2),))
1133*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(3).getCellByPosition(0, 0).Value = 1
1134*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(3).IsVisible = False
1135*0e16a31dSJim Jagielski    sh.TabColor = 0x00FF00
1136*0e16a31dSJim Jagielski    doc.Sheets.getByIndex(1).TabColor = 0xFF0000
1137*0e16a31dSJim Jagielski    save(doc, "table-attrs.ods")
1138*0e16a31dSJim Jagielski    doc.close(True)
1139*0e16a31dSJim Jagielski
1140*0e16a31dSJim Jagielski
1141*0e16a31dSJim Jagielskidef filtered_rows():
1142*0e16a31dSJim Jagielski    doc = sheets_doc(1)
1143*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
1144*0e16a31dSJim Jagielski    fill_table(sh, 20)
1145*0e16a31dSJim Jagielski    rg = sh.getCellRangeByName("A1:E20")
1146*0e16a31dSJim Jagielski    doc.DatabaseRanges.addNewByName("flt", rng_addr(0, 0, 0, 4, 19))
1147*0e16a31dSJim Jagielski    fd = rg.createFilterDescriptor(True)
1148*0e16a31dSJim Jagielski    fd.setFilterFields2((filter_field(0, "EQUAL", "North", False, "AND"),))
1149*0e16a31dSJim Jagielski    rg.filter(fd)
1150*0e16a31dSJim Jagielski    sh.Rows.getByIndex(18).IsVisible = False
1151*0e16a31dSJim Jagielski    save(doc, "filtered-rows.ods")
1152*0e16a31dSJim Jagielski    doc.close(True)
1153*0e16a31dSJim Jagielski
1154*0e16a31dSJim Jagielski
1155*0e16a31dSJim Jagielskistep("functions", functions)
1156*0e16a31dSJim Jagielskistep("formula_forms", formula_forms)
1157*0e16a31dSJim Jagielskistep("names", names_and_ranges)
1158*0e16a31dSJim Jagielskistep("condfmt", conditional_formats)
1159*0e16a31dSJim Jagielskistep("table_attrs", table_attrs)
1160*0e16a31dSJim Jagielskistep("filtered", filtered_rows)
1161*0e16a31dSJim Jagielski
1162*0e16a31dSJim Jagielski
1163*0e16a31dSJim Jagielskistep("validations", validations)
1164*0e16a31dSJim Jagielskistep("validation_edges", validation_edges)
1165*0e16a31dSJim Jagielski
1166*0e16a31dSJim Jagielskijson.dump(expect, open(os.path.join(work, "expect.json"), "w"), indent=1)
1167*0e16a31dSJim Jagielskisys.exit(1 if failed else 0)
1168