-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathabOffice.PRG
More file actions
2252 lines (1837 loc) · 68.6 KB
/
abOffice.PRG
File metadata and controls
2252 lines (1837 loc) · 68.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
* {fr} abOffice.prg
* =====================================================
* (c) Abaque SARL, 66 rue Michel Ange - 75016 Paris - France
* contact@FoxInCloud.com - http://foxincloud.com/ - +33 9 53 41 90 90
* {fr} -----------------------------------------------------
* {fr} Ce logiciel est distribué sous GNU General Public License, tel quel, sans aucune garantie
* {fr} Il peut être utilisé et/ou redistribué sans restriction
* {fr} Toute modification doit être reversée à la communauté
* {fr} La présente mention doit être intégralement reproduite dans toute copie même partielle
* {en} -----------------------------------------------------
* {en} This software is distributed under the terms of GNU General Public License, AS IS, without any warranty
* {en} It can be used and/or distributed without restriction
* {en} Any modification or improvement must be given for free to the community
* {en} This permission notice shall be entirely included in all copies or substantial portions of the Software
* =====================================================
#INCLUDE AB.H
AB()
return abUnitTests()
* --------------------------------------------------
procedure XLWBtablesBeautify && {fr} Améliore l'aspect des feuilles d'un classeur Excel contenant une table
lparameters ;
tcXLWB && {fr} Adresse d'un classeur Excel
local llResult, lnResult && {fr} Nombre de feuilles du classeur ont été traitées
lnResult = 0
llResult = vartype(m.tcXLWB) == 'C' and file(m.tcXLWB)
assert m.llResult message "Aucun fichier à l'adresse" + space(1) + cLitteral(m.tcXLWB)
if m.llResult
* {fr} Ouvrir Excel en automation si pas encore fait
local loXL as Excel.application
loXL = vartype(m.poXL) == 'O' and loXL(m.poXL)
if not m.loXL
private poXL as Excel.application
poXL = oXL()
endif
llResult = loXL(m.poXL)
assert m.llResult message "Impossible d'ouvrir Excel en automation"
if m.llResult
* {fr} Si le classeur contient au moins une feuille de calcul
local laXLWSs[1], lnXLWSs
lnXLWSs = acXLWSs(@laXLWSs, m.tcXLWB)
llResult = m.lnXLWSs > 0
assert m.llResult message "Aucune feuille trouvée dans le classeur" + space(1) + cLitteral(m.tcXLWB)
if m.llResult
* {fr} Pour chaque feuille du classeur
local lcXLWS
for each m.lcXLWS in m.laXLWSs
* {fr} Enrichir l'aspect
llResult = XLWStableBeautify(oXLWS(m.tcXLWB + '|' + m.lcXLWS))
lnResult = m.lnResult + iif(m.llResult, 1, 0)
endfor
endif
if not m.loXL
poXL.quit
release m.poXL
endif
endif
endif
return m.lnResult
* --------------------------------------------------
procedure XLWStableBeautify && {fr} Améliore l'aspect d'une feuille Excel contenant une table
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou @ adresse d'un classeur Excel
, tcXLWSName; && [nom du classeur] {fr} Nom de la feuille si on veut la renommer
, tcTypes; && [ignoré] {fr} Types des colonnes
external array tcTypes
local llResult as Boolean; && {fr} La feuille a été traitée
, llOpened;
, loXLWS as Excel.WorkSheet;
, lcXLWSName;
, lnRow;
, lnCol;
, liCol;
, luValue;
, lnMargin;
, llTypes;
, laTypes[1];
* {fr} Si la feuille excel peut être ouverte
loXLWS = oXLWS(@m.tuXLWS, @m.llOpened)
llResult = vartype(m.loXLWS)=='O' and not isnull(m.loXLWS)
if m.llResult
with m.loXLWS as Excel.WorkSheet
* {fr} Nommer la feuille élégament
lcXLWSName = iif(vartype(m.tcXLWSName) == 'C' and not empty(m.tcXLWSName), m.tcXLWSName, .name)
lcXLWSName = chrtran(m.lcXLWSName, ["'/\:|_], space(1))
lcXLWSName = cRepCharDel(m.lcXLWSName)
lcXLWSName = alltrim(m.lcXLWSName)
if not empty(m.lcXLWSName)
.name = iif(len(m.lcXLWSName) < 5, upper(m.lcXLWSName), cTronc(proper(m.lcXLWSName), 31)) && 31 est la limite Excel 97
endif
* {fr} Si lignes et colonnes
lnRow = .UsedRange.rows.count
lnCol = .UsedRange.columns.count
if m.lnRow > 1 and m.lnCol > 0
* {fr} Empty 0-cells
.UsedRange.replace(0, '', 1)
llTypes = .f.;
or type('tcTypes', 1) == 'A' and !laEmpty(@m.tcTypes) and acopy(m.tcTypes, m.laTypes) > 0;
or vartype(m.tcTypes) == 'C' and alines(m.laTypes, upper(m.tcTypes), 5, ',', ';') > 0 and !laEmpty(@m.laTypes)
* {fr} Pour chaque colonne
.AutoFilterMode = .f.
for m.liCol = 1 to m.lnCol
* {fr} Régler la cellule de la première ligne
with .Cells(1, m.liCol) as Excel.range
if m.liCol = 1 and m.lnRow > 60
.AutoFilter
endif
luValue = .value
if vartype(m.luValue) == 'C' and not isnull(m.luValue)
.font.name = 'Calibri'
.font.Bold = .t.
.value = upper(cRepCharDel(chrtran(m.luValue, '_', ' ')))
.Interior.color = rgb(0,255,64) && {fr} Green
endif
endwith
with .columns(m.liCol) as Excel.range
* {fr} Si colonne date ou datetime, ajuster le format
if m.llTypes and upper(m.laTypes[m.liCol]) $ 'DT'
.NumberFormat = cDTformat_AW_XL(upper(m.laTypes[m.liCol]) == 'T') && modify command abDate
endif
* {fr} Ajuster la largeur de colonne en automatique
if not .WrapText
.autofit
.columnwidth = min(.columnwidth, 30) && {fr} maximum 30
endif
endwith
endfor
endif
* {fr} Set up page for printing
lnMargin = .parent.parent.CentimetersToPoints(1) && 1 cm
with .PageSetup
.RightMargin = m.lnMargin
.HeaderMargin = m.lnMargin
.FooterMargin = m.lnMargin
lnMargin = m.lnMargin * 1.7
.LeftMargin = m.lnMargin
.TopMargin = m.lnMargin
.BottomMargin = m.lnMargin
.PrintTitleRows = '$1:$1' && {fr} Repeat 1st row on each page
&& {fr} cf. "Formatting codes for headers and footers" in XL8.h
.LeftHeader = '&10&F' && {fr} name of the document
.CenterHeader = '&12&A' && {fr} nom de la feuille en xx points
.RightHeader = 'Printed on &J' && {fr} date
.LeftFooter = 'Generated by Abaque on ' + transform(date())
.CenterFooter = 'Page &P/&N' && &N = nombre total de pages et non le nom du classeur comme documenté
.RightFooter = '&W' && {fr} nom du classeur ??
.PrintGridLines = .t.
endwith
endwith
* {fr} Traiter le classeur
with m.loXLWS.parent as Excel.Workbook && {fr} parent = WB
* {fr} Changer la police normale
with .Styles('Normal').font
.name = 'Calibri'
.size = 10
endwith
* {fr} Ajuster la présentation à l'écran
with .windows(1) as Excel.window
* {fr} Freeze panes on first row
.SplitRow = 1
.FreezePanes = .t.
* {fr} Dimensionner la fenêtre à un minimum
local lnWindowState
lnWindowState = .windowstate
.windowstate = xlNormal
.width = max(.width , .7 * 72 * 20) && {fr} pouces logiques > points > twips
.height = max(.height, .4 * 72 * 20)
.windowstate = m.lnWindowState
endwith
* {fr} Sauvegarder en version courante
.saveas(iif(vartype(m.tuXLWS) == 'C', m.tuXLWS, .fullname), xlWorkbookNormal)
* {fr} Refermer s'il était fermé à l'entrée
if m.llOpened
.close
endif
endwith
endif
return m.llResult
* -----------------------------------------------------------------
procedure XLWStableBeautify_Test && {fr} teste XLWStableBeautify(), nouvelle version
local loTest as sesXLWSTableBeautify_Test
loTest = createobject('sesXLWSTableBeautify_Test')
local lcTableTest, lcXLWBTest
lcTableTest = "XLWStableBeautify_Test"
lcXLWBTest = loTest.DataOpen(m.lcTableTest)
loTest.DataShow()
loTest.CoverageSet(.t.)
loTest.Test(.t., m.lcXLWBTest)
return loTest.Result()
* ------------------------------
define class sesXLWSTableBeautify_Test as abUnitTest of abDev.prg
procedure DataOpen
lparameters tcTable
local lcResult
* {fr} Créer une table de test dans le dossier temporaire
local lcAddr
lcAddr = addbs(sys(2023)) + forceext(m.tcTable, EXT_DBF)
create table (m.lcAddr) free ;
(PK I, cX1 C(20) null, nX2 n(5))
insert into (m.tcTable) values (1, 'Premier', 100)
insert into (m.tcTable) values (2, 'Second', 200)
insert into (m.tcTable) values (3, 'Troisième', 300)
insert into (m.tcTable) values (4, null, 400)
* {fr} Copier dans un classeur Excel
lcResult = forceext(m.lcAddr, EXT_XLS)
copy to (m.lcResult) type xl5
return m.lcResult
procedure DataShow
if set("Asserts") == 'ON'
local lcWindow
lcWindow = wontop()
browse title alias()
activate window (m.lcWindow)
endif
enddefine && {fr} CLASS sesXLWSTableBeautify_Test
* ---------------------
procedure XLWSMultiLineCellExpand && {fr} Augmente la hauteur des cellules contenant plusieurs lignes
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne
, tnColor && [sans changement] {fr} Couleur à appliquer aux cellules contenant plusieurs lignes
local llResult
* {fr} Si on peut accéder à la feuille Excel
local loXLWS as Excel.WorkSheet, llOpened
loXLWS = oXLWS(@m.tuXLWS, @m.llOpened)
llResult = vartype(m.loXLWS) == 'O'
assert m.llResult message program() + ": impossible d'accéder à la feuille Excel sépcifiée " + cLitteral(m.tuXLWS)
if m.llResult
with m.loXLWS as Excel.WorkSheet
* {fr} Compter les lignes et les colonnes de la feuille
local lnRows, lnCols
lnRows = .UsedRange.rows.count
lnCols = .UsedRange.columns.count
* {fr} Si au moins une ligne
llResult = m.lnRows > 1
if m.llResult
* {fr} Si la colonne spécifiée existe
local lnCol
lnCol = nXLWSCol(m.loXLWS, m.tuCol)
llResult = m.lnCol > 0
assert m.llResult message program() + " - spécification de colonne invalide" + cLitteral(m.tuCol)
if m.llResult
* {fr} Voir s'il faut appliquer une couleur aux cellules concernées
local llColor
llColor = vartype(m.tnColor) == 'N' and m.tnColor > 0 and m.tnColor < rgb(255,255,255)
* {fr} Pour chaque ligne de données
local lnRow, luValue, llAlign
for m.lnRow = 2 to m.lnRows && {fr} la première ligne doit contenir les noms des champs
* {fr} Si la cellule contient un saut de ligne, traiter
with .Cells(m.lnRow, m.lnCol)
luValue = .value
if vartype(m.luValue) == 'C' ;
and LF $ m.luValue
.value = m.luValue && {fr} Simulates 'F2 + enter'
.WrapText = .t. && {fr} Retour à la ligne automatique
.EntireRow.autofit && {fr} Hauteur de ligne automatique
if m.llColor
.Interior.color = m.tnColor
endif
llAlign = .t.
endif
endwith
endfor
endif
* {fr} Si au moins une cellule détectée, aligner tout en haut
if m.llAlign
.UsedRange.VerticalAlignment = xlVAlignTop
endif
* {fr} Sauvegarder le classeur
.parent.saveas(.parent.fullname, xlWorkbookNormal)
endif
endwith
endif
* {fr} Si le classeur a été ouvert, refermer
if m.llOpened
loXLWS.parent.close()
endif
return m.llResult
* --------------------------------------------------
procedure XLWStableBlankLinesDel && {fr} Supprime les lignes vides d'un tableau Excel
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tcXLWSName && [nom du classeur] {fr} Nom de la feuille si on veut la renommer
local llResult && {fr} La feuille a été traitée
* {fr} Si la feuille excel peut être ouverte
local llOpened, loXLWS as Excel.WorkSheet
loXLWS = oXLWS(@m.tuXLWS, @m.llOpened)
llResult = vartype(m.loXLWS)=='O' and not isnull(m.loXLWS)
if m.llResult
with m.loXLWS as Excel.WorkSheet
* {fr} S'il y a des lignes et des colonnes
local lnRows, lnCols
lnRows = .UsedRange.rows.count
lnCols = .UsedRange.columns.count
llResult = m.lnRows > 1 and m.lnCols > 0
if m.llResult
* {fr} Pour chaque Ligne
local lnRow, lnCol, llRowEmpty, luValue
for m.lnRow = m.lnRows to 2 step -1
* {fr} Si chaque cellule est vide
llRowEmpty = .f.
for m.lnCol = 1 to m.lnCols
luValue = .Cells(m.lnRow, m.lnCol).value
llRowEmpty = empty(m.luValue) or isnull(m.luValue)
if not m.llRowEmpty
exit
endif
endfor
* {fr} Si la ligne est vide, supprimer
if m.llRowEmpty
.Cells(m.lnRow,1).EntireRow.delete()
endif
endfor
* {fr} Sauvegarder le classeur en version courante
.parent.saveas(iif(vartype(m.tuXLWS) == 'C', m.tuXLWS, .fullname), xlWorkbookNormal)
endif
* {fr} Refermer le classeur s'il était fermé à l'entrée
if m.llOpened
.parent.close
endif
endwith
endif
return m.llResult
* ---------------------
procedure XLWSColFormatNumber && {fr} Applique un format de présentation à une colonne d'une feuille Excel
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne
, tcNumberFormat && [sans changement] {fr} Code format Excel à appliquer
local loXLWSCol as Excel.range, llOpened, llResult
* {fr} Si on peut accéder à la colonne de la feuille Excel
loXLWSCol = oXLWSCol(m.tuXLWS, m.tuCol, @m.llOpened)
llResult = not isnull(m.loXLWSCol)
if m.llResult
local loWB as Excel.Workbook, lcFormat
loWB = m.loXLWSCol.parent.parent
* {fr} Si un format est spécifié
lcFormat = uDefault(m.tcNumberFormat, '')
llResult = not empty(m.lcFormat)
assert m.llResult message program() + " - Spécification de format invalide " + cLitteral(m.tcNumberFormat)
if m.llResult
* {fr} Appliquer le format à la colonne
loXLWSCol.NumberFormat = m.lcFormat
* {fr} Sauvegarder le classeur en version courante
loWB.saveas(iif(vartype(m.tuXLWS) == 'C', m.tuXLWS, .fullname), xlWorkbookNormal)
endif
* {fr} Refermer le classeur si on l'a ouvert à l'entrée
if m.llOpened
loWB.close
endif
endif
return m.llResult
* ---------------------
procedure XLWSColFormatHAlign && {fr} Applique un format d'alignement horizontal à une colonne d'une feuille Excel
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne
, tnHAlign && [xlHAlignGeneral] {fr} format d'alignement horizontal à appliquer (selon constantes Excel)
local loXLWSCol as Excel.range, llOpened, llResult
* {fr} Si on peut accéder à la colonne de la feuille Excel
loXLWSCol = oXLWSCol(m.tuXLWS, m.tuCol, @m.llOpened)
llResult = not isnull(m.loXLWSCol)
if m.llResult
local loWB as Excel.Workbook
loWB = m.loXLWSCol.parent.parent
* {fr} Si un format est spécifié
local lnHAlign
lnHAlign = uDefault(m.tnHAlign, xlHAlignGeneral)
llResult = inlist(m.lnHAlign, xlHAlignCenter,xlHAlignCenterAcrossSelection,xlHAlignDistributed,xlHAlignFill,xlHAlignGeneral,xlHAlignJustify,xlHAlignLeft,xlHAlignRight)
assert m.llResult message program() + " - Spécification de format invalide : " + cLitteral(m.tnHAlign)
if m.llResult
* {fr} Appliquer le format à la colonne
loXLWSCol.HorizontalAlignment = m.lnHAlign
* {fr} Sauvegarder le classeur en version courante
loWB.saveas(iif(vartype(m.tuXLWS) == 'C', m.tuXLWS, .fullname), xlWorkbookNormal)
endif
* {fr} Refermer le classeur si on l'a ouvert à l'entrée
if m.llOpened
loWB.close
endif
endif
return m.llResult
* ---------------------
procedure oXLWSCol && {fr} Colonne d'une feuille Excel
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne
, tlOpened && @ {fr} Le classeur excel a été ouvert
local llResult, loResult as Excel.range && {fr} Référence à l'objet colonne
loResult = .null.
* {fr} Si on peut accéder à la feuille Excel
local loXLWS as Excel.WorkSheet
loXLWS = oXLWS(@m.tuXLWS, @tlOpened)
llResult = vartype(m.loXLWS) == 'O'
assert m.llResult message program() + ": impossible d'accéder à la feuille Excel " + cLitteral(m.tuXLWS)
if m.llResult
with m.loXLWS as Excel.WorkSheet
* {fr} Si au moins une ligne dans la feuille
local lnRows
lnRows = .UsedRange.rows.count
llResult = m.lnRows > 1
assert m.llResult message program() + " - Moins d'une ligne dans la feuille " + cLitteral(m.tuXLWS)
if m.llResult
* {fr} Si la colonne spécifiée existe
local lnCol
lnCol = nXLWSCol(m.loXLWS, m.tuCol)
llResult = m.lnCol > 0
assert m.llResult message program() + " - spécification de colonne invalide : " + cLitteral(m.tuCol)
if m.llResult
loResult = m.loXLWS.Cells(1, m.lnCol).EntireColumn
endif
endif
endwith
endif
return m.loResult
* ---------------------
procedure XLWSRowsInteriorColor && {fr} Applique une couleur de fond à des rangées d'une feuille Excel selon un critère de filtre sur une colonne
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne
, tcCrit; && {fr} Critère à appliquer : UDF de type logique à UN SEUL PARAMÃTRE ou opérateur relationnel (=<>$!)
, tlOpLeft; && [.F.] {fr} l'opération relationnelle est appliquée à gauche de la valeur
, tnColor && [-1] {fr} Couleur rgb(), -1 pour xlColorIndexNone, -2 pour xlColorIndexAutomatic
local llResult
* {fr} Si on peut sélectionner les rangées de la feuille Excel
local loXLWSRows as Excel.range, llOpened
loXLWSRows = oXLWSRowsFilter(m.tuXLWS, m.tuCol, m.tcCrit, m.tlOpLeft, @m.llOpened)
llResult = not isnull(m.loXLWSRows)
if m.llResult
local loWB as Excel.Workbook
loWB = m.loXLWSRows.parent.parent
* {fr} Donner sa valeur par défaut à la couleur spécifiée
local lnColor
lnColor = uDefault(m.tnColor, -1)
lnColor = iif(m.lnColor < -2 or m.lnColor > rgb(255,255,255), -1, m.lnColor)
* {fr} Appliquer la couleur demandée
with m.loXLWSRows.Interior
if m.lnColor < 0
.ColorIndex = iif(m.lnColor = -1, xlColorIndexNone, xlColorIndexAutomatic)
else
.color = m.lnColor
endif
endwith
* {fr} Sauvegarder le classeur en version courante
loWB.saveas(iif(vartype(m.tuXLWS) == 'C', m.tuXLWS, .fullname), xlWorkbookNormal)
* {fr} Refermer le classeur si on l'a ouvert à l'entrée
if m.llOpened
loWB.close()
endif
endif
return m.llResult
* ---------------------
function oXLWSRowsFilter && {fr} Rangées d'une feuille Excel selon un critère de filtre sur une colonne
lparameters ;
tuXLWS; && {fr} Référence à une feuille ou adresse d'un classeur Excel
, tuCol; && {fr} n° ou nom de la colonne oû se trouve le critère
, tcCrit; && [aucun, toutes les lignes sont renvoyées] {fr} Critère de filtre des lignes : UDF de type logique à UN SEUL PARAMÃTRE ou opération relationnelle (=<>$!)
, tlOpLeft; && [.F.] {fr} l'opération relationnelle est appliquée à gauche de la valeur
, tlOpened && @ {fr} Le classeur excel a été ouvert
local loResult as Excel.range && {fr} Référence à l'objet Range contenant les lignes
loResult = .null.
local llResult
* {fr} Si on peut accéder à la feuille Excel
local loXLWS as Excel.WorkSheet
loXLWS = oXLWS(@m.tuXLWS, @tlOpened)
llResult = vartype(m.loXLWS) == 'O'
assert m.llResult message program() + ": impossible d'accéder à la feuille Excel " + cLitteral(m.tuXLWS)
if m.llResult
with m.loXLWS as Excel.WorkSheet
* {fr} Si au moins une ligne dans la feuille
local lnRows
lnRows = .UsedRange.rows.count
llResult = m.lnRows > 1
assert m.llResult message program() + " - moins de deux lignes dans la feuille " + cLitteral(m.tuXLWS)
if m.llResult
* {fr} Si la colonne spécifiée existe
local lnCol
lnCol = nXLWSCol(m.loXLWS, m.tuCol)
llResult = m.lnCol > 0
assert m.llResult message program() + " - spécification de colonne invalide : " + cLitteral(m.tuCol)
if m.llResult
* {fr} Si le critère est précisé
local lcCrit
lcCrit = alltrim(uDefault(m.tcCrit, ''))
llResult = not empty(m.lcCrit)
if m.llResult
local llOpLeft
llOpLeft = uDefault(m.tlOpLeft, .f.)
* {fr} Si le critère est valide
local luVal, lcType, lcTest, llOp, loErr
luVal = .Cells(2, m.lnCol).value
lcType = vartype(m.luVal)
lcTest = cLitteral(m.luVal)
#define VFPLOGOP '=<>$!'
llOp = iif(m.llOpLeft, right(m.lcCrit, 1) $ VFPLOGOP, left(m.lcCrit, 1) $ VFPLOGOP)
try
local lcEval
lcEval = cCritEval(m.lcCrit, m.lcTest, m.llOp, m.llOpLeft)
luResult = evaluate(m.lcEval)
llResult = vartype(m.luResult) == 'L'
catch to m.loErr
endtry
assert m.llResult message program() + " - critère de filtrage incorrect : " + cLitteral(m.tcCrit)
if m.llResult
* {fr} Pour chaque ligne à partir de la seconde (la première contient les noms des champs)
local lnRow, luVal, loXL as Excel.application
loXL = .parent.parent
for m.lnRow = 2 to m.lnRows
with .Cells(m.lnRow, m.lnCol) as Excel.range
luVal = .value
* {fr} Si la valeur est du type attendu
if vartype(m.luVal) == m.lcType
* {fr} Si le critère est respecté
lcTest = cLitteral(m.luVal)
if evaluate(cCritEval(m.lcCrit, m.lcTest, m.llOp, m.llOpLeft))
* {fr} Ajouter la ligne au Range
loResult = iif(isnull(m.loResult), .EntireRow, m.loXL.union(m.loResult, .EntireRow))
endif
endif
endwith
endfor
endif
else
loResult = .Cells
endif
endif
endif
endwith
endif
return m.loResult
* ---------------------
function cCritEval && {fr} Critère de filtre à évaluer
lparameters ;
tcCrit; && {fr} Critère de filtre
, tcVal; && {fr} Valeur sous forme de constante caractère
, tlOp; && {fr} Le critère est une opération relationnelle (UDF si .F.)
, tlOpLeft && {fr} l'opération relationnelle est appliquée à gauche de la valeur
return icase(;
!m.tlOp,;
m.tcCrit + '(' + m.tcVal + ')',;
m.tlOpLeft,;
m.tcCrit + m.tcVal,;
m.tcVal + m.tcCrit;
)
* ---------------------
function nXLWSCol && {fr} N° d'une colonne d'après son nom inscrit dans la première ligne
lparameters ;
toXLWS as Excel.WorkSheet,; && {fr} Référence présumée à une feuille Excel
tuCol && {fr} Nom ou n° de de la colonne
local lnResult
lnResult = 0
local lcType
lcType = vartype(m.tuCol)
if loXLWS(m.toXLWS) ;
and m.lcType $ 'CN' ;
and not empty(m.tuCol)
* {fr} Compter les lignes et les colonnes de la feuille
local lnRows, lnCols
lnRows = m.toXLWS.UsedRange.rows.count
lnCols = m.toXLWS.UsedRange.columns.count
* {fr} Si au moins une ligne et une colonne
if m.lnRows > 0 and m.lnCols > 0
* {fr} Si un n° de colonne a été spécifié et qu'il est dans la plage de colonnes, OK
if m.lcType == 'N'
if m.tuCol <= m.lnCols
lnResult = m.tuCol
endif
* {fr} Sinon (nom de colonne spécifié)
else
* {fr} Pour chaque cellule de la première ligne
local lcCol, lnCol, luValue
lcCol = upper(alltrim(m.tuCol))
for m.lnCol = 1 to m.lnCols
* {fr} Si la valeur contient le nom de colonne cherché, trouvé !
luValue = m.toXLWS.Cells(1, m.lnCol).value
if vartype(m.luValue) == 'C' ;
and upper(alltrim(m.luValue)) == m.lcCol
lnResult = m.lnCol
exit
endif
endfor
endif
endif
endif
return m.lnResult
* -------------------------------------------------------------
function oXL && {fr} Référence à un objet Excel.Application
local loResult as Excel.application;
, lcWindow;
loResult = .null.
* {fr} Si Excel est déjà instancié dans la variable standard (publique ou privée)
if vartype(m.poXL) == 'O' ; && {fr} standard Abaque pour éviter d'instancier excel plusieurs fois
and type('m.poXL.ActiveWorkbook') == 'O' && {fr} vrai même si aucun classeur n'est ouvert
loResult = m.poXL
* {fr} Sinon (aucune référence disponible)
else
lcWindow = woutput()
try
loResult = getobject('', 'Excel.Application') && {fr} le premier paramètre est requis
loResult = iif(vartype(m.loResult) == 'O', m.loResult, createobject('Excel.Application'))
catch
endtry
if vartype(m.loResult) == 'O'
loResult.DisplayAlerts = .f.
* {fr} Rétablir la fenêtre active avant l'instanciation
if empty(m.lcWindow) or not wvisible(m.lcWindow)
activate screen
else
activate window (m.lcWindow)
endif
endif
endif
return m.loResult
endfunc
* -------------------------------------------------------------
function oXLWB && {fr} Référence à un objet Excel.WorkBook
lparameters ;
tuXLWB; && {fr} Référence ou adresse d'un classeur Excel
, tlOpened; && @ {fr} le classeur a été ouvert
, tlCreated && @ {fr} le classeur a été créé
store .f. to tlOpened, tlCreated
local loResult as Excel.Workbook;
, llResult;
, loXL as Excel.application;
loResult = .null.
do case
* {fr} Si une référence à un classeur Excel a été passée
case loXLWB(m.tuXLWB)
* {fr} Renvoyer tout simplement
loResult = m.tuXLWB
* {fr} Si l'adresse d'un classeur Excel a été passée
case vartype(m.tuXLWB)=='C' ;
and upper(justext(m.tuXLWB)) == EXT_XLS
* {fr} Démarrer Excel
loXL = oXL()
do case
case !Vartype(m.loXL) == 'O'
* {fr} Si le classeur existe
case lFile(m.tuXLWB)
* {fr} Ouvrir le classeur
loResult = m.loXL.Workbooks.open(fullpath(m.tuXLWB), 0) && 0: Ne met à jour aucune référence
tlOpened = .t.
* {fr} Sinon (le classeur n'existe pas)
otherwise
* {fr} Si l'adresse du classeur est valide
llResult = lDir(justpath(m.tuXLWB), .T.)
assert m.llResult message program() + " - Invalid Excel Workbook specification : " + cLitteral(m.tuXLWB)
if m.llResult
loResult = m.loXL.Workbooks.add()
tlCreated = .t.
endif
endcase
endcase
return m.loResult
* ---------------------
function oXLWS && {fr} Référence à un objet Excel.Worksheet
lparameters ;
tuXLWS; && {fr} Référence à une Feuille Excel OU @ adresse d'un classeur [(|!,) Nom Feuille]
, tlOpened && @ {fr} le classeur a été ouvert
tlOpened = .f.
local loResult as Excel.WorkSheet;
, loXLWB as Excel.Workbook;
, liSep;
, lcXLWB;
, lcXLWS;
loResult = .null.
* {fr} Si une référence à une feuille Excel a été passée
do case
case loXLWS(m.tuXLWS)
* {fr} Renvoyer tout simplement
loResult = m.tuXLWS
case vartype(m.tuXLWS) == 'C'
* {fr} Lire l'adresse du classeur [et le nom de la feuille]
liSep = evl(evl(at_c('|', m.tuXLWS), at_c(',', m.tuXLWS)), at_c('!', m.tuXLWS))
if m.liSep > 0
lcXLWB = leftc(m.tuXLWS, m.liSep - 1)
lcXLWS = substrc(m.tuXLWS, m.liSep + 1)
else
store cFileCased(m.tuXLWS, .t.) to lcXLWB, m.tuXLWS
lcXLWS = ''
endif
* {fr} Si le classeur peut être ouvert
loXLWB = oXLWB(m.lcXLWB)
if vartype(m.loXLWB) == 'O'
tlOpened = .t.
* {fr} Renvoyer une référence à la feuille spécifiée, à défaut la première feuille du classeur
loResult = iif(lXLWS(m.lcXLWS, m.loXLWB);
, m.loXLWB.Sheets(m.lcXLWS);
, m.loXLWB.ActiveSheet;
)
endif
endcase
return m.loResult
* ---------------------
function acXLWSs && {fr} Noms des feuilles de calcul contenues dans un classeur Excel
lparameters ;
taXLWS; && @ {fr} Noms des feuilles de calcul contenues dans le classeur Excel
, tcXLWBaddr && {fr} Adresse du classeur Excel à explorer
external array taXLWS
local llXLWBAddr, lnResult && {fr} Nombre de feuilles de calcul contenues dans le classeur Excel
lnResult = 0
* {fr} Initialiser le tableau des feuilles
aClear(@m.taXLWS)
* {fr} Si le classeur XL indiqué peut être localisé
llXLWBAddr = file(m.tcXLWBaddr)
assert m.llXLWBAddr message "Impossible de trouver un classeur XL à l'adresse" + alltrim(m.tcXLWBAdd)
if m.llXLWBAddr
* {fr} Ouvrir le classeur XL en automation
local loXL as Excel.application,;
loXLWB as Excel.Workbook
loXL = oXL()
loXLWB = loXL.Workbooks.open(fullpath(m.tcXLWBaddr), 0, .t.)
* {fr} Lire les noms des feuilles de calcul contenues dans le classeur
local loXLWS as Excel.WorkSheet
for each m.loXLWS in m.loXLWB.Worksheets
lnResult = m.lnResult + 1
dimension m.taXLWS[m.lnResult]
taXLWS[m.lnResult] = m.loXLWS.name
endfor
* {fr} Fermer le classeur et XL
loXLWB.close()
loXL = .null.
endif
return m.lnResult
* ---------------------
function loXL && {fr} C'est une référence à un objet application Excel
lparameters toXL && {fr} Référence présumée à un objet application Excel
return ;
vartype(m.toXL) == 'O';
and type('m.toXL.Name') == 'C';
and 'excel' $ lower(m.toXL.name) ;
and type('m.toXL.WorkBooks') == 'O'
* ---------------------
function loXLWB && {fr} C'est une référence à un objet classeur Excel
lparameters toXLWB && {fr} Référence présumée à une classeur Excel
return ;
vartype(m.toXLWB) == 'O';
and type('m.toXLWB.Application') == 'O';
and loXL(m.toXLWB.application) ;
and type('m.toXLWB.WorkSheets') == 'O'
* ---------------------
function loXLWS && {fr} C'est une référence à un objet feuille de calcul Excel
lparameters toXLWS && {fr} Référence présumée à une feuille Excel
return ;
vartype(m.toXLWS) == 'O';
and type('m.toXLWS.parent') == 'O';
and loXLWB(m.toXLWS.parent)
* ---------------------
procedure loXLWS_Test && {fr} Teste loXLWS(), nouvelle version
local loTest as abUnitTest of abDev.prg
loTest = newobject('abUnitTest', 'abDev.prg')
local loXL as Excel.application, loXLWB as Excel.Workbook
loXL = oXL()
loXLWB = m.loXL.Workbooks.add()
loTest.Test(.t., m.loXLWB.ActiveSheet)
loXLWB.close()
loXL.quit()
return loTest.Result()
* ---------------------
function lXLWS && {fr} La feuille XL existe dans le classeur spécifié
lparameters ;
tcSheet; && {fr} Nom de la feuille cherchée
, tuXLWB && {fr} Référence ou adresse d'un classeur Excel
local llResult
* {fr} Si une feuille valide a été indiquée
llResult = vartype(m.tcSheet) == 'C' and not empty(m.tcSheet)
if m.llResult
* {fr} Si le classeur spécifié peut être ouvert
local loXLWB as Excel.Workbook
loXLWB = oXLWB(m.tuXLWB)
llResult = not isnull(m.loXLWB)
if m.llResult
* {fr} Pour chaque feuille contenue
local lcSheet, loSheet as Excel.WorkSheet
lcSheet = lower(alltrim(m.tcSheet))
llResult = .f.
for each m.loSheet in m.loXLWB.Worksheets
* {fr} Si la feuille porte le nom spécifié, terminé
llResult = lower(m.loSheet.name) == m.lcSheet
if m.llResult
exit
endif
endfor
endif
endif
return m.llResult
* -------------------------------------------------------------
procedure XLWBfromWSs && {fr} Regroupe des feuilles Excel dans un classeur
lparameters ;
taWBWSSrce; && @ {fr} Adresses des classeurs | noms des feuilles [Feuille # 1] à regrouper
, tcWBDestAddr; && {fr} Adresse du classeur Excel destination
, tlWBSrceDel; && [.F.] {fr} Supprimer les classeurs source après copie des feuilles dans le classeur destination
, tlBeautify && [.F.] {fr} Embellir les feuilles
external array taWBWSSrce
tlWBSrceDel = lTrue(m.tlWBSrceDel)
tlBeautify = lTrue(m.tlBeautify)
local llResult && {fr} Toutes les feuilles ont bien été copiées dans le classeur destination
* {fr} Si des classeurs source sont indiqués
if type('taWBWSSrce', 1) == 'A'
local lnWBSrces
lnWBSrces = alen(m.taWBWSSrce, 1)
if m.lnWBSrces > 0 ;
and lFileAddress(m.tcWBDestAddr)
* {fr} Vérifier si des feuilles ont aussi été indiquées
local llWSSrce