HotXLS shranjuje izbire in položaje drsenja delovnega lista na podokno skozi en sam API, ki pozna podokna, na obeh TXLSWorksheet in TXLSXWorksheet: SelectAreas, GetSelectedAreas, ScrollWindow in TryGetWindowScroll. Za klasične datoteke .xls HotXLS zapiše zapise Selection BIFF8 (0x001D) z največ 1369 območji vsak, logična imena podoknih pretvori v bajte podoknih, ki jih definira format, vsako os drsenja pa pusti na zapisu Window2 ali Pane, kjer jo pričakuje Excel
Težava se ponavadi pokaže v orodju za usklajevanje ali revizijo. Orodje odpre izvoz glavne knjige, poišče vsako celico, ki se ne strinja z izvornim sistemom, in shrani delovni zvezek s temi celicami, ki so že izbrane pod zamrznjeno vrstico glave, tako da pregledovalec pristane na razlikah, namesto da bi jih iskal z drsenjem. Pri štiridesetih razlikah dela brez težav. Datoteka ob koncu meseca jih ima 3.000, en sam zapis Selection s 3.000 območji pa ne more obstajati: njegovo telo bi potrebovalo 18.009 bajtov, več kot dvakrat toliko, kolikor en zapis BIFF8 zmore nositi. Položaj drsenja ima podobno past. Na listu z zamrznjenimi podokni je »to, kam je gledal uporabnik« štiri podokna, ki si delijo dva položaja vrstic in dva položaja stolpcev, ne ena koordinata
Zakaj velika izbira potrebuje več kot en zapis Selection?
Velika izbira potrebuje več zapisov, ker je telo zapisa BIFF8 omejeno na 8224 bajtov in vsako izbrano območje stane fiksnih šest bajtov. [MS-XLS] §2.4.248 razporedi zapis Selection kot 9-bajtni fiksni del (bajt podokna, rwAct in colAct za aktivno celico, irefAct za aktivno območje in cref za število območij), ki mu sledi cref struktur RefU, vsaka z dvema 16-bitnima vrsticama in dvema 8-bitnima stolpcema. Največje število, ki se prilega, je (8224 − 9) / 6 zaokroženo navzdol, torej 1369, to pa da telo 8223 bajtov, bajt pod mejo. TXLSWorksheet.StoreSelectionGroup to konstanto uporabi kot MaxAreasPerRecord in večjo skupino zapiše kot zaporedne zapise Selection za isto podokno, 1369 območij naenkrat
Podrobnost, ki grize, je irefAct. Vsak odrezek ponovi isto aktivno vrstico, aktivni stolpec in indeks aktivnega območja, irefAct pa indeksira združeno zaporedje vseh odrezkov, ne območij znotraj zapisa, ki ga nosi. Izbira eno območje čez mejo to naredi konkretno: 1370 območij z zadnjim aktivnim postane dva zapisa, prvi s cref 1369 in drugi s cref 1, oba pa nosita irefAct 1369. Ta vrednost je večja od lastnega števila območij drugega zapisa. Bralec, ki v vsakem zapisu preverja irefAct proti cref, zavrne veljavno datoteko, bralec, ki svoje stanje zamenja ob vsakem zapisu, pa izgubi prvih 1369 območij. Bralec HotXLS zaporedne zapise istega podokna doda v eno skupino, zahteva, da se vsak odrezek strinja o aktivni celici in indeksu, preizkus obsega pa požene šele na zapisu EOF delovnega lista, ko je znano celotno zaporedje. Preobremenitev SelectAreas, ki najprej vzame podokno, zato nima stropa 1369 območij. Potrdi vsak sklic A1 in aktivni indeks, še preden prevzame zaklep za pisanje delovnega lista, in vrne False, ne da bi se prejšnja izbira spremenila, če je karkoli napačno oblikovano
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Diffs: TXLSSelectedAreas;
I: Integer;
begin
Book := TXLSWorkbook.Create;
try
Sheet := Book.Sheets.Add;
Sheet.FreezePanes(1, 1); // vrstica glave in stolpec A ostaneta na mestu
SetLength(Diffs, 3000);
for I := 0 to High(Diffs) do
Diffs[I] := Format('C%d', [I + 2]);
// Zamrznitev ponastavi shranjeno izbiro, zato izberite po zamrznitvi.
// 3000 območij se shrani kot trije zapisi Selection: 1369 + 1369 + 262
if not Sheet.SelectAreas(xlspBottomRight, Diffs, 0) then
raise Exception.Create('Selection rejected');
Book.SaveAs('reconciliation.xls');
finally
Book.Free;
end;
end;
Kateri bajt podokna uporabi zapis Selection?
Zapis Selection svoje podokno prepozna po številski kodi, ki jo definira format: 0 za desno spodaj, 1 za desno zgoraj, 2 za levo spodaj in 3 za levo zgoraj. Javna enumeracija TXLSPanePosition je deklarirana v bralnem vrstnem redu, xlspTopLeft, xlspTopRight, xlspBottomLeft, xlspBottomRight, zato je Ord(xlspTopLeft) 0, kar je v datoteki podokno desno spodaj. Neposredna pretvorba enumeracije v bajt podokna bi vsako izbiro levo zgoraj zapisala na podokno desno spodaj, brez vsake napake. Vsaka vstopna točka HotXLS, ki pozna podokna, enumeracijo namesto tega pretvori prek izrecnega stavka case, tako da klicniki s številskimi kodami sploh nikoli ne opravljajo. Preverjen je tudi obstoj podokna: podokno desno zgoraj obstaja le pri navpični razdelitvi, levo spodaj le pri vodoravni, desno spodaj pa le pri obeh. Za podokno, ki ga trenutna geometrija razdelitve ali zamrznitve nima, SelectAreas vrne False, GetSelectedAreas pa prazen niz z ActiveAreaIndex na -1, brez ustvarjanja podokna, objekta izbire ali celice v delovnem zvezku
Kje živi položaj drsenja vsakega podokna?
Položaj drsenja vsakega podokna je razdeljen med dva zapisa, ker si štiri podokna delijo le dva položaja vrstic in dva položaja stolpcev. V klasičnem delovnem zvezku sta prva vidna vrstica zgornjih podoknih in prvi vidni stolpec levih podoknih Window2.rwTop in Window2.colLeft, vrstica spodnjih podoknih in stolpec desnih podoknih pa sta Pane.rwTop in Pane.colLeft. ScrollWindow(xlspTopRight, R, C) torej zapiše Window2.rwTop in Pane.colLeft, nastavitev stolpca podokna desno zgoraj pa premakne tudi podokno desno spodaj, tako kot si v Excelu delita en vodoravni drsnik. Javne metode uporabljajo številke vrstic in stolpcev z osnovo 1. Manjkajoče podokno vrne False in obe izhodni vrednosti poizvedbe nastavi na nič, koordinata izven obsega pa je zavrnjena, še preden se katera koli os spremeni. Nič od tega ni odvisno od tega, kako pregledovalnik slika mrežo. Kontrola za izrisovanje ima svoja TopRow in LeftCol, kot opisuje članek o izrisovanju delovnih zvezkov v lastni mreži VCL, to pa je stanje med izvajanjem, ne tisto, kar se shrani
XLSX iste podatke razporedi čez dva elementa: sheetView/@topLeftCell (ECMA-376 Part 1, §18.3.1.87) za okno kot celoto in podrejeni pane/@topLeftCell (§18.3.1.66) za desno spodaj ležeči del razdelitve. Oba atributa sta lahko prisotna hkrati. HotXLS zunanji atribut najprej prebere v polja na ravni okna, podrejeni pane lahko preglasi le polja na ravni podokna, oba pa zapiše nazaj ločeno. Zložitev obeh plasti v eno je natanko tisto, zaradi česar zgornji ali levi položaj drsenja ob nalaganju tiho izgine. Kopije delovnega lista nosita obe plasti v obeh pogonih. Starejše vstopne točke ohranijo svoje izvirno vedenje: klasični lastnosti ScrollRow in ScrollColumn ter ničelno osnovani XLSX SetPaneScroll in GetPaneScroll. Samo geometrijo zamrznitve in razdelitve nastavite z nastavitvami na ravni lista, ki jih pokriva zaščita lista, nastavitev strani in tiskanje
var
Row, Col: Integer;
begin
Sheet.FreezePanes(1, 1);
// Desno spodaj: spodnja os vrstic (Pane.rwTop) in desna os stolpcev (Pane.colLeft)
Sheet.ScrollWindow(xlspBottomRight, 500, 3);
// Desno zgoraj si deli desno os stolpcev, zato to premakne tudi desno spodaj na stolpec 6
Sheet.ScrollWindow(xlspTopRight, 1, 6);
if Sheet.TryGetWindowScroll(xlspBottomRight, Row, Col) then
Memo1.Lines.Add(Format('Bottom-right starts at row %d, column %d', [Row, Col]));
// Desno spodaj se začne pri vrstici 500, stolpcu 6
end;
Kaj se zgodi, ko je zapis Selection pokvarjen?
Ko je zapis Selection pokvarjen, ga HotXLS obdrži kot neprozorne bajte, prijavi diagnostično kodo 1304 (xlsDiagnosticSelectionRecordInvalid) in izvorno telo ob shranjevanju zapiše nazaj bajt za bajtom. Preden zapis pride v skupino svojega podokna, ga bralec preveri po vrsti. Bajt podokna mora biti 3 ali manj. Zapisi za eno podokno morajo biti v toku sosednji. 9 fiksnih bajtov mora biti prisotnih. cref mora biti med 1 in 1369, telo pa mora biti točno 9 + cref × 6 bajtov dolgo. Vsak odrezek v skupini se mora strinjati o aktivni celici in irefAct, irefAct ne sme imeti nastavljenega znakovnega bita, aktivni stolpec mora biti na mreži in nobeno območje ne sme imeti obrnjenih mej. Težave v enem samem fizičnem zapisu so prijavljene enkrat na zapis. Protislovja, ki se pokažejo šele po združevanju, kot je irefAct, ki kaže čez skupno število območij, ali aktivna celica izven indeksiranega območja, so prijavljena enkrat na skupino pri EOF. Neveljavna skupina ostane nevidna tipiziranemu API-ju: GetSelectedAreas vrne prazen niz z indeksom -1 za to podokno, vsa ostala podokna pa še naprej delujejo
var
I: Integer;
D: TXLSDiagnostic;
begin
if Book.Open('supplier-upload.xls') <> 1 then
Exit;
for I := 0 to Book.Diagnostics.Count - 1 do
begin
D := Book.Diagnostics[I];
if D.Code = xlsDiagnosticSelectionRecordInvalid then
Log.Add(Format('%s: record $%.4x kept opaque (%s)',
[D.SheetName, D.RecordId, D.Message]));
end;
end;
Kako izbire preživijo vstavljanje vrstic in stolpcev?
Izbire preživijo strukturne spremembe, ker vstavljanje ali brisanje celih vrstic ali stolpcev preko enega skupnega preslikovalnika znova preslika vsako predstavljeno skupino podoknih v obeh pogonih, klasičnem in XLSX. Območja, ki preživijo, obdržijo svoj vrstni red, aktivno območje pa svojo identiteto. Če je aktivno območje izbrisano, postane aktivno prvi preživeli naslednik, če pa nič ne sledi, zadnji preživeli predhodnik. Če so izbrisana vsa območja, se skupina zloži v eno celico na meji brisanja, aktivna celica, ki ne pade več znotraj izbranega območja, pa se premakne v kot tega območja levo zgoraj, tako da se indeks in koordinata nikoli ne protislovita. Meje so namerno takšne. Neveljavne klasične skupine preslikovalnik preskoči, namesto da bi jih prepisal v izmišljeno izbiro, zato njihovi izvirni bajti še vedno preživijo obtok. Urejanje enega podokna zamenja le zapise tega podokna in druge pusti bajtno identične. ODS sploh ne dobi stanja izbire podoknih, ker ODF nima enakovredne strukture pogleda delovnega lista, ki bi jo nosila
Če Vaša aplikacija piše datoteke .xls, ki jih uporabniki odprejo in se morajo po njih premikati — naj gre za pregled označenih celic, nadaljevanje tam, kjer so obstali, ali deljeno zamrznjeno nadzorno ploščo — je API za izbire in drsenje, ki pozna podokna, del komponente preglednic HotXLS za Delphi, in deluje enako za XLS in XLSX