P R O G R A M U J E M EObr. 1 Pridávanie projektu do WorkspaceTá je potrebná, ak chcete pouíva kolekcie zaloené <strong>na</strong> šablónových triedach (prenešablónové triedy by ste includovali súbor afxcoll.h).To bolo pouitie triedy CtypedPtrList; <strong>sk</strong>úste si aj pouitie ostatných tried, prièomïalšie projekty pridávajte do Workspace Collections. Odporúèam vám všetky triedykolekcií MFC dobre <strong>na</strong>študova, niekedy sa budete musie rozhodnú, ktorú z nichpouijete. A správne sa rozhodnete len v takom prípade, e budete doko<strong>na</strong>le poz<strong>na</strong>kadú z týchto tried.ZLEPŠENIE PROJEKTU ŠTUDENTI. Teraz s vyuitím kolekcií zlepšímeprojekt StudentList zo 16. èasti. Nová verzia bude schopná uchováva informácie o viace−rých študentoch, prièom bude moné posúva sa po jednotlivých záz<strong>na</strong>moch a vkla−da/maza údaje o konkrétnom študentovi. Nevytvárajte zbytoène nový projekt, všetkyzmeny vyko<strong>na</strong>jte do u existujúceho projektu StudentList.Krok 1. Pridajte menu Student, ktoré bude obsahova poloky: First, Last, Next, Prev,|Separator|, Insert, Remove. Takisto pridajte tlaèidlá <strong>na</strong> toolbar, ktoré budú ma rov<strong>na</strong>kývýz<strong>na</strong>m (a hlavne rov−<strong>na</strong>ké ID), ich grafickúinterpretáciu nechám<strong>na</strong> vás. Pomocou Class−Wizardu <strong>na</strong>mapujte ob−deklaruje objekt ukazovate¾ov <strong>na</strong> triedu CSimpleListClass. Prvý parameter je základnoutriedou kolekcie. Môe to by len trieda CPtrList alebo <strong>na</strong>mi pouitá CObList. CObList saodporúèa pouíva, ak ukladáte objekt odvodený od CObject (èo v <strong>na</strong>šom prípade ajrobíme – pozri triedu CSimpleListClass), v opaènom prípade sa pouíva CPtrList (<strong>sk</strong>úste sipríklad aj s touto triedou a sledujte, ako sa zmení Dump výstup). Na riadkuPOSITION pos=m_SimpleList.GetHeadPosition();deklarujeme premennú pos typu POSITION a priraïujeme jej ukazovate¾ <strong>na</strong> zaèiatokzoz<strong>na</strong>mu m_SimpleList (podrobnejšie v helpe). Táto premenná reprezentuje polohu prvkuv zoz<strong>na</strong>me. Ïalej v kóde pouívame túto premennú v spojení s funkciou GetNext, ktorávracia ukazovate¾ <strong>na</strong> prvok, ktorého pozícia je práve pos, a zároveò inkrementuje tútopremennú, èie po zavolaní funkcie bude obsahova polohu prvku pos+1(ak sme <strong>na</strong>poslednom prvku, tak pos=NULL). Preto pri posúvaní sa po prvkoch v zoz<strong>na</strong>me vykoná−vame test:Obr. 2 Vloenie novejtriedy do projektuwhile (pos!=NULL)èi sme u náhodou nedosiahli jeho koniec. Za povšimnutie ešte stojí proces rušenia objek−tov simpleListObject. Pri odstraòovaní objektov z kolekcie vracia funkcia RemoveHeadukazovate¾ <strong>na</strong> práve rušený objekt, ktorý pouijeme, aby sme zrušili aj samotný objekt(vyòatím ukazovate¾a zo zoz<strong>na</strong>mu sa objekt nezruší!). Do súboru stdafx.h vlote ešte tútoinclude direktívu#include ID ovládacieho prvku Správa Obsluná funkcia TriedaID_STUDENT_FIRST COMMAND OnStudentFirst CStudentListViewID_STUDENT_FIRST UPDATE_COMMAND_UI OnUpdateStudentFirst CStudentListViewID_STUDENT_LAST COMMAND OnStudentLast CStudentListViewID_STUDENT_LAST UPDATE_COMMAND_UI OnUpdateStudentLast CStudentListViewID_STUDENT_NEXT COMMAND OnStudentNext CStudentListViewID_STUDENT_NEXT UPDATE_COMMAND_UI OnUpdateStudentNext CStudentListViewID_STUDENT_PREV COMMAND OnStudentPrev CStudentListViewID_STUDENT_PREV UPDATE_COMMAND_UI OnUpdateStudentPrev CStudentListViewID_STUDENT_INSERT COMMAND OnStudentInsert CStudentListViewID_STUDENT_REMOVE COMMAND OnStudentRemove CStudentListViewID_STUDENT_REMOVE UPDATE_COMMAND_UI OnUpdateStudentRemove CStudentListViewTab. 4 Ovládacie prvky projektu StudentListID ovládacieho prvkuID_STUDENT_FIRSTID_STUDENT_LASTID_STUDENT_NEXTID_STUDENT_PREVID_STUDENT_INSERTID_STUDENT_REMOVEOpisPosun <strong>na</strong> zaèiatok zoz<strong>na</strong>mu (<strong>na</strong> prvý záz<strong>na</strong>m)Posun <strong>na</strong> koniec zoz<strong>na</strong>mu (<strong>na</strong> posledný záz<strong>na</strong>m)Posun <strong>na</strong> ïalší prvok zoz<strong>na</strong>muPosun <strong>na</strong> predchádzajúci prvok zoz<strong>na</strong>muVloenie nového záz<strong>na</strong>muOdstránenie aktívneho záz<strong>na</strong>muTab. 5 Výz<strong>na</strong>m ovládacích prvkov projektu StudentListsluné správy týchto tlaèidiel(v tomto prípade je potrebné<strong>na</strong>mapova pre kadé tlaèidlookrem tlaèidla Insert aj správuUPDATE_COMMAND_UI –Obr. 3 – Okno zlepšenej aplikácie StudentListpodrobnosti sú v tab. 4). Tátospráva je potrebná <strong>na</strong> to, abysme mohli pod¾a aktuálnej pozície v zoz<strong>na</strong>me zneprístupni niektoré tlaèidlo (<strong>na</strong>pr. ak sa<strong>na</strong>chádzame <strong>na</strong> prvom záz<strong>na</strong>me, zneprístupníme tlaèidlá First a Prev). Podrobnejšívýz<strong>na</strong>m tlaèidiel je v tab. 5. Nakoniec ešte správne odstráòte tlaèidlo IDC_GETDATA(„správne“ z<strong>na</strong>mená zo zdrojov + nezabudnú odstráni ho pomocou ClassWizardu +vymaza telo obslunej funkcie). Ako by malo vyzera okno aplikácie, to vidíte <strong>na</strong>obr. 3.Krok 2. Teraz to u staèí celé iba <strong>na</strong>programova. Kód nájdete <strong>na</strong> webe PC REVUE. S jehopochopením by ste nemali ma problémy (ak predsa budete ma nejaké otázky, staèí mi<strong>na</strong>písa).TIPY NA DOMA. V helpe si pozrite informácie o kolekciách, vhodne doplnia výklad ztejto èasti. Takisto sa s<strong>na</strong>te aspoò sèasti pochopi príklad Collect, uvedený v helpe.Skutoène, aj keï sa to nezdá, kolekcie sú ve¾mi dôleitou súèasou aplikáèného systému.Ich doko<strong>na</strong>lým zvládnutím sa zaradíte medzi „vyšších“ MFC programátorov. A èo je <strong>na</strong>j−dôleitejšie, kolekcie urèite vyuijete pri mnohých projektoch a pomôu vám spreh¾adniváš kód.NABUDÚCE. V <strong>na</strong>sledujúcej èasti sa <strong>na</strong>plno vrhneme <strong>na</strong> serializáciu, opíšeme si, akoju spojazdni v <strong>na</strong>šich projektoch, samozrejme, nezabudneme <strong>na</strong> nejaký ukákový príklad.Take „doèítania“ o mesiac...Marek Šamaj1/2002 PC REVUE 107
P R O G R A M U J E M EDelfinárium / 15. èas: Prehrávanie MP3 súborov II.V tejto èasti seriálu si dokonèíme náš MP3 prehrávaè. elám vám príjemné èítanie.PREDCHÁDZAJÚCA STOPA, ÏALŠIA STOPA. MP3 prehrávaè, ktorýsme pred mesiacom zaèali vyvíja, u dokáe <strong>sk</strong>ladbu spusti, zastavi aj preruši, stálenám však chýbajú tlaèidlá Previous track a Next track. Pridajme teda <strong>na</strong> hlavný formulárdve nové tlaèidlá. Aby nezaberali ve¾a miesta, rozhodol som sa, e <strong>na</strong>miesto textu budúobsahova iba šípky. Po ich stlaèení ktoréhoko¾vek z nich sa odohrá èosi podobné ako pridvojitom kliknutí <strong>na</strong> zoz<strong>na</strong>m <strong>sk</strong>ladieb. Tieto dve rutiny však budú o trošku zloitejšie neobsluná ruti<strong>na</strong> udalosti OnDblClick, pretoe pri „<strong>sk</strong>oku“ <strong>na</strong> <strong>na</strong>stávajúcu èi predošlú<strong>sk</strong>ladbu treba dáva pozor, aby sme, obrazne povedané, nešliapli ved¾a. Èo všetko samôe pri takejto „<strong>sk</strong>okovej“ operácii prihodi, objasòujú <strong>na</strong>sledujúce odseky.Zaènime tlaèidlom Previous track. Kód, ktorý sa po jeho stlaèení vykoná, má takútopodobu:procedure TPlayerWnd.PreviousTrackClick(Sender: TObject);beginwith PlayListWnd dobeginif PlayList.Items.Count=0 then exit;if PlayList.ItemIndex=-1 then exit;if PlayList.ItemIndex=0 then exit;PlayerWnd.PlayMP3(PlayList.Items[PlayList.ItemIndex-1]);PlayList.ItemIndex:=PlayList.ItemIndex-1;end;//withend;Ak chceme prejs <strong>na</strong> ïalšiu <strong>sk</strong>ladbu, musíme <strong>na</strong>j<strong>sk</strong>ôr zisti, èi sa v zoz<strong>na</strong>me <strong>sk</strong>ladiebvôbec <strong>na</strong>chádzajú nejaké súbory. Ak je zoz<strong>na</strong>m prázdny, niet èo prehráva a procedú−ra sa musí ukonèi. Presne to má <strong>na</strong> starosti prvý riadok (<strong>na</strong>sleduje hneï po príkazebegin): ak nie sú v zoz<strong>na</strong>me iadne súbory, vlastnos Items.Count vráti nulu a proce−dúra sa ukonèí.Okrem toho môe <strong>na</strong>sta situácia, e v playlist editore síce súbory budú, ale playlistnebude ma fokus, a teda nijaká poloka nebude vyz<strong>na</strong>èená. V takom prípade nie jemoné zisti èíslo predchádzajúcej èi <strong>na</strong>sledujúcej piesne, a preto sa procedúra musípredèasne ukonèi. Túto kontrolu vykonáva druhý riadok: ak nie je vybraná nijakápoloka, vlastnos ItemIndex vráti hodnotu −1 a procedúra sa ukonèí.Èo však urobíme v prípade, ak pouívate¾ vybral prvú <strong>sk</strong>ladbu a následne stlaèil tlaèid−lo Previous track? Pred prvou <strong>sk</strong>ladbou iad<strong>na</strong> pieseò nie je, a preto niet èo prehráva. Preprvú <strong>sk</strong>ladbu v zoz<strong>na</strong>me je typické, e má vdy èíslo nula, pretoe prvky komponentuTListBox sa èíslujú od nuly. Inými slovami, ak vlastnos ItemIndex vráti nulu, môeme siby absolútne istí v tom, e je vyz<strong>na</strong>èená prvá pesnièka. V tom prípade však nie je moné<strong>sk</strong>oèi <strong>na</strong> predchádzajúcu pieseò (pred prvou <strong>sk</strong>ladbou iad<strong>na</strong> iná nie je), a preto musí byprocedúra ukonèená. Toto všetko zabezpeèí tretí riadok.Štvrtý riadok spustí prehrávanie volaním metódy PlayMP3. Samozrejme, v zoz<strong>na</strong>memusí by vdy vyz<strong>na</strong>èená tá <strong>sk</strong>ladba, ktorá sa práve prehráva. V <strong>na</strong>šom prípade je èíslotejto <strong>sk</strong>ladby o jedno menšie, ako bolo èíslo aktuálnej piesne – stlaèili sme predsa tlaèid−lo Previous track. Hodnota ItemIndex sa preto zmenší o jednotku – zabezpeèí to posled−ný riadok procedúry.Podobné útrapy nás èakajú pri druhej rutine, ktorá má za úlohu zrealizova prechod<strong>na</strong> <strong>na</strong>sledujúcu pesnièku:procedure TPlayerWnd.NextTrackClick(Sender: TObject);beginwith PlayListWnd dobeginif PlayList.Items.Count=0 then exit;if PlayList.ItemIndex=-1 then exit;if PlayList.ItemIndex+1=PlayList.Items.Count then exit;PlayerWnd.PlayMP3(PlayList.Items[PlayList.ItemIndex+1]);if PlayList.ItemIndexPlayList.Items.Count thenPlayList.ItemIndex:=PlayList.ItemIndex+1;end;//withend;Podobne ako v prípade predchádzajúcej procedúry aj teraz môe <strong>na</strong>sta situácia, keïbude zoz<strong>na</strong>m <strong>sk</strong>ladieb prázdny alebo nebude vyz<strong>na</strong>èená ani jed<strong>na</strong> pesnièka. Ak niektorýz týchto stavov <strong>na</strong>stane, prvé dva riadky procedúru ukonèia. Okrem toho sa môe sta,e pri vyvolaní tejto metódy bude vyz<strong>na</strong>èená posledná poloka zoz<strong>na</strong>mu; v takom prí−pade však prechod <strong>na</strong> ïalšiu <strong>sk</strong>ladbu nemá zmysel (za poslednou <strong>sk</strong>ladbou u iad<strong>na</strong><strong>sk</strong>ladba ne<strong>na</strong>sleduje). Presne o to sa stará tretí riadok. Pôsobí síce trochu mätúco, ne−musíte sa však bá, všetko si objasníme.To, èi je vyz<strong>na</strong>èená posledná <strong>sk</strong>ladba, zistíme tak, e budeme <strong>sk</strong>úma, èi sa èíslo právevyz<strong>na</strong>èenej piesne zhoduje s èíslom, ktoré udáva celkový poèet <strong>sk</strong>ladieb uvedených vzoz<strong>na</strong>me. Ak <strong>na</strong>príklad máme v zoz<strong>na</strong>me dve <strong>sk</strong>ladby a vyz<strong>na</strong>èená je druhá, potom budeplati, e èíslo vyz<strong>na</strong>èenej <strong>sk</strong>ladby sa rovná celkovému poètu piesní, a teda sme si istí, evyz<strong>na</strong>èená pesnièka je posledná. Je tu však malý problém, ktorý súvisí s èíslovanímpouívaným komponentom TListBox: èíslovanie sa zaèí<strong>na</strong> od nuly. Pokia¾ máme v zoz<strong>na</strong>−me len jednu pesnièku, tá má èíslo nula. Celkový poèet piesní v zoz<strong>na</strong>me je však 1. Èo stým? Riešenie je ve¾mi jednoduché: ak k vlastnosti ItemIndex prirátame jednotku, prejde−me tým vlastne <strong>na</strong> normálny systém èíslovania: prvá <strong>sk</strong>ladba s èíslom 0 bude ma èíslo 1(0 + 1 = 1), druhá <strong>sk</strong>ladba s èíslom 1 bude ma odteraz èíslo 2 (1 + 1 = 2) atï. Situáciasa teda zmenila tak, e v zoz<strong>na</strong>me máme jednu pieseò s èíslom 0. 0 + 1 sa rovná jed−nej, èo je vlastne celkový poèet <strong>sk</strong>ladieb v zoz<strong>na</strong>me. Ak budú piesne dve a vyz<strong>na</strong>èímedruhú (ktorá má èíslo 1), potom bude opä plati, e prirátaním jednotky k èíslu piesne(1 + 1) dostaneme jej <strong>sk</strong>utoèné èíslo, ktoré sa zároveò rovná poètu pesnièiek obsiahnu−tých v zoz<strong>na</strong>me. Teraz si môeme by istí, e vyz<strong>na</strong>èená pesnièka je poslednou piesòou,a teda ïalšiu u vyz<strong>na</strong>èi nemono, preto sa procedúra ukonèí.Nasleduje volanie metódy PlayMP3. Jediným rozdielom oproti predošlej procedúre jeto, e v predchádzajúcom prípade sme jednotku odèítali (keïe sme potrebovali prechod<strong>na</strong> predošlú <strong>sk</strong>ladbu) a teraz jednotku pripoèítame (pretoe potrebujeme prejs <strong>na</strong> <strong>na</strong>sle−dujúcu <strong>sk</strong>ladbu).Program je teda opä o nieèo vyspelejší, môete sa s ním chví¾ku pohra. Má však eštejednu pomerne nepríjemnú chybu: <strong>sk</strong>úste spusti ¾ubovo¾nú <strong>sk</strong>ladbu, preruši ju (tlaèidloPauza) a následne stlaèi tlaèidlo Next track alebo Previous track. Všetko prebehne korekt−ne okrem jednej malièkosti: tlaèidlo Pauza ešte stále zobrazuje text Pokraèova, pretoenevie, e došlo k prechodu <strong>na</strong> novú pieseò. Musíme preto upravi rutinu PlayMP3 tak,aby vdy po spustení novej piesne priradila tomuto tlaèidlu správny text. Nebudem uvá−dza celý výpis procedúry, aby som neplytval miestom; <strong>na</strong> orientáciu vám postaèí nieko¾koz jej posledných riadkov:Play;Pauza.E<strong>na</strong>bled:=true;Pauza.Caption:= Pauza ;end;//withTeraz si mono poviete, e prehráva <strong>sk</strong>ladby je síce pekná vec, program však ešte stálenezobrazuje informácie uloené v ID3 tagu. Buïte bez obáv, aj k týmto údajom sa bezproblémov dostaneme. Aby to však bolo moné, musíme do klauzuly uses nášho prog−ramu prida jednotku MPEGDefs. Informácie, ktoré môeme zo súboru MP3 vyextraho−va, sú dvojakého druhu: do prvej <strong>sk</strong>upiny patria technické údaje o frekvencii, bitrate, ste−reoreime atï. Do druhej <strong>sk</strong>upiny patria údaje zí<strong>sk</strong>ané z ID3 tagu.Informácie o <strong>sk</strong>ladbe by sa mali zobrazi bezprostredne po zaèatí jej prehrávania,preto budeme musie upravi procedúru PlayMP3. Naj<strong>sk</strong>ôr je však potrebné prida kom−ponenty, ktoré nám tieto údaje zobrazia. Technické údaje o <strong>sk</strong>ladbe bude zobrazovakomponent TLabel <strong>na</strong>zvaný Tech, zatia¾ èo názov piesne a interpreta budú uloené vkomponente TLabel <strong>na</strong>zvanom Nazov (ostatné informácie, ako <strong>na</strong>pr. album èi rok vyda−nia, nám zobrazí špeciálne okno). Aby program nevyzeral prive¾mi všedne, rozhodolsom sa poui trik, ktorý som kedysi pouil pri vytváraní CD prehrávaèa a audioprehrá−vaèa (PC REVUE è. 1/2000): komponenty TLabel sú lté a umiestnené <strong>na</strong> èiernom kom−ponente TPanel.Ako som u spomenul, aby sme mohli uvedené informácie zobrazi, bude potrebnémodifikova procedúru PlayMP3. Skrátenú verziu tejto procedúry nájdete <strong>na</strong> internetovejstránke PC REVUE. Aby som neodvádzal pozornos od funkcií pracujúcich s ID3 tagom, zprocedúry sú vynechané všetky príkazy súvisiace s prehrávaním.Aby procedúra PlayMP3 fungovala, musíme do programu prida <strong>na</strong>sledujúcu deklaráciu:varPlayerWnd: TPlayerWnd;const stereomodes : array[TElSStereoMode] of string = ( Stereo , JointStereo , Dual Channel , Mono );Na èo je tento zápis dobrý, to si povieme ne<strong>sk</strong>ôr.Keï si pozorne preštudujete procedúru PlayMP3, zrejme vás zarazia tieto riadky:Player.GetInfo1(nil, BufLength);Inc(BufLength);GetMem(TechInfo, BufLength);GetInfo1(TechInfo,BufLength);Ak ste èítali dokumentáciu, u urèite viete, e technické údaje o súbore MP3 vraciafunkcia GetInfo1. To by samo osebe neprekáalo, ale nikde sa nepíše, e túto funkciu jepotrebné vola dvakrát (ja som sa to tie dozvedel a po pre<strong>sk</strong>úmaní ukákového prog−ramu, dodávaného s komponentom). Môj osobný odhad je taký, e prvé volanie funkcieslúi <strong>na</strong> zistenie mnostva pamäte potrebnej <strong>na</strong> alokovanie buffera. Zistená ve¾kos sa108 PC REVUE 1/2002