|
[ Voodoo @ 08.12.2004. 12:18 ] @
| Razvijam jedan Total Commander klon i sasvim fino ide, osim jedne stvari: kako najefikasnije izvesti prikazivanje slicica levo od imena fajlova (lista je TListView namestena na Report stil)? Ako napravim TImageList i gradim listu ikonica svaki put kada ucitam direktorijum, iole veci broj fajlova se sto godina ucitava. Pokusao sam da izvedem to kao OwnerDraw, gde slicice extrakujem i prikazem samo kada se iscrtavaju (kao sto radi totalcmd) i to radi brzo, ali mi trulo izgleda (i plus sto ne znam da izvedem da preduga imena prikazem kao "Mnogodugoime...", sa tri tacke u nastavku a da izgleda lepo)...
Postoji li elegantno a efektivno resenje, kao sto radi Windows Explorer (posto on koristi ListView)? Istina, postoji sample koji koristi ListView kontrolu za prikazivanje fajlova kao explorer, ali ne zelim da se oslanjam na shell...
Zahvaljujem na svakom savetu. |
[ sasas @ 08.12.2004. 12:36 ] @
Ajde posalji kood, da pogledamo kako ti izgleda to kreiranje image list-a. Pomoglo bi da nam makar opises algoritam.
Tesko je ovako naslepo nagadjati sta si vec optimizovao a sta nisi...
ss.
[ Riste Pejov @ 08.12.2004. 12:40 ] @
A zasto jednostavno ne presmetas prema velicini u pixelima koliko to predmeta moze da stane u vidljivoj povrsini i onda samo toliko dodas u ListView, i kada korisnik skroluje onda dodas ostatak, ili mozda dodas u tvojot aplikaciji backgroud thread koji ce puniti ListView iza vidljive povrsine.
Primer ako je recimo vidljiva povrsina listview-a 500 pixela, a visina jednog item-a u listview-u 25 pixela onda ucitas 200 njih a ostatak prepustis background thread-u da ucita.
Ili mozda i bi mogao da tekstove u realnom vremenu prikazes a ikonice ostavis background thread-u, sto mislim da bi bilo jos efektivnije resenje.
btw, a zasto gradis listu ikonica svaki put kad ucitas dir ?
[ Voodoo @ 08.12.2004. 12:53 ] @
Zato sto TListItemu moze da se dodeli samo ikonica koja je u TImageListu dodeljenom roditeljskoj TListView kontroli...
algoritam za ucitavanje je standardan (nemam src ovde pa je ovo iz glave):
found := FindFirst(...)
while found = 0 do
begin
if sr.name <> '.' then
item := LevaLista.items.add;
item.caption := sr.name;
...
findnext(sr);
end;
findclose(sr);
E sad, ako gradim TImageListu, onda iscupam ikonu sa ExtractAsociatedIcon, dodam je u listu i stavim item.imageindex := brojac koji se povecava...
samo ovo traje podosta (recimo /windows/system32 se dosta dugo puni... komp je P3, ali hocu da radi na svakom krshu)...
Mada to nije losa ideja, da buildujem TImageListu samo za iteme koji se trenutno vide...
[ sasas @ 08.12.2004. 13:03 ] @
Veliku ustedu ces postici 'kesiranjem' ikonica. Recimo, ikona za word, mp3, .dll je uvek ista i nema potrebe da je za svaki fajl extractujes.
Prilikom pokretanja programa pronadji sve registrovane ekstenzije i ucitaj njihove ikone u image listu. Tako ti ostaje da kad citas dir. samo dodajes ikonice za .exe fajlove.
ExtractIcon je vremenski skupa operacija, a ovim ces izbeci njeno stalno ponavljanje.
ss.
[ Riste Pejov @ 08.12.2004. 14:51 ] @
A zasto ne napises novu klasu, koja ce imati objekte TImageList i TStringList i svaki put kad naletis na tip fajla (extenzija) koju vec nemas u ListView, onda pozoves neki public method (pr GetImageIndex) u kome predas extenziju fajla, i onda samo uz AStringList->IndexOf(ext) dobijes index icone iz ImageList-a. I dodas iconindex u listview. Ukoliko ti extenzija ne postoji, onda lepo uradis ExtractIcon i dodas ikonu u tvojoj ImageList-i sa extenzijom u StringList-u. Na kraj izvrsenja programa mozes stream-ovati ikone na tvrdi disk i na pocetka rada programa ih opet ucitati .... :)
A ako zelise da ovo radi maksimalno brzo, samo odaberi najcesce koriscene ikone i napuni imagelist sa jedno 50-tak ikonica i to ti je to, sve drugo sto nemas u listi, onda lepo ili postavis onu standardnu ikonicu sa prozorcicem iz windowsa ili uradi ExtractIcon
[ Srki_82 @ 08.12.2004. 14:59 ] @
Mozda bi mogao da napises neki svoj Icon Resource Manager? Mozda ovo zvuci malo komplikovano, ali mozda bi ipak moglo da radi ovako:
Napravis klasu koja cuva ikonicu i neki tag (recimo ekstenziju ili puno ime fajla) po kojoj mozes da izvadis odredjenu ikonicu. Znaci... krenes da citas iz Reg., procitas ikonicu za *.mp3 i ubacis je u manager sa tagom "*.mp3", zatim za *.doc... itd. Kasnije kad hoces da pronadjes ikonicu samo pozoves npr. funkciju GetIcon('Pesma.mp3') i manager prvo potrazi da li ima neka ikonica sa tagom "Pesma.mp3" ako je nema onda potrazi sa tagom "*.mp3" ako ni toga nema iscrta ti neku default ikonicu.
Prilikom pokretanja programa iscitas sve standardne ikonice iz Reg. (bice taman dosta vremena da se vidi tvoj splash screen ;) ) i kasnije kad god imas neki .exe, .pif ili .lnk fajl proveris samo da li imas ikonicu u manageru. Ako je nemas ucitas je i pici ;) Jedino sto bi bilo lepo je da pazis da ne zauzmes previse memorije. Ogranicis listu na odredjen broj ikonica i kada treba da dodas novu, a lista ti je puna samo izbrises neku od ikonica .exe fajlova ili ako mozes da znas onu koja najduze nije citana iz managera.
[ Voodoo @ 08.12.2004. 16:13 ] @
Zahvaljujem na savetima momci, probacu sve ovo cim dodjem kuci.
[ bancika @ 08.12.2004. 16:26 ] @
mozes preko API-ja da dobijes handle od sistemskog listView-a koji sadrzi sve ikonice (osim ikona koje predstavljaju ikone - znaci samo ikone za registrovane tipove), to brzo radi koliko znam. takodje koristis api funkcije da dobijes ImageIndex ikone za odredjeni fajl. Nemam bas pri sebi taj kod ali sam vidjao po netu. Ja imam neki ali ne radi bas najbolje na XP-u. Drugi nacin je (tako sam ja radio u jednom svom programu) da dinamicki pravis ListView tako sto svaki put kad otvoris neki direktorijum proveravas da li u listview do tad postoji ikonica za taj tip ili ne. ako nema onda preko API-ja uzmes ikonicu i ubacis je u listview.
Evo koda za to
Code:
function TMainForm.GetSmallIcon(Name: string): Integer;
var
FileInfo: TSHFileInfo;
aIcon: TIcon;
begin
// clear the memory
FillChar(FileInfo, SizeOf(FileInfo), #0);
// get a handle to the ImageList for the file selected
SHGetFileInfo(PChar(Name), 0, FileInfo, SizeOf(FileInfo),
SHGFI_ICON or SHGFI_SMALLICON or SHGFI_SHELLICONSIZE);
aIcon := TIcon.Create;
// assign the handle of the icon returned
aIcon.Handle := FileInfo.hIcon;
// lets paint it transparent
aIcon.Transparent := True;
if Name[Length(Name)] = '\' then //za folder
ObjectIconsSmall.ReplaceIcon(0, aIcon)
else
Result := ObjectIconsSmall.AddIcon(aIcon); //za fajl
// free our icon class
FreeAndNil(aIcon);
DestroyIcon(FileInfo.hIcon);
end;
analogno za vece ikone, koristi flag SHGFI_LARGEICON,, rezultat je ImageIndex dodate ikone
uzgred, mozda ne znas, ali na XP-u moras da konvertujes ImageList u 32bitni format da bi mogao da stavljas XP-ove fancy ikone sa alpha kanalom. To se radi ovako
Code:
procedure TMainForm.ConvertTo32BitImageList(const ImageList: TImageList);
const
Mask: array[Boolean] of Longint = (0, ILC_MASK);
var
TemporyImageList: TImageList;
begin
if Assigned(ImageList) then
begin
TemporyImageList := TImageList.Create(nil);
try
TemporyImageList.Assign(ImageList);
with ImageList do
begin
ImageList.Handle := ImageList_Create(Width, Height, ILC_COLOR32 or Mask[Masked], 0, AllocBy);
if not ImageList.HandleAllocated then
begin
raise EInvalidOperation.Create(SInvalidImageList);
end;
end;
ImageList.AddImages(TemporyImageList);
finally
TemporyImageList.Free;
end;
end;
end;
nadam se da ima vajde od ovoga :)
[ Voodoo @ 09.12.2004. 10:02 ] @
Evo rezultata:
Napravio sam cache svih registrovanih ikona tako sto sam zaredjao po HKEY_CLASSES_ROOT, pokupio ekstenzije, onda otvorio ponaosob svaku i njen DefaultIcon potkljuc i izvadio EXE koji sadrzi ikonu. I to sad kako-tako radi (moram jos nesto da izvedem, izmedju ostalog i ono za ubacivanje alpha kanala u ikonu, ali to kasnije).
Da li bi neko hteo da pogleda ove funkcije koje sam pravio, da negde ne postoji memory leak (mozda nisam rucno unistio nesto sto je trebalo)? Hvala.
Code: //-------------------------------------------------------------------------
// CUPANJE IMENA IZ "IME,INDEKS" STRINGA
//-------------------------------------------------------------------------
function GetExeName(S: string): string;
var
kopija: string;
i: Integer;
res: string;
winpath: array[0..259] of char;
begin
if AnsiContainsStr(S, '%SystemRoot%') then
begin
FillChar(winpath, 260, #0);
GetWindowsDirectory(winpath, 259);
kopija := AnsiReplaceStr(S, '%SystemRoot%', StrPas(winpath));
end
else kopija := Copy(S, 1, Length(S));
res := '';
for i := 1 to Length(kopija) do
begin
if kopija[i] = '"' then Continue;
if kopija[i] = ',' then Break;
res := res + kopija[i];
end;
Result := res;
end;
//-------------------------------------------------------------------------
// CUPANJE INDEKSA IZ "IME,INDEKS" STRINGA
//-------------------------------------------------------------------------
function GetIconIndex(S: string): Integer;
var
i: Integer;
sgn: Integer;
res: Integer;
SreliSmoZarez: Boolean;
begin
SreliSmoZarez := False;
res := 0;
sgn := 1;
for i := 1 to Length(S) do
begin
if S[i] = ',' then SreliSmoZarez := True;
if (S[i] = '-') and (SreliSmoZarez) then sgn := -1;
if (Ord(S[i]) >= Ord('0')) and (Ord(S[i]) <= Ord('9')) and (SreliSmoZarez) then res := (res * 10) + StrToInt(S[i]);
end;
Result := sgn * res;
end;
//-------------------------------------------------------------------------
// KESIRANJE SVIH REGISTROVANIH IKONA
//-------------------------------------------------------------------------
procedure CacheRegIcons(ExtList: TStringList; ImgList: TImageList);
var
Reg: TRegistry;
StrList: TStringList; // Pomocna lista za sve kljuceve iz HKEY_CLASSES_ROOT
i: Integer;
RegTip: string;
RegExe: string;
Icon: TIcon;
SmIcon: HICON;
LgIcon: HICON;
begin
ExtList.Clear;
StrList := TStringList.Create;
Reg := TRegistry.Create(KEY_READ);
Reg.RootKey := HKEY_CLASSES_ROOT;
Reg.OpenKey('', False);
Reg.GetKeyNames(StrList);
Reg.CloseKey;
// Vadjenje samo onih kljuceva koji su ekstenzije
for i := 0 to (StrList.Count - 1) do
if StrList.Strings[i][1] = '.' then ExtList.Add(LowerCase(StrList.Strings[i]));
StrList.Free;
// Cupanje ikona za svaku ekstenziju
for i := 0 to (ExtList.Count - 1) do
begin
Reg.OpenKey(ExtList.Strings[i], False);
RegTip := Reg.ReadString('');
Reg.CloseKey;
Reg.OpenKey(RegTip + '\DefaultIcon', False);
RegExe := Reg.ReadString('');
Reg.CloseKey;
Icon := TIcon.Create;
if not FileExists(GetExeName(RegExe)) then ImgList.GetIcon(1, Icon) else
begin
ExtractIconEx(pchar(GetExeName(RegExe)), GetIconIndex(RegExe), LgIcon, Smicon, 1);
Icon.Handle := SmIcon;
end;
ImgList.AddIcon(Icon);
end;
Reg.Free;
end;
[ Voodoo @ 09.12.2004. 10:33 ] @
Ubacio sam i ovo za 32-bit color ikone i radi... doduse tek kada sam dodao CommCtrl u 'uses' listu :)
(nisam ni znao do sada da su komon kontrole razdvojili u ComCtrls i CommCtrl)
Nego evo da pogledate na sta to lici... solidan broj ikona se ne prikazuje kako treba (to jest prikazuju se pogresne), ali bice ispravljeno.
http://www.geocities.com/ivantod84/VN01.zip
[ Voodoo @ 10.12.2004. 11:04 ] @
Odradio sam i OwnerDrawn verziju, ako nekog interesuje moze skinuti sa istog linka...
Copyright (C) 2001-2025 by www.elitesecurity.org. All rights reserved.
|