From d502f3631310111d578d7ab6a0a9c1a29d80f6a8 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Fri, 8 Jan 2021 21:14:15 -0600 Subject: [PATCH] Diable search --- easymacro.py | 137 +- files/ZAZPip_v0.6.0.oxt | Bin 77759 -> 86551 bytes source/pythonpath/easymacro.py | 2140 +++++++++++++++++++++++++++++--- source/pythonpath/main.py | 4 +- 4 files changed, 2016 insertions(+), 265 deletions(-) diff --git a/easymacro.py b/easymacro.py index d91c6af..0745d96 100644 --- a/easymacro.py +++ b/easymacro.py @@ -4,7 +4,7 @@ # ~ This file is part of ZAZ. -# ~ https://git.elmau.net/elmau/zaz +# ~ https://git.cuates.net/elmau/zaz # ~ ZAZ is free software: you can redistribute it and/or modify # ~ it under the terms of the GNU General Public License as published by @@ -290,7 +290,7 @@ def create_instance(name: str, with_context: bool=False, args: Any=None) -> Any: return instance -def get_app_config(node_name, key=''): +def get_app_config(node_name: str, key: str=''): name = 'com.sun.star.configuration.ConfigurationProvider' service = 'com.sun.star.configuration.ConfigurationAccess' cp = create_instance(name, True) @@ -337,7 +337,7 @@ def info(*args): return -def save_log(path, data): +def save_log(path: str, data): with open(path, 'a') as f: f.write(f'{str(now())[:19]} -{LOG_NAME}- ') pprint(data, stream=f) @@ -365,7 +365,7 @@ def inspect(obj: Any) -> None: return -def mri(obj): +def mri(obj: Any) -> None: m = create_instance('mytools.Mri') if m is None: msg = 'Extension MRI not found' @@ -384,7 +384,7 @@ def run_in_thread(fn): return run -def now(only_time=False): +def now(only_time: bool=False): now = datetime.datetime.now() if only_time: now = now.time() @@ -538,7 +538,7 @@ def _struct_to_date(value): return d -def _get_url_script(args): +def _get_url_script(args: dict): library = args['library'] module = '.' name = args['name'] @@ -557,7 +557,7 @@ def _get_url_script(args): return url -def _call_macro(args): +def _call_macro(args: dict): #~ https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification url = _get_url_script(args) @@ -641,7 +641,7 @@ def stop_timer(name): return -def install_locales(path, domain='base', dir_locales=DIR['locales']): +def install_locales(path: str, domain: str='base', dir_locales=DIR['locales']): path_locales = _P.join(_P(path).path, dir_locales) try: lang = gettext.translation(domain, path_locales, languages=[LANG]) @@ -702,7 +702,7 @@ def start(): return -def end(get_seconds=False): +def end(get_seconds: bool=False): global _start e = now() td = e - _start @@ -2775,7 +2775,7 @@ class LOWriterTextRange(object): @property def string(self): s = '' - if self._is_paragraph: + if not self._is_table: s = self.obj.String return s @string.setter @@ -4676,12 +4676,11 @@ class UnoGrid(UnoBaseObject): def __init__(self, obj): super().__init__(obj) self._gdm = self.model.GridDataModel - self._columns = [] self._data = [] - # ~ self._format_columns = () + self._formats = () def __setattr__(self, name, value): - if name in ('_gdm', '_columns', '_data'): + if name in ('_gdm', '_data', '_formats'): self.__dict__[name] = value else: super().__setattr__(name, value) @@ -4700,10 +4699,10 @@ class UnoGrid(UnoBaseObject): @property def columns(self): - return self._columns + return {} @columns.setter def columns(self, values): - self._columns = values + # ~ self._columns = values #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True) for properties in values: @@ -4742,35 +4741,46 @@ class UnoGrid(UnoBaseObject): def row(self): return self.obj.CurrentRow + @property + def row_count(self): + return self._gdm.RowCount + @property def column(self): return self.obj.CurrentColumn + @property + def column(self): + return self.obj.CurrentColumn + + @property + def is_valid(self): + return not (self.row == -1 or self.column == -1) + + @property + def formats(self): + return self._formats + @formats.setter + def formats(self, values): + self._formats = values + def clear(self): self._gdm.removeAllRows() return - # UP - def _format_cols(self): - rows = tuple(tuple( - self._format_columns[i].format(r) for i, r in enumerate(row)) for row in self._data - ) - return rows + def _format_columns(self, data): + row = data + if self.formats: + for i, f in enumerate(formats): + if f: + row[i] = f.format(data[i]) + return row - # ~ @property - # ~ def format_columns(self): - # ~ return self._format_columns - # ~ @format_columns.setter - # ~ def format_columns(self, value): - # ~ self._format_columns = value - - # ~ @property - # ~ def rows(self): - # ~ return self._gdm.RowCount - - # ~ @property - # ~ def columns(self): - # ~ return self._gdm.ColumnCount + def add_row(self, data): + self._data.append(data) + row = self._format_columns(data) + self._gdm.addRow(self.row_count + 1, row) + return def set_cell_tooltip(self, col, row, value): self._gdm.updateCellToolTip(col, row, value) @@ -4780,45 +4790,20 @@ class UnoGrid(UnoBaseObject): value = self._gdm.getCellToolTip(col, row) return value - def _validate_column(self, data): - row = [] - for i, d in enumerate(data): - if i in self._columns: - if 'image' in self._columns[i]: - row.append(self._columns[i]['image']) - else: - row.append(d) - return tuple(row) - - def add_row(self, data): - # ~ self._data.append(data) - data = self._validate_column(data) - self._gdm.addRow(self.rows + 1, data) - return - - def remove_row(self, row): - self._gdm.removeRow(row) - # ~ del self._data[row] - self.update_row_heading() - return - - def update_row_heading(self): - for i in range(self.rows): - self._gdm.updateRowHeading(i, i + 1) - return - def sort(self, column, asc=True): self._gdm.sortByColumn(column, asc) self.update_row_heading() return - def set_column_image(self, column, path): - gp = create_instance('com.sun.star.graphic.GraphicProvider') - data = dict_to_property({'URL': _path_url(path)}) - image = gp.queryGraphic(data) - if not column in self._columns: - self._columns[column] = {} - self._columns[column]['image'] = image + def update_row_heading(self): + for i in range(self.row_count): + self._gdm.updateRowHeading(i, i + 1) + return + + def remove_row(self, row): + self._gdm.removeRow(row) + del self._data[row] + self.update_row_heading() return @@ -5422,9 +5407,9 @@ class LOMenu(object): if MENUS[index.lower()] == cmd: self._menu = menu break - line = self._menu.get('CommandURL', '') - line += self._get_submenus(self._menu['ItemDescriptorContainer']) - return line + # ~ line = self._menu.get('CommandURL', '') + # ~ line += self._get_submenus(self._menu['ItemDescriptorContainer']) + return self._menu def _get_menus(self): instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier' @@ -6212,6 +6197,14 @@ class Paths(object): return True + @classmethod + def image(cls, path): + gp = create_instance('com.sun.star.graphic.GraphicProvider') + image = gp.queryGraphic(( + PropertyValue(Name='URL', Value=cls.to_url(path)), + )) + return image + @classmethod def copy(cls, source, target='', name=''): p, f, n, e = _P(source).info diff --git a/files/ZAZPip_v0.6.0.oxt b/files/ZAZPip_v0.6.0.oxt index e6c732c18755ae0b583db32f86fed519182a80a7..4802a06c5a3f17bcbe4c877b9e9af39f344d3869 100644 GIT binary patch delta 41620 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 Hvw{8(a|;w? delta 32733 zcmV)GK)%11qy@j<1P)M30|XQR000O8b%kq4|K^&V7 z1a*aSQM2GGfoKkLg>q5)E3p`;3jhEpIFoOaBY&>@gOa%*GtT35cWdh^ig?NsH}1!a*zNn#)|ByH?Bi$JQpE64 zF8=v&q%WTOyw8HUoApD+x5Qtxb+~_e;+!5KU3A?~Z>DPK-=7c8N9vq^WGv-=6w2w= z+1~!q-p>c;Fm>M|@I$@H#HYJN?bTI-$$ui|(G(6lTfk&x77uk-TNB*PgW6!2&ErLF zG& z%rTpgC}yEE_T3UbCCueak+eJ7Y#-OEk%aLq3DurS7Pj4Fntygl z?DCltM4lV4RP8(sqANEbj`)C<1yFRN_5z)e%KAB8-L zf`BDyJG%Ovd0a6)J=#GNInO)~fSP1Mupo*j8&gFxK$1`FkIYZloQ0gY0mwdHXc&1LoGV<)!|uu%uh9JnaC*$t>llY5>Dvu)G&5m`hTxphI4YBhn_^M6m;DQNu( z2_r60&Z9>g)^~{rbK0iWQ<*kNE$BN9a((bYHJAi(HF8-d2&}Rmc%{OS0d5Y6B?%xFER@=crGGqGm?v!9>zunu z;X{5nwHN{ZfSZDZ6Z-1D8p$4BkyrOQF7Tn(%V2M6tOh+1*c^2&F%<>`dsc8%(1VHN zc|wo1aaEV_EDo53wgxGZcdTSr;GEaB89OX;7mNHdEMUk&r>4Cx74-)06$_M~V4?)d zuJBWn3`7)^b%FBpTz`qM(Z6wn3|(DNHxXANXk#F;EOWTg1Oe7rEuF!^>e3rr(1Qw2kOCPaprQsow{8OIdTKIacOHv5IS{`fs$dyh z;c6I{RFN<9M`LG-vs#@b+NOj=3OS5T$G_Ap~VZuFZGTc`x0J z-$4~6N}9rvC^d;3td*AVE4Og2*}_R!0GBE@Zs3b44~P0kY_T7WfuRfqM+d)NP=;xa zURuI33ZA%CqNOtBIv#x5ynE-CB%Cu(n5Z6jc_6*XGvD(lcjqg$iiQFdDmP8GMS z1Z9G3Aqz?88~&a~q5Tl~?jGngF>D~s0=_(otF@`3LM9HDP-$DP)*=b!z8Diup;N?+_=1M+oY70TXI18jGJhEF=C{G6qQelB5A_;RvnSN& zB=WNK2^Fy1LOF{t%w^`HZXYEZQ85@kYgf2js7GAM>rq-?w?v7dTtc-YZ9k0zpSNkJ zl9f<%Kq5#lwl8yUB!4A9YEX_@>cO1IPWgh~PO-wu8F$Y)w=J9CO5sD8VIin^d5hf@mG$1J+7UT|!=$qeI7*-4n|a8>ZG$9kl(O0>dXtEVkimL7_~ zmW!5`m`i0-mj6h}a8<*F>HxE5VXI<$9_4kR<$rQzEd)11DnHAF9;!}*I?gV+#{)f( zHQEl3={E>(dUC%(54XDn5Fb)1n|1l8WkI-(?9zPvm{!HgxK4{!4%{K*Y2rc!RlQ9D zyizh-z*R;jLnFl6rqE|gBNKWLe?BOc(q&;}+45eZZo?0+noFOj<{l7U?1UXpAQeks zsec+9YMExRt~G}YLl8ICiKGB60Z5BT17Lj;LpQ4!^Ks4L#+ zF3Re%QckPI%6MpLa%NmIY#D8)=Icg-@%~{~Hk{DW^DGIhy2NzU=j%8=F#;4V>vc=6 z|Btsn!dvYHUm+*yEvl7^z3O`&uY3JtdVd%KQ$VsjKkL`VrJhtc!nr7`w4RSLi^t^N zs;zJ2jfJEZr(DY)*K)+tm6~0#DEek>4czeKsVf9rK7ddevo=xF7fS>rB*QJg8+c3)J%W2j@ql(=+U%(0^$d z!ELy4D>2=JQgQBwF4hki7Hl0{_;)>!wafo{f@DP&_j%}lZQ^aIw0f8f~Z zkV0izFC#_Swq|v@AB7V?nHNPAb^)c2hB?-mE2#*x2WI z;Nsd%dU^&!&L?{x54z$EUk*Ou*7C#Yd8dHJc=M&BMJxa~##aJe{2a7`tA9QzsdvjG zS#aC2QgBtSq$H(_QHzycqB18`(MiTTs3SgdHLByYm$XcZBQw-1=G%tt+$=c z^_IVyEQW(zbCw5}Cu#?$<$vR@dOk1Szf_d0ywn-AW3G_}ixb zzgyM+diy2R|1k@=e+&gHOdyuepnrUyUkv~DW~DE^%Q!dPB}dVCPqSeOO54MEeE+64 zpZbRt(;|1RK;;m-nx8)%<5{F-im$R&DbAwQ$81^R{8Zo!ff2s9+kYFvr@b*Z0GRj3 znjo~Bd4x+ee`)LlKQr!1=#wG)prZ$f4w^PZ|LVwc4iF$ z1hco=qmLol!ezvYr@SNE>b-^lxU>D{I|wC@f%3rq8>V}8x3;_2nR-C9`qqo5d>u77 zd-0V=3wA3Y8L>NVLw}^~)!TLa#wzfj@vJ5W>#2RCh^elIHg!^bWwJ>%c!E$rkw4Aw zD85Ef363SvqXXn-K58-QLfW@>qaD0mVQ}9^UjnuU=lgSCOg>L#(2&(^YHLziq0+M~ zok_8jkQ?5V3Lw^u(90pkubS92Z$oDj=#WnpxexfbcN*^FYkx!iTg&v zDQMJJiq(HY`X5kB0|XQR000O8b%kWp!Q_s~72XHJRQ7v%BglpFUez`Ae|1 z6`aPibQJ6+H%XR%&w`_PSmb4pPJ_erqDYR%<8+t+7y|#F!P!+>2IDkK0{C|p7gdmt zga6$A&xo_Hs%lnluC89DRg`3tcpgoYYL$Mi{vQ9%Kmd-V$3>C^Wj?NM<09D%?(%ss zjHf}7jMB0y(u;Wol~h4I9j)d?Fv&;h_>Rg$;(R(vilDlGN`fjWCM6PqA3wf*7yOt^ zlOoQ7lleuK4xtglWLhRc4CG=;d6kTU3r2)R_K}Ldpd#4M0driX`Lq`#DdZQyO;VKb z9Xykj2#mcTF9=H~u8{a5$Y)rv3l!f4SzOg+Bct7Q^G4Xqi1I3*K})Ux5wz_#&9dMk z3Fc)oo@YIODhklS&j)93j^CXH+i%|oKX0F&ZofTy|8D>dLkYPsdW{XoeqP1pRhC}J&wrHpRQ}HL%gc0nDL-?CVg}p* zJv5Pj--W6Ra3vq>k9n15>MI{!C#u@AtTW~pvmzfRHHqb&`dKBD8BS02eN`m!NTW(` zX;s9-@(B!4mL)^v=`s)i#|6y0WVD+OD+eZuFNPBCXkNt^S#lK5 zW>`0Yj*?+Ife{uUyZp&{$#g!E83)s94?a16V?3V@t31yX1-C^!D>-i#S2z?h=L9}Q z#;i!El}zU^&cT@nyd-kYptEqIa$a>e)AF{bcfH`<6dK04^CE+~q9XZio|FprDS!4b z8|JVHn-m313jz7=^l%Tqd%>HtvlIHMShJi|Gvp~Ikw0X){t-Zb940dsO8B`whexEAv+@{g)R=F?pJd6i_dq>%hJiOTsjDyz7N;@g@Ym=5t} z@;d+cdR|q?u^{k|`acd{zdJiSe%tqdmyYUUr%4IDqsV{1*HEM$`C%1LFEdyN`swfR z-;-Q%nN!V4ULug<)Z>2t;6L};EBJc?3<&=@!i6tQ3i{XsDetRfm`_3Of&`VN-{$@K z@MxF5NpiIeXs= ze%d~Kx7Q2K_I^3*;j(rF%b5oia;mBsSex1?{)^wfb|Uvj$nv80?dk9;FLWBV`vX{E zPq%l@_D+2mw88yln`Gu8r~zI#nP#hitdz3FAMK7_R59j5Q zu)wbh($Ee;-;6R;E^|^YX~`bt!*VqpWvfLpUY!EplrvbI0QX>huny?yAE*KUg-YD~$EUp@{B`Zwv-90m@p-@9w>R_am17 zxPEc|?Ae6Amo9zqcK=uv-k9L0uPpR-Is{oTPA|D1wyf%T1>CBCd$32U=6J$s*g1p^ zdPtT8UGorrCPim3z-}E3a15j-jk_a&2w(Hg7^d#Phrr*0D^-vUqJu1T_+ojY3VL@YaY*=g>u?qidl@Lv~3t{5{h_t@w zMEkIcWA8Xk8eK#Z3}r}v#XmwyhW+l3dv*FL ztdEJA*|%~_%BB%l`5jq9FHq&QpZzY=(0{Xi!uY(y*1!4Kzlvwcj@tc%bup(De9WM0 zqW+KyDWZQo-9CB4yxPD01v%$O5cXH;@R%fX$fTfuF4NawxM`uvv$H}L7!CSX#5&e7 z3}#%_yAz>4y*(a?Uwi-U-S#0SoDJyb$}odHabTA2h^<E}H$6jd(}` zaJIkPr&Nk%HdU*Z?fN6*INjqle+-@n?}SpR2^j2x|i@47k1xXvu;i<0qeh&Pdp; z?th*BI{l<7kUu(I%6Jld3#;TbjQ#;TpAHto4LMApllNzDj!EXSoiSWV#{svRd(j4n zl8?x?*!olIv`_ad102tax_PC)2P@5PAC^qyrk88MH~gQC=PSDhr%IH=`ViyP*i^rN zSpI}r4oeMZ$e$=}cK7zT-yNO}a4$PRF+mhfFM*W5@4c_lg2;qrX){;{o*^l9bi8}8 zf3SDjuVKoobX)<9wqc9SSd(<+OLA}pI^j<{1?_n0M;6Twjb=I2sYiDC+YAS z)pyy6WQ9?=XZVHH%52=EtK2v6p=aI-7MJtxpAro)l%EQ!k6(H=ZlqN%F0pzh?Op56BiopNMWR(d z{#OT?0Yleg10N=7u&!vN!IzPpr6VmHzs%D4t(n`06aj(kuFVg@DL6~yG+V_Ff$ASK zE&U&}}_+{qy6I_4E5{Mx5 zIRwcS7eO(H@o+&$X#xM_#hnd*FEi5IQ%cSZy+B+HuhJU>`T$h$0@0xHzCaT@O6*%$ zNwDzSd~!i1a>$*>P*IQvXt*gri~S$}Zyco4oB#3uK=%M!@q%W5OR|kvtx)NWREQg9pbD;v3?qaH- zMc+ZM4q!~NskoWMb$q}zUMF{3utsw|nB>tdu?&nXZ3=euFHqTUU@n80$I?P)dI*B; z;gEJ)SbR8xlDmCdjTbHsaA4-QDsM=%3`c+);c7j zJuoVWNsfx(96$g&U_2zkF02u2V=#<81%`2S6_>B?$Q}SCyA4HgBABg2VCD<9(kPRw zW}~5OwA*x&np%M~4AIyETab6#w9B!0A_N^c!UG1O68E9hZ1r$`?d>Y6ae017dJU*^ zhy%(?R8a|{0Cc~9r(ySe?Zb-L$e$gY9WGr9G;}nF?FiL_Jp;A;1j^3=sc;WN0T?Ld zCsr`g#ANHANaB5P^GQ^3AAFK+VRf{Izkjl_B3Le5wd&<#>u4v=hO79WIiB;Z9^;Ab zDjLGVl{=Ss;-nya{~qX?G-e+iLH4LIdv_PI(YP}>-rw(k@8SMTo-m@ZJ?Y%_f=MqJ zbtBNIicva^GbKftJJRVm7o3;nmywpJ2@z3Zlw8a&JKvyW5=BrCfjZAY_IBbfoy_9W zLbC6JLdxaBUn`-$W2vqOKUVt)^i*sy_A)FOhpH4dZHf zRa;>@V?ovrbo|&6MGz7XD7k}r!8hMr-)ddu$Te-gkDW5Lh-jA}+EOr%q?ypjwRT(j z-=w^}$Uk<}!EeOMF}Ad$iBOaESj+>+0Ecy}miMxMEzkvuUCJ4#Kb`#IAGEO3nvRP* zlk$7~yJr~*r-}3y0f0#05Tgc-0g)V6Fv5ToWzfYxLal@0lWo}%{7+UAX>@o-cT%L3 z!&oLhk|uYw--(Wjv?gSN?i+P$M2DjI+51=}Q*skIIz6CuXN;aCp(XbL+Lrt)Ov3_- zi}@6Ph#T-}erG(DlaaGJ-&`{z>5K7x$y3gU3)<LemIBh z50d+M4vMpqUQne~mTb|I+(SnnJiXr1j;AuJRe%9t2N8`!pb~ zr6#PyXzE^9<0D3m)(~~-Ww0q=Mzhv%UL}eF$yWrI6W`b?K1M$TAj-=oy}-v9@AvwH zw|HhH2QM^8ktnywtAR-ogWpbxWNaUQ9W^7?d=wgbOcRqo5WU2ukfYi~*}!$?LIZ;# z4r4(p0oitgGghAXM4&57%8n)?QTD*PQO;**124tSwsYgx`mc4WNnIzIg4B?~*d<5p zWhoza;#-=G1exIt;6G_!Q~z7*CAqRCQ8xcznQzV|5;X9|Ij8 zqc2aL%4a=?$497E>97Yo0yrH?+kg@r&@`d%>K$(|nP}s=1o*%ZLQ74DP&6OR#Ga{h zgPSJU`(s8AEf0ejI!iMSh%uO9r@?+9OFHMCZRJ$%CU001or*DM^m4>MsHH|$RO^GK zFnqvRb;9!xu4hYRR12G+S!uL?dsa$LK!3|SY$Y~Iv{0_oIP^EL0ox32&euN>ts0^C z#tJ6!$F1xb++)9n6sdY;#MY z0BYE>JRUV-(=sJDz;D$ktrQGUS}GCG_0!mQX>)GnL2t@zXECJRdtG*n(sG8IjgBcY_G7?^{g1ZJOL<1<-Ez~MXB_3VVK||hx$DeriP1`bhU3Qc1TBJi`6)>4 zw)NbuIfMX18Nt8Ep)@s~k-cdXWZ-BiDsUv_2Mqth`mLr)8khzCBUoEN0`-Pt&sa=X zuc6G+U22avN+pn81NL?4X!ai56~zTk1WY^+#dpYNu?e}kps^rgvw$$-Ff4uuK=k~a zZKJmwW^h6)(n#wIfkvZI2E5dk2r&sJ%Lr~D&m~k^5*{(*RmOlA|2sONQxKbcWs+UM{43{AKV0 z{sKNg^Bw^-9RvyAt(iDAijLwEHXuxyJW3Kfb?)^se|UE1q6#L6@#6T1Z9YSrh&y4} zrPD=j4zx^vPCl_47SyHFoBTTAgE$hc{9iiTaJRg=CR7g`Dz(CrX6!XoU53b(pvzg- zG$s)yHsjEciW}B7^B7T);4oWbAelHv6rpI5VaRbwx&k}P9%(I^?RyAf}2Cn&Ff=`xcza1w>=QLL*Vx+gWwqP8vZ z30isbk!hC|vrSv=D8& zqGpnRA0K#p$p||M9u-W1=RsAf3WAavcbO&0tW%05RdgM$>Sl(i{<8}nstkpkVAhNS zOsr#}Xa>=fR)YZ`%f>yr3Q4yE33D%?X{4LREmVyyI>HY(FUPkQ=NIWE?D2iZzs~CzCnof;SDhrxSxxK<0xiqyknxoq| z6$Q=imw&pHsquFksA8NCpvpYz!&Z@*UmhrV$LBK3FXAi^u7H#zexGBh4~FceYcYax zIaTZgB-0p0of@@rhD^5T&Sx6gOOgp4F0qZ0%*=D^NcrByKsYDL)6g7U*0#)ZcN%?|7Z%u4^e)5GwCp|-WyQ)FGp zTI{_q)ymoyt6;`Nq^mjr5y{82teD4?h`x$9Uc597cw&hZ*PSB`L|!!McB8B0<0yr} zH)oDoItA_*>l=%3OOWMpD+`hEzF=3W9tzkP0Jc8e1FIIvIQ_U4B8cuqHJ|{023vuB zlSN83F2~ifB~{l7>+uxxE0Vr1??CZo52_Frc~z5ASx`V*Bj9LZg8mh=awvt9V1O1? zF?0f_;oqd-g#jEl0M~erRAB8IN-_gU9x%F6?1aBe7e2DXRAaPJ_KdDOSm)4y53&3x)d!aOWHYu)>gjhXA+ducw= z_Z~vdhad5ZUJ?8s!DW%mf+ycRfuF;96+9gUPvHNaj-Q7XA*eO-_LaAF%sJM$MN*gN zE|!99R8xr+a+UnjgnN$fD2I;fE&Uh&gg=|H z_!vOYtFwGcH)}zWF+02m#kAjZ;5}6 zEt?7}v1rT2qlxk&F%ZZI75?(DOOAmryl9L(vl9d>2-T(|8^#WT#$KV`lQRS<@W$v% z-n5VBMM>?nk<+>!laOM4@_Es1y0B<;q_=)oyVE@^?q*efi7Mb-bQS*&t6MZHQgpPx zNmSqi)Mb)#*;3Je^=KT;F5tf#DssZ>zwhpEyn&4ZQw@3OO07~madw#(X>~Q(;<}># zn{8CdVByS?>1B1b^=zX@mx8zOu5WEtKtBRf;mfV{HT>gFh4w-P92F-5E+gT3@$$Kt z1V|zD6Hu#qrB==iu>*+I;zrFD^KD^gj+0_qM}XL;FK#@4Hl4pa?%1d`Zov$1oY_@= zolMDnQYTomX=#lkSmkTW(3JMb_Vt2;Dd-&O=!_9-gbMk(MB5E%DN5sLY2;~&w9zaX zL_j0LJKsYA5fF&!PlPop8%=*w&CqiuA^*%|ShW~tSc%hyh|C9rl8WmpcAdvDbX4c) zg(w7=Cnqg`QL0Yy7S5JP%JRz;&39D2P99zDlZGGHA_}Pn^he#w^l?buF#JoX%gV9V zZsah_OJy$oL2mC9?waNnQuL-ZA>z?%!;x_4A=N7Gn}z`r+hR^X4|$}-ITij9XV>}o zspSH>uui6O#$mkr|Rcg z)ig1zS+)c@i!LZGNt34!+pa*EWN6y#GL?LJ#71H*Trcv`osg}y?>YWq20QtJq6>Rn zbYlATS30o&_t&TY5q53NSfECXJTLJI?_T$*NlwT@2Ht12E|p_%aCC`lrP8RzNSyizBB z^G`*)jjl4Oi+CJ5GH#xbUBn-%W%vbB3uE?F6PP%f1RT>WudZ-f8HD4Qit~Mw=JAy5 zUBdXUc*200vGGbq{OcIL7kJlZSS8g{z9(hPV=RiM=)o+$!%Y{O^>E8JxJZSC(pnls z$UmW&4tV2%9`=<7v1TL)MZC4ATJHmYZQ};c9#8KrN{#;K`n1VK0ZD0t+VKrJj;y>x z*$cbDw?3eqoi-q2xGp5CYw#^fi>V^OoWlyObx*aSC!1OhIL@VgrAuB`q8X3TgQ6%3 z4Ty#{&z>#fCT|!vEmw50PZa48RlRxwQ|S*&ud08Pur_EGIzCfwS?VWzYeWQpt&H$f zSd-blV~HBf2Zd#L-Dw$U9vU6xk=}dOI3f&85qF>{XPnK;E3L9I-Z9PW3K4QXwdm6C zq><|kZCkAEL_3%MHcxGHmejluSs*qrc-wpl?qeP>tE1?=HpRjj6XwJq5t9VTQEVp< z`siY*wrOQBx?9RW4Hrexrh4ChWyx3!dkl@Mg|c@~p=PhuHb+~QyEoRa*^8oXWT0oL zW1FT1%iE2Du6hOam`~L08#L1CoL(1JFv0}U<^)v}C7qz0!4O#d=k90#-r@Ts(#*iu zSU51&VnaiHxwB?Zbx~9=1q&EnEmZ}1JEHsWG83k}2bDKbYhXj8Ks&U5fR*6~NDTw_ z=!8fB%FF>?srYD1PC-aV(Q4At!ZYQ3hH*F|y)j17KiJ$sv$MfE0qttFP0;A55kHV8 z^@(CiBMJ@vHASMKJYO^#%((v?#(89`{6%q~+REbnw71gOkxnkdCPWyZ8HZ;Pve#)f ziD%IffMe)dbM+IQz-yU*#JJ^z$;fEpl8u7(_!ei4t}ALT7^W)t?(|R{9nXu5n`FA}$^Mkh z!fra3m3^pT9dTiLrO072TvM#3g<;)V6qI=6i9R*Sv}2F*?!oJSt^q4-#R^V1w)9EC zC-wdHU6qs_JXAou-qLh2>goE2h|kwsE2s&H>eKiI)M(HKI1B)(XcuZ+&C@q|ArCpF z79zTnOf5(D4-U`t_-*(Xj&cmL79RD3pYviAy!uX;C6M`1&)ryC`!4w9=ivPinC3+xLNOJ87;Cc7G%GGLpN`PQkQC=XSkf8O$usDqGlJuO`GE@I zzO)k72@{jMwku4f#H25q9;y}yS?ao`!t->rs6nUmDVi(Z9qcwCq3dm}Bn*exFLafl z^~eFI(?Wtk=Q$L2BU+J7I5Ocvd}(7|6SG6WwjG^|oY#c*rj#&y*ZM=E78ew8;LjtOJH7^Gjar^lZGgqI052hou7Ue~A z*WbW41r1C4TRYmze1m{yt)fXm`9n-*2Pi59Wsm<#%RqAy+V z07Qfc)HNO|Vrn&xF@cdqSpvcd(d>9VWPEl3{4;C=u09EfX=Qwqjz-DUM6Wtv2z3_& zUoYy*j@CrjeNmYPLyBjrB_eFJrvkkj#q!^m;D$bbBqw+sfnq)-iXD{*(i)dD3`IJK z=O6>wYF;|lo_OqWZ}tUDr?95mfQloSmhK@2;EKeXB)F?7wLW*Qkf|MZO$w_`s&^X0 zENV3x`+YQ5kMRMEh0;ib!1T!ldI6)&WR}mIHOstY?K=ku?J)x!B}VekD0gwy0YZ?)}_$8Aov zmNA9)qD^I3kaK z_q=gPwNwYCyaic8^D%pFyIkCJOZHx44;oFgyd_*?vq7F4Ay8f&ke%&4KdKjS$~8IL zx&nH5jL&&cF!qjq2C%&GUzx`R5QnA0tjC*U8~2&&l@uFGoug|oGO|!VsSc*&Tz;oX zt-}hb@M$JOJzeSt1!OAZ91egr6SseV1(tie738|9)1Km;0aO3C8lmpU)QuU?xxWd< zw8-l;p5ZH(I<~=UkHe+yWgM3b{;Q+Rl4+yiOSh65CnM0Ucw0e{LWeYNd7T|4o^yiz zV4y24boG@g&pa^iD`{9pNoHelS0wEik7{wRIk2ZU{FixGaaNLOecF%`ZpERmO)s4olL!zNQ(l}>9;{|e>>@_fjmgUO6ZAnWg2 zag?n+sO2y`^D~SxJw;_2Bgjp;0qxdSP(QonxX22r-9@lBFJ;&c~)t!7M(pGucDaB9sLo6*ZKGM;oUAR*NcC+-XM?#5>8jHAp&9uk_xszw} z$@Db8eZ%mKP2SV96aXD(i#^iDxyUS8#TN_dq{f~BAmhLy&S8#3(?Xg;vxuf)D@8*P z%maQMsHuDnVI#7PRV3i|<4Kx<#&jOmYLVtC>M9u}2Pz=cjpq>;gM!|KvnkMQF3exJ z{!@MBpX4`xy73pUhLl@R*LAfQ zl?AlK<)z+E_zeEtjv^>wrP|+Z?U`ow6DAz|$Mje+6bPhUydA>`Sr2Mto+7bYYby6& zoIb8kqi_i$wj2f=SaPsNONl!sUxwN*GqgsVu_4Bo$c8kGP79lgf;zV%_5oh}6nzDI z2n>{r!C9!IghQl%zt29av99t+wc49QPW4ZB-0(~d-O`b!RJv7U2oRkh(N7e)o7!&^ z#ib+e1n&H0((t!&*YK$Hb}cp z@pF~;A(AXFDLhXAf4fcxE!jIHyrw<9 z>8^yMfFELV@EvVzlZZlrho)0KKA~U|f^pwkp_(a6tTwDtI}0YQsiJDGlq;hD8n&q` zbUt2amg13S;}q;71~c=z6_1rB)U2E)<5i;TBxJ;xTqL7WGCHmwX0M^e<~(Gr+rIOR zdT8LiKuu47L#k>hMhd2M>XujqPylV|g`*R3>*i5ge$8#0aE{XWvWO@2AP0;MY#(Z) zXNR%;ujjJtFS^K@-0IRJ`l+>iLP|LUexAP?l_kemd$%QvH|@3rUz*M!D!?>H3$~;E z8jSE0j{Pvs?1Pn>P?bb)OAN30(%RU1#}JdFPgL!Hz33BrmL$T@Z&&MEk!tx8)HQV* z_gy&W+V`^nz-955-LtBA<35(>wAr)zyxg7+ukt#4dJ8AYdvmSiN=4)c3GEyV=vbPl zX7SNXv=k;DQ(Y~b#Dd`wwzx*INP|U60XP;TQ5U{4SXR=)q!vNfgmeMJf9)NE846G-3M%P8uF{;`13o z-aS5`VyYs(5mvVN2~MQab&8~i)jOS5*S9v_x7Lk{h=(JYBlxx4GHsEVzCknRZUKz*T zYQQGEQkxoRly141D2O(0BJo8Xnd3*$?3P=Xw89H$#Ct-qnAkjFIP{PzJ`Ti;_dkAbLVaDVjci%n8wD zUgdk!(ed~l;Mzu1O`AGH5}%9W+ZSb?%`2L2A6hj6wb^TQDbZbC!~woYX+}D08y{6= zqvH`;Oj=zk7NTh0Qwircr!f&}tJ0^Zk@?te(}Y^L?Wh{~dx85!Ar+X#Do!JgG3nc_ zNcI(or}3IlFnlrWd4aog1Au0KN?Z7!BzBC3>~t&f)fEVpaS1=l7N-X+U7uG|U_4vpK;_+O)Sc^?4ZV0n-3753%M@7quAzg~Z6NAFsHJox-w zL)m-=U&$M*bOQf1j@^y6HbyX6;)Gis5_(x)KWSF2w~w2|`3(zT>45t9Dq1i?*IRqk~+&;MAF5 z{O#f_L7P$%Y&rdMyJ?B2M2mD{W|` zHCPa)$;Zk?>g-?iX?QNGlqVoV&4+vOZMY&%%OvPm`RqWxj^NP!?N+d^%LmmS>Dfu1 z7+$|fv(b77eY>@8cLufUPicLTjRw}6x}G@bjY9mgN$jdc6m}1PSm?Z702?+NyK!GZ z=m_c;$PFp@Td*uV@E0&mC~yb{%WZktH{>k>`49Q(<-^4S}W3Vgnnk<|e79M^jAttN8| z6&-HKC(45&xkSGdy6!L#m}+pWqllwEh7>*3g9vSK#@=)U$z~M|iyvF?i}lT71C+S+ z&EZD7F{afBpmaXfllaw!%R%LIpWD9A>bs%dcfO(F?&F+)uhN6d1g|`639Ux*%SLcx zt5R}BW3RQX;HT}wcYEeqb)$DknA6y9=$u$(CG3#>F7{<8#di+q?M3;+!ixI|dIYP=^J0Xtz^zzXXh9%c!BZC*y*nz) zjk;a+=JjgO-`hEUyW1b^ZogmD48Xj*eYQ6^-rw(k@121FsUFm-hqh=T1xwf7(4YKR z?NvT6f^ULnFW1(fNt856r?Bcn;!Bmp9oBtVhc&A`9^0<)wWU`$6|SGmlN~I_-Bx$2 zF}954TYoRr;Uk)_cc!i)qrZ=?lLh_b>kCAIK@Vh@*?I^lu_GT%HQtPu1;waX+zaD@ z+@=tJcL|*oGXm`^TJvE!gru}LLnYN^FJcJf5bLZ|udqKNNgAVk4q zhR9dAJi$BEPFCzzJR%4KVy3?*9W=>^^7P1{_&`zLjYMD)@rZCeoti_&=)|i)Aq}08 zdR{^UrAZw;$T%kWca8c9Wa`(y1$F0y==XGghHs4)T@I2`;DSU2);B*mw{PnZDOlF& z+0;C4)q7Uf$p*kFy3edF8^dCaBGB+7`ap@B5`P)1x?!>tQiV4H3TEd<;afOGwwN;IZJ8K%RGn}d4 zBdtSf`VOD9Y@al!@#N!inW!77u&{2e5$Bd$`pIi;l4zSmmZ1T$SW`*21~X-iTV+S5f(up^qjmz zW3@uMN|>woaT)!Q2cv+`WkJ77PsN+tC-Cy&iAKJ*l@l1W{il{eecS!@KD6r_l%uGh#KrIm^aW^rvG;L?qJ^&+ z8dWNIv<6WTVH1)Sb={^2)B1L%Ufvr$=*qj-;Xq=NbEEdBXN`=B+fB-UVUfxOpMlPv}YA$ub!HpOlPi2f#blu2Fmz<8~9}OF;&oSxQ=eA(ID52S2_&KHb~d z?(do7t=biANgPo*zmOGC3s@v|of#@JUA+TvAi);*8*FTBY-eNJwl>(<#>URXww-Kj z+qTV(ZEc*q+JZf=~{GMi8mna|oU!I3wt%7|o;9F!(`nC zc_d0J=S>ij=(Ddddma(GyU4)q(|w_vYI8$MJ`?26VkkUL?kAsgW72noS$k2bmLv${ zBVuoh+B1-Cx8N$)0xFhB*_ob+)j@q@R78O%Q*3=csx3pRbf;rl8!+E?<*rD>TkrKBjY4JvsV36Lwodj(-sj2`aGu}%JO1XQm zOlG5#uZIPf&P_ildqG3&VStrGVwpb2nwNtc2g1GUq;ykUAjuu{@KHvvAQ=zQ2NN#S8OiG$6XJO-c8cY zP9Y;jA>KdYBA*t~%~ye!J7*wbApsxW!&msBe!Vb4psU+2=5=+Msu5!xisZyZ{Vofh zye^P!#QeOK@zo32F)SzkkhSO6k!z)4AEI$GT9~$?@CrFl3;Ry84cH59Y2{u)9DCxU zn`v>kM#`gEZ%#9sxN7vWQs4@vmXDcZV0UUf>ZvpQgmQ|TUj%Y%ccO@CEh64~2eWpN z#mBqZoMkNV^htZ3Urg=pM4a-Ur*?BpemST=6PvWb@w|jdG@5B%GvqaKkJ5=~%l?Cr z3L(Ao))HSGTQ6@})6`pcD`NXDab8wtno(YULmO51mDP27!--F!q^n)k_l;V8Ua3iw zY)>`Zi9H~TpagIXc3*Onw3%&J_LWq$?oj`s@43Vw5_#DUyngP?e$eb%76a{pMN{0>Ywb@1Y1VsTix6w>g@wO6j| z$&7P#e7n%yxCaxjM|&jo z7~#g%{GI3TvHaI*pXa=h5)qTpG^Vl|(fyqbq4uY&HaMD^&XGQG(q7HPcaAgK5(3g2 zMuO-#BGEZo`U3ll$&5&-lFeq_AP;XFn@VxuKpn`IBPx?klfl&6FH8F}7CiCg$BPu;O0t-PY;YWw#=F=CKP@E&wCaHuh)gKBUJ-^Wd4ONP1A4RcvZQ5VaVIkZWG6 zeLz|TQm>v*XNcp#g=`1S^$3*hn++OWT~3$9eUM(@2$f3x>e#K^+mXQTo%!5<(+C`E z_A=3_pn_L1-IzyM!UQ<*WJFG?QHT#Imk9#Dd8{@Q110g1_MiJn`>NIYS- za7!>V<*zs4A5~eW@KU(t{1=f{%XFmzZ0sIHaa>y3R)yYQfsJmEd1u%u2N`dBH|R*D zJ72T9O|Lh-)Q&*G&_)HD0mG z?SF+Nf1q~#vuCB}TCvQw_k;dqQgdey8LVw)ZVF-f+um_MPFj#l?m=FlS1%Uv-{Sx{ zL!JX?b902TE0gjitVT^Bi^X3ISeLsy)(w=DD{gX4_WCVa6;@~P1HSWn|A-^&^m?0D zsZExyiB3|v@EFg-J)>`DXkmIn;*vJDO@~4Ku?^ivvzY)C+2>j|c26P$A(~eh@`1Q$ zASA167^0>@;m>y!}hkrhcpi&TizHZNEJ55YnT z|018qmRXW|(xOQaPg!6KU?rTO1}!#H``Lu4z52YCK|#r-+XvBG@|+}{)n!Uw-ZA4f z{72k1@t~Qi`bywrjIEhpWm@{n>8&xbZc=R*cFM%`k)*{~YY%JAS zuJ}_{=20L30zt*SJxQCn(16OU>J8q?okhXnW6wQ)oF=s%s(zez$*DABy#XrVgoy$tr}rzo zBbwlLsj-9g8DtOb%hi>JMK5*oBJ!I>qh(XewQ4xy+QkTHMWfw!SK?6e@fa$18K)oN z#_23%#XQf8L35%9E`tz_UqDF^$(B;RG8YxZ9R5ggOT`XV{$}r7FyTr@;SV#uXMDnB z=~ON~^2HZ|*hRiN-Zr=MogT~?*A%#qd}+ftOS*$cx`vc(UQ)pWU$m|ns??r~r@|Mh z@rqK3-JHM3X_RI|`R9Db$H~pLdG@Hf*n+!3-fZNwrq+EAlu2L?*}!_T(3tT-mSb&K z)|91sl1xosmX&+8p;HX`won;{4HPJOLGt#Zm+Juz;k9N~Wl_}vOb4$PaoPogl3joQ z#tD2YVwzK^82lKij)_5Vnh)-w=?Xj z>@3!r7~7zLwcXd*pq98$ea*^#FYbp8efH^lhD;s2){;d3B`JRC>#9qRt$pKw1*z}U zM+Vq2Xb#Biol4kB7fkjj_Tgaq`yjvhiR&TxkjYR7vkGCHzFopYftTKr!tXtAJEgCJ zrTlBF93B2j%b2<4a8^HTj-=lkOi&IMo}x@>sJrq9wpG8u(*D>jMzj^8%i5v#$)JI6 z(p^}cwbaC_#BSaPZ*3ce(BK}QQFY&y<3VI&us5FM-MGv_Ky(rLHBssyw|>3fd_Y7H zb0#79vsC;eEK+!KSpEV7RWy(>Sd?qh4m*9X){^TJK}**&ooHgzQ&aYJC@8Gva|;nF zWY`V>23lcY7Yq=`J*Fo@CQ^U|FfaHi2^R%}K6o*s`FaWXdjypAXE0%a^0hZ*N42d}b%$PL)^kY* zr623jAT4BQn2JeU*OqVPVBpZ}-B-bd%FOWqMN+MY+ToL6FMOWLf2koC z0-A&_=l1Tv%<A%#*P0p*+8SJX(XlKbK0F+ zT)BnQy_ueIkTOqf+x8#~hNx*4JWJI8tUBQvD?50nklZ-QyBuWlYMwkwt+K>PM8iQ@MD zQdnl2!0F{wdUQAGtdy}o z*zh+SqR)g#ouSAok-oql>U5usmkr9^Iy;YafWbeT9(~3wfA4w5w(xO41)>rj8>gO) z&i6t7>mTuc(|bprBVyytkaJ1dGBFkd-U5sGoO4RSQmuV+x*Xm8Im7dBdB!|dmr*Y5 z$LzpZeAiSYBj=w)TR&Nf#I?NgGv3;E-n6Pw{$##*#U$TsS_E}RYH6FkIj(1yh%wX0 z-;Uj>wN@CX9u#(C;5beJwrAi~xhQ@^tTKuLa9TKK&dA&rCw)iK0)HRA<(&LLhgBS| zlGw+xREr{;`+==vu85f|CAF|9NsB|K!mQ(gAcGV4i(6_ooe(kvN&?DEh_2K=eR3rN zbX>#w6*o)$J$X?<^9@1tKEk}49NDRj0=a?<}dL>5|Oh45LLWg3QAY2`_LOeHEbE8p^0SvXVz4c>sQ zEEE$(?a?_!9MmW!@G?{&-nCe~-?_onQl{wd5+pA_~qjVW{MEstG zy_6jd?@!l@bM*keO-?5h0@}$njXDhOY5@NG0`6uHg3*Y6V&bwYKU`D8@FX6p_ILRt z5>HIPBA{OtIQcE!8iJMed{~YGr5x%yVbAAlf{id^q z@Cm_duF^{RcqKl$!U+X!V-gP^0<9PDH`l3%2p~I4Q~!3Nr%Nc1FwEUnybg6-0@;#d zlG(N>FT=w35~g51yuBRZ!n+W? zFrU8xQT*qR=wLIP!Q|K&D~$w8+ZBXeVapqU;Cv!a&1l%!XaZ#~lOS^64b@=T59_t9e^)LViERp_&q_@3*{X67FU0l1e7nAb_ zR%=8i*v&9QI2WR0WP>^6wTEO>pUQ?_4xZhccVgsROY}F3rIPk#n)>Rkvqz+C)XLqZ z@YHtmm+Qr-HI)#v$Z7JQ34}Q-f?ZNIk0s-AvC$Hwu(BDl33=ya98^v>SazD<-G?B7 zQtp`sFjhVakYV;!b0jhTg*u5;-RbflIsf2i^1XB^5XlPDeP0<*3&ABziO0fkFEDoMwitU*jfqJooR$#kCXQ-<|Fdd5U(Hw*_edavsvnMIxf7F;c~b8*4> z#4VWeDM}t_SqJVeS%ze3P7VEH!eP{aOz|?(@V#yNRWV`0EUtzotf)agoxYmv?rJ_` zj>E5@_}|JsM&EaFAvz8DIi70m$QV&uC1MV$L?KW?LP=7{@n?Um8wa?s{vDiBy9+t5 zt4a=Yn=Ac}l`@)V{|sx45B7JONG7^mERAb@4SJrK=b-}KsEgIahct$N34f3fIJ$oB z9jZ9go`fj6$`DMhbuyCPWP14CYx=M7vE8yfcv=ru{a@rE6;9?e9OG!9spu&I4b1Pz z3wpKcpB{{T{<+EnWWJ^$-GK#+r4?=9rww{QWIs6S=VF?o*fCeJY(|{3v7>1isU**mwQy229C-()D>G@r*oI`af zNR7PZeWh2pUddPtQc*tZ32rQUFYYlrn{F8sanzYc=t)!>kUJc((72cNme*-0Dt^znF?~> z_{SdHpM6oR>WR5(*p^__Jp9$aCyG>T+TGJhu)H}TY3g!!Ad(`Y{w)pZn;|Uy*$|Ms zzZ2T$)_*Pmbwib-XFTfgw74m1_0tna(@Khs?;Iz*=9>*aXg(qj1Sk&2BUbwz4*9>L z2c<^`2>zl))N87I?@S|-exTB6lz{;g{da37hTX+$Ia4(k5{<_|OrEUWN!MQ) zC!R1}s=c6yOVbcHUJ zue9?iV_t%Kh7%PHjw(@tgE1eLkU0Dn&7x=!uuTg$?_WK6Z1F1QM^D#JPQTw=YJiTr zsx3~xQXWLdhu>2vr$D4YUYLT-Ys+BU*BkW~S&mIDs^Z*^F%QOgY zE(S9!Ly=ae(|9kDH5NfOhBh6Q=*w;?B(C<6O*j|085`xM4%GyLml+46I`X?D%7rqY zxv(BEI_O1911;!rj$W(G0q?U8iCqcXIIXZjDW>)QL4oD47)p8>aLF191+_vIKgvq# zTT&(0mjSFv|5<5^_ktBe=}H?UqY9Ljd(9H96KB_-l|K~@emetc)AxMQ0*TGmqK{G&7MX{4h!VdJZWiMqXUKP<|ogE~D=)Ap>f7S_a zQC=^qTHQn!e7415PvZ9|+{W+jaAoT_Q1a4b>F`^<$+OXq$}Fg9E^_j4>Zm3Snwi|g z@v~uf7Jr6g?Xh6%bYN!UJ-$MHwjMR(w38kON;F478S>=gXTEffG%_jY`mirbNN2Tt zTm!R*p)m*(*ZN@Ii4gzlm$EmRQMA$r=+}O1@ip*lF>GaBr5ww+=>AdP!$(Fo|qJv|) z<{t$ZdoN(hzV%C$ow6Q%cQ~@C!Lk0P&`P1X#nhYS1O2Kb@#BPDY1ccO2A`Z^$_L1V z_F7)C(d^LA{bbJi=O3eG)wykZCZ=#-dL`8vL$K;Mz=HVnT=_Oy`L1TA7H~Wpai+TI zu772@(%$xC7H-RO(xya)!A?7P55rR$`ky5)q8G;`(q07F8J5fKbO-qY*_5Vk;Am{< z5T*e}SJ|j?gk~Sr6DZVj-yGJ!j5weY!CziH(k|%phQ2td^VXhqx&t#kyD+;G(}>}-Ks%tBN>{vn zQXnIVT6W&d!NF$s1y7v4Qp1@6Uor6vaNtlXxfp(&?|7n3gC4|`qwGLgWLHFS{u*tI z&?9ZLe@7LWC!&UL(yGbO(VMF?q|5h+PhLEG+rz|H3_v}ZMGi)KG>eVFsnr7uA-eko zZfxA;82C{W$+R7p4Sj*5{tM9ACnTIsGNjxn9!G@PyfF7+ue-1(zHvF>w2W~%4ud<< z!e9dX(_z+ELN%2C?w>tyXs==}3Pl>!xkP;j<}E4Kn_SF{iIux(vq(tAoM^)kx_&Vg zuR=F|>7}{U%lH-yGEuaX$NFPy4=!)A$xk_^hG~4fk}ct;ar`-UTphUMtFS{6%ta?^ zk9ytor*uD|ITO#5HauDo#?|#)Ls4Ghp6PS7Pispkm#W=GCJ-FNf6kY@=YK*NrvDaABqVZ7EtU z`o`#%`RP%A(i)cHWeaFF7_w>bVWdzytv~0wkeMl1L5Fc zr6H+UC;-tFHUh<(#;QFHH(*08R$TAx-+ilb2s^sJ!ugyN3Qo0@M}$#uFgODpJJHBd z{UGaGsJ@(NC=XB)5ghZU1FMYW#+B>~yhLwtD*S_w6lOz_2mfX7Oxk1iFogHKm?wS> zHwAF)X^y^{MLfwY$B@|S#ObO$U2H)SiiD;TEJ!2IOgl0B>`X{G7$nNMwGgSl;uo10 zdzkT9nJ71mC}MYtygzgG%raS#irXs4FDS8^BL$n^oCDA}NNQXwab`Q6Kj(v*x`eK= zxvip2Nw{-jOP8e2qZXjZ5v*LPNZse;Gz&HgWS^RhhTKExmH%K{qu3zY8}L`Tj#%9D zg0q^SW0KNT=~Db)i;$Ae)#lAGDSdJ>d|NCu_krv&MJA=M# zbeCYh`o%v8yTpH&Gu(I~1AZw?tR;@75)+)R4QK35<=%+%ep>UBL;_Wsby(%&VoxEr zCh{!e)yT?kv0aGex%-EwlvIx0W0@LNWq-8}{zmZ2*ZqdUU6aoEbWSD%Ixqk)9ECvO^+&pi7#Gi`YK7R&8(U{Nl^RjpN|I;7Di*s&+Y z#b3H$>-X&F=Ec{?a~b0r{hH80_i5J6(!;Gov(5sYa*O>lRv#Eo&;G6YQI@E z6$%iSG+ZmuH3{kjX%q1{DGr$xtJNO-WYnk)tj#Z^XCbztIM~yIz)Z+K)^( zNGzib;kabLf70fhO8O1r?| z=b;$M8}!fXKB`Iav(E1}$3M=*+fIMQ9E#9%a8G`R`G=P<+E|wNI#OnU_L2Q^vsS5x zr_Cm3dB-VlP$X!vMD$k;HO%zsQ#?k^4=C6m;(*q!Y5?Wk5*|+iN`vNnmvw;Ob~+rZ zgGmT<)7tnqu^l=oqyvbJ;n@wBN|*_+bZ@UenPRw*2l5HyggV7q3&oZ4XOC6S)W&rO zxB9xBe_4HfaISbCP6ajjyc3Eq>6`{mJ7_RD1n(U zRrBD=e2USB&W}_pRV^&m0w>yA$6}vw$9YdZz_f4T!=u0+nwf@E#2Y#}=%|ssa8SM8 z@x>qGmHCU0B^I+wUC}BALGxPKRIw-aFS0lW0aQjMEt!#41&G@sK5IHcUbC56f|uPAaqg$%Am1 z=%=QmL4sX#(?BnXUlp6WA4p|*SylEVHy$QBQdH2EWd_tav_}a5YcS1`Qw1=>sIe`8 zRw*igdVlJF>R&YgKBSMdgxY1sAeFg3yg2y*jyj5}-yFdrxZ!gdHQ+Vaz3Xyt~oPmJLs4=(zu_368N9Ku&9TkAYCIQuQCH`eSlS!E(C(0STZ6JF z4=MR+{#1j}7get%7 z%O#M!s9be9dJp9N{-P=$Pa)Za$hkoQdn7^eEde!E%)vxUzJ>Gl^fs6prt_xN(?%5x zJ<5;?%t@U@0T8U~ny#Y=lf%H~uvVp-<))oe?gJ1=0|$C)tDwiL3N(qN9jDnpo!!qT z5PcEv`U|!`r$0Y{|85|%6i&P`F_K3|Zr4yN*?-uaim%H-e(uA8gGD{s`#~5{`h-pm z4ew(hEaz762BR7?;ic&n6B8z7FDIeA%02PjhmnWtTnd_p0765k9leAEonnD|GZR27 zfEUm*ym5w_SZ(Xg*3DqEZ z^Q3+!mW$PHCOC37@ylC5^g}7vY($eKVfyL)7fqv|!tb`oV78Kah5@L=R+^gc!YAjW zMQhuF1*~GIM*J++oG(`Y@nX=Ci(uJdWUg=_v=f*@j;^xB#e7GxJqNZvcJpjBK z=4QS|g|Xoi5Cdt4ZYMQ*Y5GSxbCV&3d=w!=67{Jj=7U=gzDWq z*|JJ^HlMZ+<&8DNu^B+RNj&(~Lg@(WTjqoUTU1oH-tT zVS*@?Y*`A4UH*Bz-67HCH3F&^8e}M%P;m~~yaB(oxTsu@oX>7}F4Jtl?jIJMM=xe+ zs;CD?FdVB9`PIsPWJcj`5!DL@k zi^x|*MN5^c7Baws?%@VnxqjS#*bbVna@HBz*K{MRo)YRA`K~)?V0cdxCWEGFF*#W1 zM1_A}Vme$`7$AW_YLaan{kyktcM4-{+lc^_A%TIrn*1k4>~TE1gWn*&PRg?;G*yT8 zuk=2P&+XrKza4;@yT?wI90aF<=J~vLRvG!Z9X3tgjQy6^-%%~CQY&S*?3GpAJ6Mc1 zRaI*@>V=+$s!p-e=!z)*T{Ly|nPMb`cYJcZI^!&gc$tmL+ocACjyY1Dq9P0RW2>Ri zrP^4*IZTTT&iuP+3bP5L1vg_-p1&RZ^!W*j38$Ai1XzJTN~X(@yk0TBVu{vmPfI>c z9aD8g;VARiDK&($8Fs}bii>bPiDT?m31dS>@I=cM3mkVNoF73TSTj~s5D~i zE1ru~{SvbPr8V?v{Cay3k~sqkmOyI>&h~)Ces|Vy6emK!^}WZ|!bJ!go=Zg@EeVfw^*i__ zxhPDoENR6*!Kkt(94(LaLz+|!ANCqi;e&2r3lFeOpK!X_FaHJqT~!_*?IRO&nH%CI z!S%`Y$6CVm(@2R{&vblpX*6tug1<5_zOi1Jq8ye#h@Kjo_x>M%*|;1!{~_agmX|C67)a|E ze~m=!prRE?<*i!d>4x%FF&J|y=mgA?r5*hfy_5K_*6^}^|EbX;3w`ZbVuT3Ms;Nu@ zYN>FTDv5I;sGx2DT4{P)Fx*1qeKO>O(mc>HsCZJ?E}A7od9Lz}#4w{~9l9$cc}6dt z*SwIXPy5(fdzu@!jhq6%T=&75Eqlym2$*X};G}q?cwtR8dwC?z(T=P7AuYMwp}X>~ zZ?u40!@{7rw_2=>uk_?~0shwA8c;{`dr=dgg%DDv!WZ70U+;7KuNJ&WITAYTf%Rq5 z#}e-XoF&S&BC5Z%9jj?81XvVe%h+1|Vhxg6eu3M!@pqv+Aa7D)|UA8_+;=k)6^SrU73Zm?v=$#yOf)CsNjI4 zbz58eIxeLh3GVMbKB5whzZAD= zqbvQv0Mn89O&s|=#wG16<>CSWSXu(_tY9|u<&l*qx=MoHOEDOXvI7}GsIT={T5l_>`O6oiZgX6`?yQ~fddT|y;9>y(wJV%RUF?-lJnv=b{pJ)Ly zQZ<3vVw|qCOgA)5vzh?L;m2>!zFN_e`bcr^30npZx znq+jyosGHHxu-EK-V74g&gsvdinV3-Os*I$rcQ5;^rHGIR--$=;(`2UbdffVGXZQ^ zGwERWOm>Z3Of-0DeK_MTI{S?+8dyclUFMp7$7DTmm_z6IV7ADVL8$4@b?v1+O_g<87InXX`JQvA<5Q%!5y-hk!^ z!rbG4QO>-dr%~5#t~;cblijpVo*^v?Z85 z1OCrFH`jI&RWtZ+@@PrD*58F3C6b=F7iRf981v44r2tYJx3!196vb39Wz5`fXWI>P z`B1eun|5@7q!1+95vHBL&xT-MN;m{use#INs!2LY^U)Ef;+`WJN;>o9+*JK5o#kHR zn{lAp8!NJmLzt^>=7QBRmB-VT<0(eZmeso4ulrGEHaSH;I;T9_NpHdw28{-pXl;0W zu@Jpaci=bk%I3Pj2LJjx+q0HXLA`hDY`-q0%hkzoQ^OW3l8{g`W_@8w9hPK8gJwXo-L0eQ?iOI((Mn||$T%gu2t*|=1UY>R3(I56B%sLYpb#CAv zkB21sO-*>cY^c_12rQcm>Dx4R*~^khKi9>1m3{8h_7ZYNPev?agiVBR|FLeD&@ED_ zkUY9b}jpGmVg0(ws~=PN$O8u3@`Mb$&`GmbSj@-gwzFDuSY>4iWH7 zO>x!Vp!gi%V*4Abd*WKuE>Q8CMsIo8HO2F$(F++E!`Vx4cOP{3 z?;{`5JY*b{C%pH6XwPcq9Ma| z**L+oLGa)0>Zsy~!)@HisC{-E?_n>ZkPFGK5j6TO3F%vctgC+ccXcdp`;;DTUMKF+ zM1qSi)hhfp<&KkxURMy{%T1xIHbmN;QX`kDmmB5itQM^fA=&B?cYs-*&{7efC;v^= zhd*!(wc+CJ;P-n_$1++9Rv2emGWDrsyW8+bsE%yR?kYTze0u(7?x2WuMAsKcdFeG3 zeRJABn@e0MC9(vppDmQu-X zD~j>=;0Jaf%4l0EydEw0Rv)Ju2FbJ4s6!jqX^5bRb@_phw^i4dTeMI17~-RY2+Zm8 zeJSl4ihu9*SKYuDcP{&#XGB8j;LaI*2cp73d->(1JSyb{%q=dto2ihY`_*pl-#WYh7V;yej4su{Qj& z_J}*pk&SIU5pjOWK3&>$#Gu=`;nq~uO@SQ~Fa7h`VeeoZ6L$;OPHsp{mF!O0Wvw0) zSKI|L0OwAU_x18seY$&Ii)SK&b5EQf=(^bqP6OER=PTlzyWJW;PG6nj3S{f8W@Q+1 zUHFxXRO)fUy=aAaIN6%8Z_sKXDgY<_LH;-SkyqY>lO=ja*hQE$eoA z*#aurW<)T?7Yw=`FPDonxDb)EKWiRcr@O(s9BxC@@2@GGZtfZz9}s8=3ld5`2G<;E zs>%3^e?3@VydIsgUowZ0+cTc%f1tM$(B&I=nrpOyH-F;prgTJ`9x3@nS%G)l@&KN+0vH>KP zC=`)kp(G}#9D#MWMq5)y39!O53fyqrds9X592>94TTh;M`cXknB`AJ9&nDB*U78uy zEAi1vP1;Bd-2>H0r0K5DsTGDo6%Ep2UZbTPKH%m(BI-!(p9wwZ!75AsKEg}Yotw8A zjz(gr+s)?VjE#pH#uk~1fstG72VmWw>Bo%8S=xcxlEcTMK^I|HT$kSk^?ZNIla*BW z&6GnjlU@5e5ky;Mj(*{)0jTA=E$WUmODC00H` z{bzMO%~=>JRNvKGc-%if?^M|reo42`6%|;XP!!EdH5AL#;Izs6KbHhx!ob_(yS7Wa z&OgBh88T7rrge31)uwUP8JId>*CdA@k-#w~bSC8L#t+#6|lJzf~OL(MWPOl z&3Hw~58Pwj)#^;SY8dqX_5r0jl|}UOsi;h0E#SYwgR1@*d~cK5P4m(gBHUD$Jp&gh zGC<}`V@(`Fw21c++J`{yP z_aMgdim*?mK_nYT4*}i)i2yvS>9M{dE?Ab(@mLd!h+;6(&FG%1!oxP}@m-hs%d5); z-fcn}T^CR4{&;xH45?Yp)F*=10>hMfDia@)dYWg=4FR&y0i3HWQc@BP#70!B;^?q1GtvAIx?vB&$O9@ z{n8_HL}zqrVl{59W7+hU1cW@OmZ{lNGJXn0rK zf8Hw~vF0~|Wq|+qDusi2nW1@NRkDY*QBMtPD5#@pqlXL#~ul{3~P^(ZrS0kSWX768iA1p9t|00)=a1)L*D0w zcbfNG=oL%^L(;yAGKI3SFwsS6%SpoT8jd9*dUv2^12}WOTYdL(AtgY8E_a7D?Vu(c zw#(Fr>2<2-&gXksWF@r>u1qw2CPbQyBkd=UQjE6nYhr}i~&i3D?8MtaKkn)zV>Ou;uw$hcu4;Yc@Mt65O+-(14n#o;{PA0o>apRRe z)`R&HpY|w|rW_jl3{7!Tu#XG$Zp>=nHgk#D=S-LX^?GKjx1358VN#)QUuU%|6$D?{ zrv_^Ea@>`*WwhzE{@_iWx2osdF7tffcl}aDU=+)Ce%+!ms+q{+LKAwBvu?oRqv35! z4@j@>QD*QiRcv|U<)cwfGpw=aFdHFo(#M+5IjP=a7p>CfFJt{?sg~EvI@|AJ=C;D3 zYx4=Xk6fug3!Xu4MEfiw#sTMmQ-NP)>YW`+iq$sUUt5=w<6nydg$I{J0h4qyNvWo%ZS<;N420l`b+q=_NywrJ4S=^RoV|K$EIH74C^T zR|LQ#1*gqyTy#j1ar@d%Nh^&SY@Uax)DK)?3cy8(o2O-EO@-L>ushFQjn#<-U>$gF zq6vh&1uEo2K|6u9p0xmF`A}EFL2YDs81otNzu>sGro_#G@t2hfccw)ACR+%B2y-_# zxiFVoh=T#DIn5`2M3*m3viwg|zBy77^wt*sn4gJ%7HQy{YS6<|07@tg83z9|NkxpehfJYbUD~5 zs`8*ezM1nq16 h|KC&qWlH{+)&E+bQ2J_pk}~C336vTV>p#tc{69I Any: return instance -def get_app_config(node_name, key=''): +def get_app_config(node_name: str, key: str=''): name = 'com.sun.star.configuration.ConfigurationProvider' service = 'com.sun.star.configuration.ConfigurationAccess' cp = create_instance(name, True) @@ -298,7 +337,7 @@ def info(*args): return -def save_log(path, data): +def save_log(path: str, data): with open(path, 'a') as f: f.write(f'{str(now())[:19]} -{LOG_NAME}- ') pprint(data, stream=f) @@ -326,7 +365,7 @@ def inspect(obj: Any) -> None: return -def mri(obj): +def mri(obj: Any) -> None: m = create_instance('mytools.Mri') if m is None: msg = 'Extension MRI not found' @@ -345,7 +384,7 @@ def run_in_thread(fn): return run -def now(only_time=False): +def now(only_time: bool=False): now = datetime.datetime.now() if only_time: now = now.time() @@ -449,6 +488,8 @@ def _get_dispatch() -> Any: return create_instance('com.sun.star.frame.DispatchHelper') +# ~ https://wiki.documentfoundation.org/Development/DispatchCommands +# ~ Used only if not exists in API def call_dispatch(frame: Any, url: str, args: dict={}) -> None: dispatch = _get_dispatch() opt = dict_to_property(args) @@ -497,7 +538,7 @@ def _struct_to_date(value): return d -def _get_url_script(args): +def _get_url_script(args: dict): library = args['library'] module = '.' name = args['name'] @@ -513,11 +554,10 @@ def _get_url_script(args): url = 'vnd.sun.star.script' url = f'{url}:{library}{module}{name}?language={language}&location={location}' - return url -def _call_macro(args): +def _call_macro(args: dict): #~ https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification url = _get_url_script(args) @@ -601,7 +641,7 @@ def stop_timer(name): return -def install_locales(path, domain='base', dir_locales=DIR['locales']): +def install_locales(path: str, domain: str='base', dir_locales=DIR['locales']): path_locales = _P.join(_P(path).path, dir_locales) try: lang = gettext.translation(domain, path_locales, languages=[LANG]) @@ -662,7 +702,7 @@ def start(): return -def end(get_seconds=False): +def end(get_seconds: bool=False): global _start e = now() td = e - _start @@ -753,6 +793,11 @@ def decrypt(token, password): return data +def switch_design_mode(doc): + call_dispatch(doc.frame, '.uno:SwitchControlDesignMode') + return + + class SmtpServer(object): def __init__(self, config): @@ -892,6 +937,63 @@ def send_email(server, message): return +class ImapServer(object): + + def __init__(self, config): + self._server = None + self._error = '' + self._is_connect = self._login(config) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + @property + def is_connect(self): + return self._is_connect + + @property + def error(self): + return self._error + + def _login(self, config): + try: + # ~ hosts = 'gmail' in config['server'] + if config['ssl']: + self._server = imaplib.IMAP4_SSL(config['server'], config['port']) + else: + self._server = imaplib.IMAP4(config['server'], config['port']) + self._server.login(config['user'], config['password']) + self._server.select() + return True + except imaplib.IMAP4.error as e: + self._error = str(e) + return False + except Exception as e: + self._error = str(e) + return False + return False + + def get_folders(self, exclude=()): + folders = {} + result, subdir = self._server.list() + for s in subdir: + print(s.decode('utf-8')) + return folders + + def close(self): + try: + self._server.close() + self._server.logout() + msg = 'Close connection...' + debug(msg) + except: + pass + return + + # ~ Classes class LOBaseObject(object): @@ -901,7 +1003,7 @@ class LOBaseObject(object): def __setattr__(self, name, value): exists = hasattr(self, name) - if not exists and not name in ('_obj', '_index'): + if not exists and not name in ('_obj', '_index', '_view'): setattr(self._obj, name, value) else: super().__setattr__(name, value) @@ -917,54 +1019,6 @@ class LOBaseObject(object): return self._obj -class LOImage(object): - TYPE = { - 'png': 'image/png', - 'jpg': 'image/jpeg', - } - - def __init__(self, obj): - self._obj = obj - - @property - def obj(self): - return self._obj - - @property - def name(self): - return self.obj.Name or 'img' - - @property - def mimetype(self): - return self.obj.Bitmap.MimeType - - def save(self, path, mimetype=DEFAULT_MIME_TYPE): - p = _P(path) - if _P.is_dir(path): - name = self.name - else: - path = p.path - name = p.name - - path = _P.join(path, f'{name}.{mimetype.lower()}') - args = dict( - URL = _P.to_url(path), - MimeType = self.TYPE[mimetype], - ) - if not _export_image(self.obj, args): - path = '' - - # ~ size = len(self.obj.Bitmap.DIB) - # ~ data = self.obj.GraphicStream.readBytes((), size) - # ~ data = data[-1].value - - # ~ data = self.obj.Bitmap.DIB.value - # ~ data = self.obj.Graphic.DIB.value - - # ~ _P.save_bin(path, data) - return path - - class LODocument(object): FILTERS = { 'doc': 'MS Word 97', @@ -1096,6 +1150,10 @@ class LODocument(object): call_dispatch(self.frame, '.uno:Copy') return + def insert_contents(self, args={}): + call_dispatch(self.frame, '.uno:InsertContents', args) + return + def paste(self): sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') transferable = sc.getContents() @@ -1463,66 +1521,279 @@ class LOSheetCharts(object): class LOFormControl(LOBaseObject): + EVENTS = { + 'action': 'actionPerformed', + 'click': 'mousePressed', + } + TYPES = { + 'actionPerformed': 'XActionListener', + 'mousePressed': 'XMouseListener', + } - def __init__(self, obj): - self._obj = obj - self._control = self.doc.CurrentController.getControl(self.obj) + def __init__(self, obj, view, form): + super().__init__(obj) + self._view = view + self._form = form + self._m = view.Model + self._index = -1 def __setattr__(self, name, value): - if name == '_control': + if name in ('_form', '_view', '_m', '_index'): self.__dict__[name] = value else: super().__setattr__(name, value) + def __str__(self): + return f'{self.name} ({self.type}) {[self.index]}' + + @property + def form(self): + return self._form + @property def doc(self): - return self.obj.Parent.Parent.Parent + return self.obj.Parent.Forms.Parent + + @property + def name(self): + return self._m.Name + @name.setter + def name(self, value): + self._m.Name = value + + @property + def tag(self): + return self._m.Tag + @tag.setter + def tag(self, value): + self._m.Tag = value + + @property + def index(self): + return self._index + @index.setter + def index(self, value): + self._index = value + + @property + def enabled(self): + return self._m.Enabled + @enabled.setter + def enabled(self, value): + self._m.Enabled = value + + @property + def events(self): + return self.form.getScriptEvents(self.index) + def add_event(self, name, macro): + if not 'name' in macro: + macro['name'] = '{}_{}'.format(self.name, name) + + event = ScriptEventDescriptor() + event.AddListenerParam = '' + event.EventMethod = self.EVENTS[name] + event.ListenerType = self.TYPES[event.EventMethod] + event.ScriptCode = _get_url_script(macro) + event.ScriptType = 'Script' + + for ev in self.events: + if ev.EventMethod == event.EventMethod and \ + ev.ListenerType == event.ListenerType: + self.form.revokeScriptEvent(self.index, + event.ListenerType, event.EventMethod, event.AddListenerParam) + break + + self.form.registerScriptEvent(self.index, event) + return + + def set_focus(self): + self._view.setFocus() + return + + +class LOFormControlLabel(LOFormControl): + + def __init__(self, obj, view, form): + super().__init__(obj, view, form) + + @property + def type(self): + return 'label' + + @property + def value(self): + return self._m.Label + @value.setter + def value(self, value): + self._m.Label = value + + +class LOFormControlText(LOFormControl): + + def __init__(self, obj, view, form): + super().__init__(obj, view, form) + + @property + def type(self): + return 'text' + + @property + def value(self): + return self._m.Text + @value.setter + def value(self, value): + self._m.Text = value + + +class LOFormControlButton(LOFormControl): + + def __init__(self, obj, view, form): + super().__init__(obj, view, form) + + @property + def type(self): + return 'button' + + @property + def value(self): + return self._m.Label + @value.setter + def value(self, value): + self._m.Text = Label + + +FORM_CONTROL_CLASS = { + 'label': LOFormControlLabel, + 'text': LOFormControlText, + 'button': LOFormControlButton, +} + + +class LOForm(object): + MODELS = { + 'label': 'com.sun.star.form.component.FixedText', + 'text': 'com.sun.star.form.component.TextField', + 'button': 'com.sun.star.form.component.CommandButton', + } + + def __init__(self, obj, draw_page): + self._obj = obj + self._dp = draw_page + self._controls = {} + self._init_controls() + + def __getitem__(self, index): + control = self.obj[index] + return self._controls[control.Name] + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + + def __contains__(self, item): + return item in self.obj + + def __len__(self): + return len(self.obj) + + def __str__(self): + return f'Form: {self.name}' + + def _init_controls(self): + types = { + 'com.sun.star.form.OFixedTextModel': 'label', + 'com.sun.star.form.OEditModel': 'text', + 'com.sun.star.form.OButtonModel': 'button', + } + for i, control in enumerate(self.obj): + name = control.Name + tipo = types[control.ImplementationName] + view = self.doc.CurrentController.getControl(control) + control = FORM_CONTROL_CLASS[tipo](control, view) + control.index = i + setattr(self, name, control) + self._controls[name] = control + return + + @property + def obj(self): + return self._obj @property def name(self): return self.obj.Name + @name.setter + def name(self, value): + self.obj.Name = value @property - def label(self): - return self.obj.Label - - def set_focus(self): - self._control.setFocus() - return - - -class LOForm(object): - - def __init__(self, obj): - self._obj = obj - - def __getitem__(self, index): - return LOFormControl(self.obj[index]) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - pass - - def __contains__(self, item): - return item in self.obj - - def __len__(self): - return len(self.obj) + def source(self): + return self.obj.DataSourceName + @source.setter + def source(self, value): + self.obj.DataSourceName = value @property - def obj(self): - return self._obj + def type(self): + return self.obj.CommandType + @type.setter + def type(self, value): + self.obj.CommandType = value + + @property + def command(self): + return self.obj.Command + @command.setter + def command(self, value): + self.obj.Command = value + + @property + def doc(self): + return self.obj.Parent.Parent + + def _special_properties(self, tipo, args): + if tipo == 'button': + # ~ if 'ImageURL' in args: + # ~ args['ImageURL'] = self._set_image_url(args['ImageURL']) + args['FocusOnClick'] = args.get('FocusOnClick', False) + return args + return args + + def add(self, args): + name = args['Name'] + tipo = args.pop('Type').lower() + w = args.pop('Width') + h = args.pop('Height') + x = args.pop('X', 0) + y = args.pop('Y', 0) + control = self.doc.createInstance('com.sun.star.drawing.ControlShape') + control.setSize(Size(w, h)) + control.setPosition(Point(x, y)) + model = self.doc.createInstance(self.MODELS[tipo]) + args = self._special_properties(tipo, args) + _set_properties(model, args) + control.Control = model + index = len(self) + self.obj.insertByIndex(index, model) + self._dp.add(control) + view = self.doc.CurrentController.getControl(self.obj.getByName(name)) + control = FORM_CONTROL_CLASS[tipo](control, view, self.obj) + control.index = index + setattr(self, name, control) + self._controls[name] = control + return control class LOSheetForms(object): - def __init__(self, obj): - self._obj = obj + def __init__(self, draw_page): + self._dp = draw_page + self._obj = draw_page.Forms def __getitem__(self, index): - return LOForm(self.obj[index]) + return LOForm(self.obj[index], self._dp) def __enter__(self): return self @@ -1540,24 +1811,144 @@ class LOSheetForms(object): def obj(self): return self._obj + @property + def doc(self): + return self.obj.Parent + @property + def count(self): + return len(self) + + @property + def names(self): + return self.obj.ElementNames + + def insert(self, name): + form = self.doc.createInstance('com.sun.star.form.component.Form') + self.obj.insertByName(name, form) + return LOForm(form, self._dp) + + def remove(self, index): + if isinstance(index, int): + self.obj.removeByIndex(index) + else: + self.obj.removeByName(index) + return + + +# ~ IsFiltered, +# ~ IsManualPageBreak, +# ~ IsStartOfNewPage class LOSheetRows(object): - def __init__(self, sheet): + def __init__(self, sheet, obj): self._sheet = sheet - self._obj = sheet.obj.Rows + self._obj = obj def __getitem__(self, index): - return LOSheetRows(self.obj[index]) + if isinstance(index, int): + rows = LOSheetRows(self._sheet, self.obj[index]) + else: + rango = self._sheet[index.start:index.stop,0:] + rows = LOSheetRows(self._sheet, rango.obj.Rows) + return rows + + def __len__(self): + return self.obj.Count @property def obj(self): return self._obj + @property + def visible(self): + return self._obj.IsVisible + @visible.setter + def visible(self, value): + self._obj.IsVisible = value + + @property + def color(self): + return self.obj.CellBackColor + @color.setter + def color(self, value): + self.obj.CellBackColor = value + + @property + def is_transparent(self): + return self.obj.IsCellBackgroundTransparent + @is_transparent.setter + def is_transparent(self, value): + self.obj.IsCellBackgroundTransparent = value + + @property + def height(self): + return self.obj.Height + @height.setter + def height(self, value): + self.obj.Height = value + + def optimal(self): + self.obj.OptimalHeight = True + return + def insert(self, index, count): self.obj.insertByIndex(index, count) - end = index + count - return self._sheet[index:end,0:] + return + + def remove(self, index, count): + self.obj.removeByIndex(index, count) + return + + +# ~ IsManualPageBreak, +# ~ IsStartOfNewPage +class LOSheetColumns(object): + + def __init__(self, sheet, obj): + self._sheet = sheet + self._obj = obj + + def __getitem__(self, index): + if isinstance(index, (int, str)): + rows = LOSheetColumns(self._sheet, self.obj[index]) + else: + rango = self._sheet[0,index.start:index.stop] + rows = LOSheetColumns(self._sheet, rango.obj.Columns) + return rows + + def __len__(self): + return self.obj.Count + + @property + def obj(self): + return self._obj + + @property + def visible(self): + return self._obj.IsVisible + @visible.setter + def visible(self, value): + self._obj.IsVisible = value + + @property + def width(self): + return self.obj.Width + @width.setter + def width(self, value): + self.obj.Width = value + + def optimal(self): + self.obj.OptimalWidth = True + return + + def insert(self, index, count): + self.obj.insertByIndex(index, count) + return + + def remove(self, index, count): + self.obj.removeByIndex(index, count) + return class LOCalcSheet(object): @@ -1656,14 +2047,44 @@ class LOCalcSheet(object): @property def rows(self): - return LOSheetRows(self) + return LOSheetRows(self, self.obj.Rows) + + @property + def columns(self): + return LOSheetColumns(self, self.obj.Columns) @property def forms(self): - return LOSheetForms(self.obj.DrawPage.Forms) + return LOSheetForms(self.obj.DrawPage) + + @property + def events(self): + names = ('OnFocus', 'OnUnfocus', 'OnSelect', 'OnDoubleClick', + 'OnRightClick', 'OnChange', 'OnCalculate') + evs = self.obj.Events + events = {n: _property_to_dict(evs.getByName(n)) for n in names + if evs.getByName(n)} + return events + @events.setter + def events(self, values): + pv = '[]com.sun.star.beans.PropertyValue' + ev = self.obj.Events + for name, v in values.items(): + url = _get_url_script(v) + args = dict_to_property(dict(EventType='Script', Script=url)) + # ~ e.replaceByName(k, args) + uno.invoke(ev, 'replaceByName', (name, uno.Any(pv, args))) + + @property + def search_descriptor(self): + return self.obj.createSearchDescriptor() + + @property + def replace_descriptor(self): + return self.obj.createReplaceDescriptor() def activate(self): - self.doc.activate(self._obj) + self.doc.activate(self.obj) return def clean(self): @@ -1715,32 +2136,10 @@ class LOCalcSheet(object): rango = self.used_area return rango.render(data, clean) - -class LOCalcRows(object): - - def __init__(self, obj): - self._obj = obj - - def __len__(self): - return self.obj.Count - - def __str__(self): - return 'Rows' - - @property - def obj(self): - return self._obj - - @property - def count(self): - return len(self) - - @property - def visible(self): - return self.obj.IsVisible - @visible.setter - def visible(self, value): - self.obj.IsVisible = value + def find(self, search_string, rango=None): + if rango is None: + rango = self.used_area + return rango.find(search_string) class LOCalcRange(object): @@ -1775,6 +2174,9 @@ class LOCalcRange(object): def __exit__(self, exc_type, exc_value, traceback): pass + def __contains__(self, item): + return item.in_range(self) + def __str__(self): if self.is_none: s = 'Range: None' @@ -1838,7 +2240,7 @@ class LOCalcRange(object): @property def rows(self): - return LOCalcRows(self.obj.Rows) + return LOSheetRows(self.sheet, self.obj.Rows) @property def row(self): @@ -1991,10 +2393,112 @@ class LOCalcRange(object): rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos] return tuple(rangos) + @property + def merged_area(self): + cursor = self.cursor + cursor.collapseToMergedArea() + rango = LOCalcRange(self.sheet[cursor.AbsoluteName].obj) + return rango + + @property + def empty(self): + cursor = self.sheet.get_cursor(self.obj) + cursor = self.cursor + rangos = cursor.queryEmptyCells() + rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos] + return tuple(rangos) + + @property + def merge(self): + return self.obj.IsMerged + @merge.setter + def merge(self, value): + self.obj.merge(value) + + @property + def style(self): + return self.obj.CellStyle + @style.setter + def style(self, value): + self.obj.CellStyle = value + + @property + def auto_format(self): + return '' + @auto_format.setter + def auto_format(self, value): + self.obj.autoFormat(value) + + @property + def validation(self): + return self.obj.Validation + @validation.setter + def validation(self, values): + current = self.validation + if not values: + current.Type = ValidationType.ANY + current.ShowInputMessage = False + else: + is_list = False + for k, v in values.items(): + if k == 'Type' and v == VT.LIST: + is_list = True + if k == 'Formula1' and is_list: + if isinstance(v, (tuple, list)): + v = ';'.join(['"{}"'.format(i) for i in v]) + setattr(current, k, v) + self.obj.Validation = current + def select(self): self.doc.select(self.obj) return + def search(self, options, find_all=True): + rangos = None + + descriptor = self.sheet.search_descriptor + descriptor.setSearchString(options['Search']) + descriptor.SearchCaseSensitive = options.get('CaseSensitive', False) + descriptor.SearchWords = options.get('Words', False) + if hasattr(descriptor, 'SearchRegularExpression'): + descriptor.SearchRegularExpression = options.get('RegularExpression', False) + if hasattr(descriptor, 'SearchType') and 'Type' in options: + descriptor.SearchType = options['Type'] + + if find_all: + found = self.obj.findAll(descriptor) + else: + found = self.obj.findFirst(descriptor) + + if found: + if found.ImplementationName == OBJ_CELL: + rangos = LOCalcRange(found) + else: + rangos = [LOCalcRange(f) for f in found] + + return rangos + + def replace(self, options): + descriptor = self.sheet.replace_descriptor + descriptor.setSearchString(options['Search']) + descriptor.setReplaceString(options['Replace']) + descriptor.SearchCaseSensitive = options.get('CaseSensitive', False) + descriptor.SearchWords = options.get('Words', False) + if hasattr(descriptor, 'SearchRegularExpression'): + descriptor.SearchRegularExpression = options.get('RegularExpression', False) + if hasattr(descriptor, 'SearchType') and 'Type' in options: + descriptor.SearchType = options['Type'] + count = self.obj.replaceAll(descriptor) + return count + + def in_range(self, rango): + if isinstance(rango, LOCalcRange): + address = rango.range_address + else: + address = rango.RangeAddress + result = self.cursor.queryIntersection(address) + return bool(result.Count) + def offset(self, rows=0, cols=1): ra = self.range_address col = ra.EndColumn + cols @@ -2006,6 +2510,10 @@ class LOCalcRange(object): cursor.collapseToSize(cols, rows) return LOCalcRange(self.sheet[cursor.AbsoluteName].obj) + def copy(self, source): + self.sheet.obj.copyRange(self.address, source.range_address) + return + def copy_to(self, cell, formula=False): rango = cell.to_size(self.rows, self.columns) if formula: @@ -2029,7 +2537,7 @@ class LOCalcRange(object): self.to_size(rows, cols).data = data return - def auto_width(self): + def optimal_width(self): self.obj.Columns.OptimalWidth = True return @@ -2083,6 +2591,17 @@ class LOCalcRange(object): self._render_value(k, v) return + def find(self, search_string): + if self._sd is None: + self._sd = self.sheet.obj.createSearchDescriptor() + self._sd.SearchCaseSensitive = False + + self._sd.setSearchString(search_string) + cell = self.obj.findFirst(self._sd) + if cell: + cell = LOCalcRange(cell) + return cell + def find_all(self, search_string): if self._sd is None: self._sd = self.sheet.obj.createSearchDescriptor() @@ -2138,10 +2657,22 @@ class LOCalcRange(object): args['Y'] = args.get('Y', ps['Y']) # ~ img.ResizeWithCell = True img = self.sheet.dp.insert_image(path, args) - img.Anchor = self.obj + img.anchor = self.obj args.clear() return img + def insert_shape(self, tipo, args={}): + ps = self.possize + args['Width'] = args.get('Width', ps['Width']) + args['Height'] = args.get('Height', ps['Height']) + args['X'] = args.get('X', ps['X']) + args['Y'] = args.get('Y', ps['Y']) + + shape = self.sheet.dp.add(tipo, args) + shape.anchor = self.obj + args.clear() + return + def filter_by_color(self, cell): rangos = cell.column[1:,:].visible for r in rangos: @@ -2155,6 +2686,38 @@ class LOCalcRange(object): self.obj.clearContents(what) return + def transpose(self): + # ~ 'Flags': 'A', + # ~ 'FormulaCommand': 0, + # ~ 'SkipEmptyCells': False, + # ~ 'AsLink': False, + # ~ 'MoveMode': 4, + self.select() + self.doc.copy() + self.clear(1023) + self[0,0].select() + self.doc.insert_contents({'Transpose': True}) + _CB.set('') + return + + def transpose_data(self, formula=False): + data = self.data + if formula: + data = self.formula + data = tuple(zip(*data)) + self.clear(1023) + self[0,0].copy_from(data, formula=formula) + return + + def merge_by_row(self): + for r in range(len(self.rows)): + self[r].merge = True + return + + def fill(self, source=1): + self.obj.fillAuto(0, source) + return + class LOWriterPageStyle(LOBaseObject): @@ -2211,7 +2774,10 @@ class LOWriterTextRange(object): @property def string(self): - return self.obj.String + s = '' + if not self._is_table: + s = self.obj.String + return s @string.setter def string(self, value): self.obj.String = value @@ -2236,10 +2802,6 @@ class LOWriterTextRange(object): def dp(self): return self._doc.dp - @property - def is_table(self): - return self._is_table - def offset(self): cursor = self.cursor.getEnd() return LOWriterTextRange(cursor, self._doc) @@ -2250,6 +2812,23 @@ class LOWriterTextRange(object): self.text.insertTextContent(cursor, data, replace) return + def new_line(self, count=1): + cursor = self.cursor + for i in range(count): + self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False) + return self._doc.selection + + def insert_table(self, data): + table = self._doc.create_instance('com.sun.star.text.TextTable') + rows = len(data) + cols = len(data[0]) + table.initialize(rows, cols) + self.insert_content(table) + table.DataArray = data + name = table.Name + table = LOWriterTextTable(self._doc.tables[name], self._doc) + return table + def insert_image(self, path, args={}): w = args.get('Width', 1000) h = args.get('Height', 1000) @@ -2292,6 +2871,47 @@ class LOWriterTextRanges(object): return self._obj +class LOWriterTextTable(object): + + def __init__(self, obj, doc): + self._obj = obj + self._doc = doc + + @property + def obj(self): + return self._obj + + @property + def name(self): + return self._obj.Name + + @property + def data(self): + return self._obj.DataArray + @data.setter + def data(self, values): + self._obj.DataArray = values + + +class LOWriterTextTables(object): + + def __init__(self, doc): + self._doc = doc + self._obj = doc.obj.TextTables + + def __getitem__(self, key): + return LOWriterTextTable(self._obj[key], self._doc) + + def __len__(self): + return self._obj.Count + + def insert(self, data, text_range=None): + if text_range is None: + text_range = self._doc.selection + text_range.insert_table(data) + return + + class LOWriter(LODocument): def __init__(self, obj): @@ -2306,6 +2926,10 @@ class LOWriter(LODocument): def paragraphs(self): return LOWriterTextRanges(self.obj.Text, self) + @property + def tables(self): + return LOWriterTextTables(self) + @property def selection(self): sel = self.obj.CurrentSelection @@ -2349,8 +2973,72 @@ class LOWriter(LODocument): ps = self.obj.StyleFamilies['PageStyles'] return LOWriterPageStyles(ps) + @property + def search_descriptor(self): + return self.obj.createSearchDescriptor() + + @property + def replace_descriptor(self): + return self.obj.createReplaceDescriptor() + + def goto_start(self): + self.view_cursor.gotoStart(False) + return self.selection + + def goto_end(self): + self.view_cursor.gotoEnd(False) + return self.selection + + def search(self, options, find_all=True): + descriptor = self.search_descriptor + descriptor.setSearchString(options.get('Search', '')) + descriptor.SearchCaseSensitive = options.get('CaseSensitive', False) + descriptor.SearchWords = options.get('Words', False) + if 'Attributes' in options: + attr = dict_to_property(options['Attributes']) + descriptor.setSearchAttributes(attr) + if hasattr(descriptor, 'SearchRegularExpression'): + descriptor.SearchRegularExpression = options.get('RegularExpression', False) + if hasattr(descriptor, 'SearchType') and 'Type' in options: + descriptor.SearchType = options['Type'] + + result = False + if find_all: + found = self.obj.findAll(descriptor) + if len(found): + result = [LOWriterTextRange(f, self) for f in found] + else: + found = self.obj.findFirst(descriptor) + if found: + result = LOWriterTextRange(found, self) + + return result + + def replace(self, options): + descriptor = self.replace_descriptor + descriptor.setSearchString(options['Search']) + descriptor.setReplaceString(options['Replace']) + descriptor.SearchCaseSensitive = options.get('CaseSensitive', False) + descriptor.SearchWords = options.get('Words', False) + if 'Attributes' in options: + attr = dict_to_property(options['Attributes']) + descriptor.setSearchAttributes(attr) + if hasattr(descriptor, 'SearchRegularExpression'): + descriptor.SearchRegularExpression = options.get('RegularExpression', False) + if hasattr(descriptor, 'SearchType') and 'Type' in options: + descriptor.SearchType = options['Type'] + found = self.obj.replaceAll(descriptor) + return found + + def select(self, text): + if hasattr(text, 'obj'): + text = text.obj + self._cc.select(text) + return + class LOShape(LOBaseObject): + IMAGE = 'com.sun.star.drawing.GraphicObjectShape' def __init__(self, obj, index): self._index = index @@ -2358,11 +3046,22 @@ class LOShape(LOBaseObject): @property def type(self): - return 'shape' + t = self.shape_type[21:] + if self.is_image: + t = 'image' + return t + + @property + def shape_type(self): + return self.obj.ShapeType + + @property + def is_image(self): + return self.shape_type == self.IMAGE @property def name(self): - return self.obj.Name or f'shape{self.index}' + return self.obj.Name or f'{self.type}{self.index}' @name.setter def name(self, value): self.obj.Name = value @@ -2418,10 +3117,64 @@ class LOShape(LOBaseObject): def visible(self, value): self.obj.Visible = value + @property + def path(self): + return self.url + @property + def url(self): + url = '' + if self.is_image: + url = _P.to_system(self.obj.GraphicURL.OriginURL) + return url + + @property + def mimetype(self): + mt = '' + if self.is_image: + mt = self.obj.GraphicURL.MimeType + return mt + + @property + def linked(self): + l = False + if self.is_image: + l = self.obj.GraphicURL.Linked + return l + + def delete(self): + self.remove() + return def remove(self): self.obj.Parent.remove(self.obj) return + def save(self, path: str, mimetype=DEFAULT_MIME_TYPE): + if _P.is_dir(path): + name = self.name + ext = mimetype.lower() + else: + p = _P(path) + path = p.path + name = p.name + ext = p.ext.lower() + + path = _P.join(path, f'{name}.{ext}') + args = dict( + URL = _P.to_url(path), + MimeType = MIME_TYPE[ext], + ) + if not _export_image(self.obj, args): + path = '' + return path + + # ~ def save2(self, path: str): + # ~ size = len(self.obj.Bitmap.DIB) + # ~ data = self.obj.GraphicStream.readBytes((), size) + # ~ data = data[-1].value + # ~ path = _P.join(path, f'{self.name}.png') + # ~ _P.save_bin(path, b'') + # ~ return + class LODrawPage(LOBaseObject): @@ -2440,6 +3193,18 @@ class LODrawPage(LOBaseObject): break return shape + def __iter__(self): + self._index = 0 + return self + + def __next__(self): + if self._index == self.count: + raise StopIteration + shape = self[self._index] + self._index += 1 + return shape + + @property def name(self): return self.obj.Name @@ -2474,17 +3239,18 @@ class LODrawPage(LOBaseObject): Ellipse Text """ + index = self.count w = args.get('Width', 3000) h = args.get('Height', 3000) x = args.get('X', 1000) y = args.get('Y', 1000) + name = args.get('Name', f'{type_shape.lower()}{index}') service = f'com.sun.star.drawing.{type_shape}Shape' shape = self.create_instance(service) shape.Size = Size(w, h) shape.Position = Point(x, y) - index = self.count - shape.Name = f'{type_shape.lower()}{index}' + shape.Name = name self.obj.add(shape) return LOShape(self.obj[index], index) @@ -2493,18 +3259,24 @@ class LODrawPage(LOBaseObject): shape = shape.obj return self.obj.remove(shape) + def remove_all(self): + while self.count: + self.obj.remove(self.obj[0]) + return + def insert_image(self, path, args={}): + index = self.count w = args.get('Width', 3000) h = args.get('Height', 3000) x = args.get('X', 1000) y = args.get('Y', 1000) + name = args.get('Name', f'image{index}') image = self.create_instance('com.sun.star.drawing.GraphicObjectShape') image.GraphicURL = _P.to_url(path) image.Size = Size(w, h) image.Position = Point(x, y) - index = self.count - image.Name = f'image{index}' + image.Name = name self.obj.add(image) return LOShape(self.obj[index], index) @@ -2533,7 +3305,7 @@ class LODrawImpress(LODocument): def paste(self): call_dispatch(self.frame, '.uno:Paste') - return self.selection + return self.current_page[-1] def add(self, type_shape, args={}): return self.current_page.add(type_shape, args) @@ -2877,30 +3649,31 @@ class LODocs(object): LODocs._desktop = self._desktop def __getitem__(self, index): - doc = None + document = None for i, doc in enumerate(self._desktop.Components): if isinstance(index, int) and i == index: - doc = _get_class_doc(doc) + document = _get_class_doc(doc) break elif isinstance(index, str) and doc.Title == index: - doc = _get_class_doc(doc) + document = _get_class_doc(doc) break - return doc + return document def __contains__(self, item): doc = self[item] return not doc is None def __iter__(self): - self._i = 0 + self._i = -1 return self def __next__(self): + self._i += 1 doc = self[self._i] if doc is None: raise StopIteration - self._i += 1 - return doc + else: + return doc def __len__(self): for i, _ in enumerate(self._desktop.Components): @@ -2952,9 +3725,9 @@ def _add_listeners(events, control, name=''): 'addActionListener': EventsButton, 'addMouseListener': EventsMouse, 'addFocusListener': EventsFocus, - # ~ 'addItemListener': EventsItem, + 'addItemListener': EventsItem, 'addKeyListener': EventsKey, - # ~ 'addTabListener': EventsTab, + 'addTabListener': EventsTab, } if hasattr(control, 'obj'): control = control.obj @@ -2962,6 +3735,7 @@ def _add_listeners(events, control, name=''): is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl' is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl' is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl' + is_pages = control.ImplementationName == 'stardiv.Toolkit.UnoMultiPageControl' for key, value in listeners.items(): if hasattr(control, key): @@ -2977,10 +3751,10 @@ def _add_listeners(events, control, name=''): getattr(control, key)(listeners[key](events, name)) - # ~ if is_grid: - # ~ controllers = EventsGrid(events, name) - # ~ control.addSelectionListener(controllers) - # ~ control.Model.GridDataModel.addGridDataListener(controllers) + if is_grid: + controllers = EventsGrid(events, name) + control.addSelectionListener(controllers) + control.Model.GridDataModel.addGridDataListener(controllers) return @@ -3114,9 +3888,195 @@ class EventsKey(EventsListenerBase, XKeyListener): event_name = '{}_key_released'.format(self._name) if hasattr(self._controller, event_name): getattr(self._controller, event_name)(event) + # ~ else: + # ~ if event.KeyFunc == QUIT and hasattr(self._cls, 'close'): + # ~ self._cls.close() return +class EventsItem(EventsListenerBase, XItemListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def disposing(self, event): + pass + + def itemStateChanged(self, event): + event_name = '{}_item_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + +class EventsItemRoadmap(EventsItem): + + def itemStateChanged(self, event): + dialog = event.Source.Context.Model + dialog.Step = event.ItemId + 1 + return + + +class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def dataChanged(self, event): + event_name = '{}_data_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def rowHeadingChanged(self, event): + pass + + def rowsInserted(self, event): + pass + + def rowsRemoved(self, evemt): + pass + + def selectionChanged(self, event): + event_name = '{}_selection_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + +class EventsMouseGrid(EventsMouse): + selected = False + + def mousePressed(self, event): + super().mousePressed(event) + # ~ obj = event.Source + # ~ col = obj.getColumnAtPoint(event.X, event.Y) + # ~ row = obj.getRowAtPoint(event.X, event.Y) + # ~ print(col, row) + # ~ if col == -1 and row == -1: + # ~ if self.selected: + # ~ obj.deselectAllRows() + # ~ else: + # ~ obj.selectAllRows() + # ~ self.selected = not self.selected + return + + def mouseReleased(self, event): + # ~ obj = event.Source + # ~ col = obj.getColumnAtPoint(event.X, event.Y) + # ~ row = obj.getRowAtPoint(event.X, event.Y) + # ~ if row == -1 and col > -1: + # ~ gdm = obj.Model.GridDataModel + # ~ for i in range(gdm.RowCount): + # ~ gdm.updateRowHeading(i, i + 1) + return + + +class EventsTab(EventsListenerBase, XTabListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def activated(self, id): + event_name = '{}_activated'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(id) + return + + +class EventsMenu(EventsListenerBase, XMenuListener): + + def __init__(self, controller): + super().__init__(controller, '') + + def itemHighlighted(self, event): + pass + + def itemSelected(self, event): + name = event.Source.getCommand(event.MenuId) + if name.startswith('menu'): + event_name = '{}_selected'.format(name) + else: + event_name = 'menu_{}_selected'.format(name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def itemActivated(self, event): + return + + def itemDeactivated(self, event): + return + + +class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener): + + def __init__(self, cls): + self._cls = cls + super().__init__(cls.events, cls.name, cls._window) + + def windowOpened(self, event): + event_name = '{}_opened'.format(self._name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def windowActivated(self, event): + control_name = '{}_activated'.format(event.Source.Model.Name) + if hasattr(self._controller, control_name): + getattr(self._controller, control_name)(event) + return + + def windowDeactivated(self, event): + control_name = '{}_deactivated'.format(event.Source.Model.Name) + if hasattr(self._controller, control_name): + getattr(self._controller, control_name)(event) + return + + def windowMinimized(self, event): + pass + + def windowNormalized(self, event): + pass + + def windowClosing(self, event): + if self._window: + control_name = 'window_closing' + else: + control_name = '{}_closing'.format(event.Source.Model.Name) + + if hasattr(self._controller, control_name): + getattr(self._controller, control_name)(event) + # ~ else: + # ~ if not self._modal and not self._block: + # ~ event.Source.Visible = False + return + + def windowClosed(self, event): + control_name = '{}_closed'.format(event.Source.Model.Name) + if hasattr(self._controller, control_name): + getattr(self._controller, control_name)(event) + return + + # ~ XWindowListener + def windowResized(self, event): + sb = self._cls._subcont + sb.setPosSize(0, 0, event.Width, event.Height, SIZE) + event_name = '{}_resized'.format(self._name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def windowMoved(self, event): + pass + + def windowShown(self, event): + pass + + def windowHidden(self, event): + pass + + # ~ BorderColor = ? # ~ FontStyleName = ? # ~ HelpURL = ? @@ -3125,7 +4085,6 @@ class UnoBaseObject(object): def __init__(self, obj, path=''): self._obj = obj self._model = obj.Model - # ~ self._path = path def __setattr__(self, name, value): exists = hasattr(self, name) @@ -3134,6 +4093,12 @@ class UnoBaseObject(object): else: super().__setattr__(name, value) + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + @property def obj(self): return self._obj @@ -3432,14 +4397,14 @@ class UnoRadio(UnoBaseObject): self.model.Label = value -class UnoCheck(UnoBaseObject): +class UnoCheckBox(UnoBaseObject): def __init__(self, obj): super().__init__(obj) @property def type(self): - return 'check' + return 'checkbox' @property def value(self): @@ -3480,6 +4445,9 @@ class UnoText(UnoBaseObject): def value(self, value): self.model.Text = value + def validate(self): + return + class UnoImage(UnoBaseObject): @@ -3578,17 +4546,483 @@ class UnoListBox(UnoBaseObject): return +class UnoRoadmap(UnoBaseObject): + + def __init__(self, obj): + super().__init__(obj) + self._options = () + + def __setattr__(self, name, value): + if name in ('_options',): + self.__dict__[name] = value + else: + super().__setattr__(name, value) + + @property + def options(self): + return self._options + @options.setter + def options(self, values): + self._options = values + for i, v in enumerate(values): + opt = self.model.createInstance() + opt.ID = i + opt.Label = v + self.model.insertByIndex(i, opt) + return + + @property + def enabled(self): + return True + @enabled.setter + def enabled(self, value): + for m in self.model: + m.Enabled = value + return + + def set_enabled(self, index, value): + self.model.getByIndex(index).Enabled = value + return + + +class UnoTree(UnoBaseObject): + + def __init__(self, obj, ): + super().__init__(obj) + self._tdm = None + self._data = [] + + def __setattr__(self, name, value): + if name in ('_tdm', '_data'): + self.__dict__[name] = value + else: + super().__setattr__(name, value) + + @property + def selection(self): + sel = self.obj.Selection + return sel.DataValue, sel.DisplayValue + + @property + def parent(self): + parent = self.obj.Selection.Parent + if parent is None: + return () + return parent.DataValue, parent.DisplayValue + + def _get_parents(self, node): + value = (node.DisplayValue,) + parent = node.Parent + if parent is None: + return value + return self._get_parents(parent) + value + + @property + def parents(self): + values = self._get_parents(self.obj.Selection) + return values + + @property + def root(self): + if self._tdm is None: + return '' + return self._tdm.Root.DisplayValue + @root.setter + def root(self, value): + self._add_data_model(value) + + def _add_data_model(self, name): + tdm = create_instance('com.sun.star.awt.tree.MutableTreeDataModel') + root = tdm.createNode(name, True) + root.DataValue = 0 + tdm.setRoot(root) + self.model.DataModel = tdm + self._tdm = self.model.DataModel + return + + @property + def path(self): + return self.root + @path.setter + def path(self, value): + self.data = _P.walk_dir(value, True) + + @property + def data(self): + return self._data + @data.setter + def data(self, values): + self._data = list(values) + self._add_data() + + def _add_data(self): + if not self.data: + return + + parents = {} + for node in self.data: + parent = parents.get(node[1], self._tdm.Root) + child = self._tdm.createNode(node[2], False) + child.DataValue = node[0] + parent.appendChild(child) + parents[node[0]] = child + self.obj.expandNode(self._tdm.Root) + return + + +# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1grid.html +class UnoGrid(UnoBaseObject): + + def __init__(self, obj): + super().__init__(obj) + self._gdm = self.model.GridDataModel + self._data = [] + self._formats = () + + def __setattr__(self, name, value): + if name in ('_gdm', '_data', '_formats'): + self.__dict__[name] = value + else: + super().__setattr__(name, value) + + def __getitem__(self, key): + value = self._gdm.getCellData(key[0], key[1]) + return value + + def __setitem__(self, key, value): + self._gdm.updateCellData(key[0], key[1], value) + return + + @property + def type(self): + return 'grid' + + @property + def columns(self): + return {} + @columns.setter + def columns(self, values): + # ~ self._columns = values + #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html + model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True) + for properties in values: + column = create_instance('com.sun.star.awt.grid.GridColumn', True) + for k, v in properties.items(): + setattr(column, k, v) + model.addColumn(column) + self.model.ColumnModel = model + return + + @property + def data(self): + return self._data + @data.setter + def data(self, values): + self._data = values + self.clear() + headings = tuple(range(1, len(values) + 1)) + self._gdm.addRows(headings, values) + # ~ rows = range(grid_dm.RowCount) + # ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows] + # ~ grid.Model.RowBackgroundColors = tuple(colors) + return + + @property + def value(self): + if self.column == -1 or self.row == -1: + return '' + return self[self.column, self.row] + @value.setter + def value(self, value): + if self.column > -1 and self.row > -1: + self[self.column, self.row] = value + + @property + def row(self): + return self.obj.CurrentRow + + @property + def row_count(self): + return self._gdm.RowCount + + @property + def column(self): + return self.obj.CurrentColumn + + @property + def column(self): + return self.obj.CurrentColumn + + @property + def is_valid(self): + return not (self.row == -1 or self.column == -1) + + @property + def formats(self): + return self._formats + @formats.setter + def formats(self, values): + self._formats = values + + def clear(self): + self._gdm.removeAllRows() + return + + def _format_columns(self, data): + row = data + if self.formats: + for i, f in enumerate(formats): + if f: + row[i] = f.format(data[i]) + return row + + def add_row(self, data): + self._data.append(data) + row = self._format_columns(data) + self._gdm.addRow(self.row_count + 1, row) + return + + def set_cell_tooltip(self, col, row, value): + self._gdm.updateCellToolTip(col, row, value) + return + + def get_cell_tooltip(self, col, row): + value = self._gdm.getCellToolTip(col, row) + return value + + def sort(self, column, asc=True): + self._gdm.sortByColumn(column, asc) + self.update_row_heading() + return + + def update_row_heading(self): + for i in range(self.row_count): + self._gdm.updateRowHeading(i, i + 1) + return + + def remove_row(self, row): + self._gdm.removeRow(row) + del self._data[row] + self.update_row_heading() + return + + +class UnoPage(object): + + def __init__(self, obj): + self._obj = obj + self._events = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + + @property + def obj(self): + return self._obj + + @property + def model(self): + return self._obj.Model + + # ~ @property + # ~ def id(self): + # ~ return self.m.TabPageID + + @property + def parent(self): + return self.obj.Context + + def _set_image_url(self, image): + if _P.exists(image): + return _P.to_url(image) + + path = _P.join(self._path, DIR['images'], image) + return _P.to_url(path) + + def _special_properties(self, tipo, args): + if tipo == 'link' and not 'Label' in args: + args['Label'] = args['URL'] + return args + + if tipo == 'button': + if 'ImageURL' in args: + args['ImageURL'] = self._set_image_url(args['ImageURL']) + args['FocusOnClick'] = args.get('FocusOnClick', False) + return args + + if tipo == 'roadmap': + args['Height'] = args.get('Height', self.height) + if 'Title' in args: + args['Text'] = args.pop('Title') + return args + + if tipo == 'tree': + args['SelectionType'] = args.get('SelectionType', SINGLE) + return args + + if tipo == 'grid': + args['ShowRowHeader'] = args.get('ShowRowHeader', True) + return args + + if tipo == 'pages': + args['Width'] = args.get('Width', self.width) + args['Height'] = args.get('Height', self.height) + + return args + + def add_control(self, args): + tipo = args.pop('Type').lower() + root = args.pop('Root', '') + sheets = args.pop('Sheets', ()) + columns = args.pop('Columns', ()) + + args = self._special_properties(tipo, args) + model = self.model.createInstance(UNO_MODELS[tipo]) + _set_properties(model, args) + name = args['Name'] + self.model.insertByName(name, model) + control = self.obj.getControl(name) + _add_listeners(self._events, control, name) + control = UNO_CLASSES[tipo](control) + + if tipo in ('listbox',): + control.path = self.path + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'grid' and columns: + control.columns = columns + elif tipo == 'pages' and sheets: + control.sheets = sheets + control.events = self.events + + setattr(self, name, control) + return control + + +class UnoPages(UnoBaseObject): + + def __init__(self, obj): + super().__init__(obj) + self._sheets = [] + self._events = None + + def __setattr__(self, name, value): + if name in ('_sheets', '_events'): + self.__dict__[name] = value + else: + super().__setattr__(name, value) + + def __getitem__(self, index): + name = index + if isinstance(index, int): + name = f'sheet{index}' + sheet = self.obj.getControl(name) + page = UnoPage(sheet) + page._events = self._events + return page + + @property + def type(self): + return 'pages' + + @property + def current(self): + return self.obj.ActiveTabID + @property + def active(self): + return self.current + + @property + def sheets(self): + return self._sheets + @sheets.setter + def sheets(self, values): + self._sheets = values + for i, title in enumerate(values): + sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.m.insertByName(f'sheet{i + 1}', sheet) + return + + @property + def events(self): + return self._events + @events.setter + def events(self, controllers): + self._events = controllers + + @property + def visible(self): + return self.obj.Visible + @visible.setter + def visible(self, value): + self.obj.Visible = value + + def insert(self, title): + self._sheets.append(title) + id = len(self._sheets) + sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.m.insertByName(f'sheet{id}', sheet) + return self[id] + + def remove(self, id): + self.obj.removeTab(id) + return + + def activate(self, id): + self.obj.activateTab(id) + return + + UNO_CLASSES = { 'label': UnoLabel, 'link': UnoLabelLink, 'button': UnoButton, 'radio': UnoRadio, - 'check': UnoCheck, + 'checkbox': UnoCheckBox, 'text': UnoText, 'image': UnoImage, 'listbox': UnoListBox, + 'roadmap': UnoRoadmap, + 'tree': UnoTree, + 'grid': UnoGrid, + 'pages': UnoPages, } +UNO_MODELS = { + 'label': 'com.sun.star.awt.UnoControlFixedTextModel', + 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel', + 'button': 'com.sun.star.awt.UnoControlButtonModel', + 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel', + 'text': 'com.sun.star.awt.UnoControlEditModel', + 'image': 'com.sun.star.awt.UnoControlImageControlModel', + 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', + 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', + 'tree': 'com.sun.star.awt.tree.TreeControlModel', + 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', + 'pages': 'com.sun.star.awt.UnoMultiPageModel', + 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', + 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel', +} +# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel', +# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel', +# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel', +# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel', +# ~ 'NumericField': 'com.sun.star.awt.UnoControlNumericFieldModel', +# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel', +# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel', +# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel', +# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel', +# ~ 'SpinButton': 'com.sun.star.awt.UnoControlSpinButtonModel', +# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel', +# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel', + class LODialog(object): SEPARATION = 5 @@ -3597,15 +5031,16 @@ class LODialog(object): 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel', 'button': 'com.sun.star.awt.UnoControlButtonModel', 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', - 'check': 'com.sun.star.awt.UnoControlCheckBoxModel', + 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel', 'text': 'com.sun.star.awt.UnoControlEditModel', 'image': 'com.sun.star.awt.UnoControlImageControlModel', 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', - # ~ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', - # ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', - # ~ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', - # ~ 'tree': 'com.sun.star.awt.tree.TreeControlModel', - # ~ 'pages': 'com.sun.star.awt.UnoMultiPageModel', + 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', + 'tree': 'com.sun.star.awt.tree.TreeControlModel', + 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', + 'pages': 'com.sun.star.awt.UnoMultiPageModel', + 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', + 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel', } def __init__(self, args): @@ -3617,6 +5052,7 @@ class LODialog(object): self._color_on_focus = COLOR_ON_FOCUS self._id = '' self._path = '' + self._init_controls() def _create(self, args): service = 'com.sun.star.awt.DialogProvider' @@ -3629,7 +5065,7 @@ class LODialog(object): if 'Location' in args: name = args['Name'] library = args.get('Library', 'Standard') - location = args.get('Location', 'application') + location = args.get('Location', 'application').lower() if location == 'user': location = 'application' url = f'vnd.sun.star.script:{library}.{name}?location={location}' @@ -3651,6 +5087,23 @@ class LODialog(object): dlg.createPeer(toolkit, None) return dlg + def _get_type_control(self, name): + name = name.split('.')[2] + types = { + 'UnoFixedTextControl': 'label', + 'UnoEditControl': 'text', + 'UnoButtonControl': 'button', + } + return types[name] + + def _init_controls(self): + for control in self.obj.getControls(): + tipo = self._get_type_control(control.ImplementationName) + name = control.Model.Name + control = UNO_CLASSES[tipo](control) + setattr(self, name, control) + return + @property def obj(self): return self._obj @@ -3730,27 +5183,33 @@ class LODialog(object): return _P.to_url(path) def _special_properties(self, tipo, args): - columns = args.pop('Columns', ()) - if tipo == 'link' and not 'Label' in args: args['Label'] = args['URL'] - elif tipo == 'grid': - args['ColumnModel'] = self._set_column_model(columns) - elif tipo == 'button': + return args + + if tipo == 'button': if 'ImageURL' in args: args['ImageURL'] = self._set_image_url(args['ImageURL']) - if not 'FocusOnClick' in args: - args['FocusOnClick'] = False - elif tipo == 'roadmap': - if not 'Height' in args: - args['Height'] = self.height + args['FocusOnClick'] = args.get('FocusOnClick', False) + return args + + if tipo == 'roadmap': + args['Height'] = args.get('Height', self.height) if 'Title' in args: args['Text'] = args.pop('Title') - elif tipo == 'tab': - if not 'Width' in args: - args['Width'] = self.width - if not 'Height' in args: - args['Height'] = self.height + return args + + if tipo == 'tree': + args['SelectionType'] = args.get('SelectionType', SINGLE) + return args + + if tipo == 'grid': + args['ShowRowHeader'] = args.get('ShowRowHeader', True) + return args + + if tipo == 'pages': + args['Width'] = args.get('Width', self.width) + args['Height'] = args.get('Height', self.height) return args @@ -3758,6 +5217,7 @@ class LODialog(object): tipo = args.pop('Type').lower() root = args.pop('Root', '') sheets = args.pop('Sheets', ()) + columns = args.pop('Columns', ()) args = self._special_properties(tipo, args) model = self.model.createInstance(self.MODELS[tipo]) @@ -3767,11 +5227,14 @@ class LODialog(object): control = self.obj.getControl(name) _add_listeners(self.events, control, name) control = UNO_CLASSES[tipo](control) + if tipo in ('listbox',): control.path = self.path if tipo == 'tree' and root: control.root = root + elif tipo == 'grid' and columns: + control.columns = columns elif tipo == 'pages' and sheets: control.sheets = sheets control.events = self.events @@ -3944,9 +5407,9 @@ class LOMenu(object): if MENUS[index.lower()] == cmd: self._menu = menu break - line = self._menu.get('CommandURL', '') - line += self._get_submenus(self._menu['ItemDescriptorContainer']) - return line + # ~ line = self._menu.get('CommandURL', '') + # ~ line += self._get_submenus(self._menu['ItemDescriptorContainer']) + return self._menu def _get_menus(self): instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier' @@ -4073,6 +5536,245 @@ class LOMenus(object): return LOMenu(index) +class LOWindow(object): + EMPTY = """ + +""" + MODELS = { + 'label': 'com.sun.star.awt.UnoControlFixedTextModel', + 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel', + 'button': 'com.sun.star.awt.UnoControlButtonModel', + 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel', + 'text': 'com.sun.star.awt.UnoControlEditModel', + 'image': 'com.sun.star.awt.UnoControlImageControlModel', + 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', + 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', + 'tree': 'com.sun.star.awt.tree.TreeControlModel', + 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', + 'pages': 'com.sun.star.awt.UnoMultiPageModel', + 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', + 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel', + } + + def __init__(self, args): + self._events = None + self._menu = None + self._container = None + self._model = None + self._id = '' + self._path = '' + self._obj = self._create(args) + + def _create(self, properties): + ps = ( + properties.get('X', 0), + properties.get('Y', 0), + properties.get('Width', 500), + properties.get('Height', 500), + ) + self._title = properties.get('Title', TITLE) + self._create_frame(ps) + self._create_container(ps) + self._create_subcontainer(ps) + # ~ self._create_splitter(ps) + return + + def _create_frame(self, ps): + service = 'com.sun.star.frame.TaskCreator' + tc = create_instance(service, True) + self._frame = tc.createInstanceWithArguments(( + NamedValue('FrameName', 'EasyMacroWin'), + NamedValue('PosSize', Rectangle(*ps)), + )) + self._window = self._frame.getContainerWindow() + self._toolkit = self._window.getToolkit() + desktop = get_desktop() + self._frame.setCreator(desktop) + desktop.getFrames().append(self._frame) + self._frame.Title = self._title + return + + def _create_container(self, ps): + service = 'com.sun.star.awt.UnoControlContainer' + self._container = create_instance(service, True) + service = 'com.sun.star.awt.UnoControlContainerModel' + model = create_instance(service, True) + model.BackgroundColor = get_color((225, 225, 225)) + self._container.setModel(model) + self._container.createPeer(self._toolkit, self._window) + self._container.setPosSize(*ps, POSSIZE) + self._frame.setComponent(self._container, None) + return + + def _create_subcontainer(self, ps): + service = 'com.sun.star.awt.ContainerWindowProvider' + cwp = create_instance(service, True) + + path_tmp = _P.save_tmp(self.EMPTY) + subcont = cwp.createContainerWindow( + _P.to_url(path_tmp), '', self._container.getPeer(), None) + _P.kill(path_tmp) + + subcont.setPosSize(0, 0, 500, 500, POSSIZE) + subcont.setVisible(True) + self._container.addControl('subcont', subcont) + self._subcont = subcont + self._model = subcont.Model + return + + def _create_popupmenu(self, menus): + menu = create_instance('com.sun.star.awt.PopupMenu', True) + for i, m in enumerate(menus): + label = m['label'] + cmd = m.get('event', '') + if not cmd: + cmd = label.lower().replace(' ', '_') + if label == '-': + menu.insertSeparator(i) + else: + menu.insertItem(i, label, m.get('style', 0), i) + menu.setCommand(i, cmd) + # ~ menu.setItemImage(i, path?, True) + menu.addMenuListener(EventsMenu(self.events)) + return menu + + def _create_menu(self, menus): + #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html + #~ nItemId specifies the ID of the menu item to be inserted. + #~ aText specifies the label of the menu item. + #~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4 + #~ nItemPos specifies the position where the menu item will be inserted. + self._menu = create_instance('com.sun.star.awt.MenuBar', True) + for i, m in enumerate(menus): + self._menu.insertItem(i, m['label'], m.get('style', 0), i) + cmd = m['label'].lower().replace(' ', '_') + self._menu.setCommand(i, cmd) + submenu = self._create_popupmenu(m['submenu']) + self._menu.setPopupMenu(i, submenu) + + self._window.setMenuBar(self._menu) + return + + def _add_listeners(self, control=None): + if self.events is None: + return + controller = EventsWindow(self) + self._window.addTopWindowListener(controller) + self._window.addWindowListener(controller) + # ~ self._container.addKeyListener(EventsKeyWindow(self)) + return + + def _set_image_url(self, image): + if _P.exists(image): + return _P.to_url(image) + + path = _P.join(self._path, DIR['images'], image) + return _P.to_url(path) + + def _special_properties(self, tipo, args): + if tipo == 'link' and not 'Label' in args: + args['Label'] = args['URL'] + return args + + if tipo == 'button': + if 'ImageURL' in args: + args['ImageURL'] = self._set_image_url(args['ImageURL']) + args['FocusOnClick'] = args.get('FocusOnClick', False) + return args + + if tipo == 'roadmap': + args['Height'] = args.get('Height', self.height) + if 'Title' in args: + args['Text'] = args.pop('Title') + return args + + if tipo == 'tree': + args['SelectionType'] = args.get('SelectionType', SINGLE) + return args + + if tipo == 'grid': + args['ShowRowHeader'] = args.get('ShowRowHeader', True) + return args + + if tipo == 'pages': + args['Width'] = args.get('Width', self.width) + args['Height'] = args.get('Height', self.height) + + return args + + def add_control(self, args): + tipo = args.pop('Type').lower() + root = args.pop('Root', '') + sheets = args.pop('Sheets', ()) + columns = args.pop('Columns', ()) + + args = self._special_properties(tipo, args) + model = self.model.createInstance(self.MODELS[tipo]) + _set_properties(model, args) + name = args['Name'] + self.model.insertByName(name, model) + control = self._subcont.getControl(name) + _add_listeners(self.events, control, name) + control = UNO_CLASSES[tipo](control) + + # ~ if tipo in ('listbox',): + # ~ control.path = self.path + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'grid' and columns: + control.columns = columns + elif tipo == 'pages' and sheets: + control.sheets = sheets + control.events = self.events + + setattr(self, name, control) + return control + + @property + def events(self): + return self._events + @events.setter + def events(self, controllers): + self._events = controllers(self) + self._add_listeners() + + @property + def model(self): + return self._model + + @property + def width(self): + return self._container.Size.Width + + @property + def height(self): + return self._container.Size.Height + + @property + def name(self): + return self._title.lower().replace(' ', '_') + + def add_menu(self, menus): + self._create_menu(menus) + return + + def open(self): + self._window.setVisible(True) + return + + def close(self): + self._window.setMenuBar(None) + self._window.dispose() + self._frame.close(True) + return + + +def create_window(args): + return LOWindow(args) + + class classproperty: def __init__(self, method=None): self.fget = method @@ -4125,6 +5827,7 @@ class ClipBoard(object): if df: text = transferable.getTransferData(df) return text +_CB = ClipBoard class Paths(object): @@ -4197,6 +5900,12 @@ class Paths(object): tmp = tempfile.NamedTemporaryFile(suffix=ext) return tmp.name + @classmethod + def save_tmp(cls, data): + path_tmp = cls.tmp() + cls.save(path_tmp, data) + return path_tmp + @classmethod def config(cls, name='Work'): """ @@ -4361,6 +6070,16 @@ class Paths(object): return result + @classmethod + def files(cls, path, pattern='*'): + files = [str(p) for p in Path(path).glob(pattern) if p.is_file()] + return files + + @classmethod + def dirs(cls, path): + dirs = [str(p) for p in Path(path).iterdir() if p.is_dir()] + return dirs + @classmethod def walk(cls, path, filters=''): paths = [] @@ -4374,6 +6093,25 @@ class Paths(object): paths += [cls.join(folder, f) for f in files] return paths + @classmethod + def walk_dir(cls, path, tree=False): + folders = [] + if tree: + i = 0 + p = 0 + parents = {path: 0} + for root, dirs, _ in os.walk(path): + for name in dirs: + i += 1 + rn = cls.join(root, name) + if not rn in parents: + parents[rn] = i + folders.append((i, parents[root], name)) + else: + for root, dirs, _ in os.walk(path): + folders += [cls.join(root, name) for name in dirs] + return folders + @classmethod def from_id(cls, id_ext): pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider') @@ -4445,6 +6183,28 @@ class Paths(object): z.extractall(path, members=members, pwd=pwd) return True + @classmethod + def merge_zip(cls, target, zips): + try: + with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t: + for path in zips: + with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s: + for name in s.namelist(): + t.writestr(name, s.open(name).read()) + except Exception as e: + error(e) + return False + + return True + + @classmethod + def image(cls, path): + gp = create_instance('com.sun.star.graphic.GraphicProvider') + image = gp.queryGraphic(( + PropertyValue(Name='URL', Value=cls.to_url(path)), + )) + return image + @classmethod def copy(cls, source, target='', name=''): p, f, n, e = _P(source).info diff --git a/source/pythonpath/main.py b/source/pythonpath/main.py index 547fed7..5df7702 100644 --- a/source/pythonpath/main.py +++ b/source/pythonpath/main.py @@ -448,8 +448,6 @@ def _create_dialog(): 'Width': 12, 'Height': 12, 'Step': 1, - # ~ 'ImageURL': 'close.svg', - # ~ 'ImagePosition': 1, } dialog.add_control(args) @@ -471,7 +469,6 @@ def _create_dialog(): 'Step': 1, 'ImageURL': 'close.svg', 'ImagePosition': 1, - # ~ 'PushButtonType': 2, } dialog.add_control(args) dialog.center(dialog.cmd_close, y=-5) @@ -496,6 +493,7 @@ def _create_dialog(): 'Step': 1, 'ImageURL': 'search.svg', 'FocusOnClick': False, + 'Enabled': False, 'Y': 2, } dialog.add_control(args)