From pouchintv-svn at baysse.fr Sat Nov 17 18:57:47 2007 From: pouchintv-svn at baysse.fr (pouchintv-svn at baysse.fr) Date: Sat, 17 Nov 2007 18:57:47 +0100 (CET) Subject: [Pouchintv-dev] [PouchinTVMod] gingko | r101 - trunk Message-ID: <20071117175747.7A5FD5F2E9@mail.baysse.fr> Author: gingko Date: 2007-11-17 18:57:47 +0100 (Sat, 17 Nov 2007) New Revision: 101 Modified: trunk/channels.cpp trunk/channels.h trunk/epg.cpp trunk/epgfilter.cpp trunk/ini.cpp trunk/ini.h trunk/main.cpp trunk/parse.cpp trunk/recprog.cpp trunk/res.rc trunk/resource.h trunk/settings.cpp trunk/xml.cpp trunk/xml.h Log: Mise ?\195?\160 jour de transition : S?\195?\169curisation du membre "getStr" de la classe "CXMLNode", ainsi que surcharge de celui-ci pour lui permettre de g?\195?\169rer indiff?\195?\169remment les cha?\195?\174nes de caract?\195?\168res 8 ou 16 bits. Optimisation des boucles bas?\195?\169es sur la table des cha?\195?\174nes. Divers am?\195?\169nagements pr?\195?\169paratoires ?\195?\160 la mise en place du dialogue de r?\195?\169organisation des cha?\195?\174nes qui va bient?\195?\180t suivre. Modified: trunk/channels.cpp =================================================================== --- trunk/channels.cpp 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/channels.cpp 2007-11-17 17:57:47 UTC (rev 101) @@ -60,6 +60,20 @@ return hImage; } + +char ChainePMT::norme(void) const +{ + return video_pid!=0 ? + (mpeg4_pid!=0 ? 'X': '2') : + (mpeg4_pid!=0 ? '4': '?'); +} + +// Constructeur : +Chaine::Chaine() +{ + memset(this, 0, sizeof(*this)); +} + HBITMAP Chaine::ChargeIcone() { if (hImage==INVALID_HANDLE_VALUE) @@ -96,9 +110,9 @@ **/ int trouve_chaine_par_no(WORD numChaine) { - int nbchaines = (int)Canaux.size(); + int nbChaines = (int)Canaux.size(); - for (int i=0; istr != NULL && wcscmp(lpStr, assocTable->str) != 0) + assocTable++; + + if (assocTable->str==NULL) + return bydefault; + return assocTable->bin; +} + +/** + * Trouver la chaîne de caractères associée à une valeur binaire fournie, + * selon la table 'assocTable'. Si la valeur binaire ne se trouve pas dans la table, + * alors un pointeur sur une chaîne vide est retourné. + **/ +LPCWSTR dword2str(const AssocElement * assocTable, DWORD value) +{ + while (assocTable->str != NULL && assocTable->bin != value) + assocTable++; + + LPCWSTR str = assocTable->str; + + if (str==NULL) + str = L"??"; + return str; +} + BOOL save_config_int(LPCWSTR lpKeyName, LONG value); // forward void lit_fichier_scan(wchar_t * ville) @@ -305,7 +337,7 @@ Programme prog; memset(&prog, 0, sizeof(Programme)); - pNode->getStr(L"Nom", (char*)prog.nom); + pNode->getStr(L"Nom", prog.nom, _countof(prog.nom)); prog.numeroChaine = (WORD)pNode->getInt(L"Chaine"); prog.sidChaine = (WORD)pNode->getInt(L"ServiceID"); @@ -453,6 +485,7 @@ } CXMLElement root = xml.getDocElement(); + wchar_t var[16]; std::vector children = root.getChildren(); @@ -463,9 +496,9 @@ Chaine canal; memset(&canal, 0, sizeof(Chaine)); - pNode->getStr(L"Nom", canal.nom); + pNode->getStr(L"Nom", canal.nom, _countof(canal.nom)); canal.canal_no = (WORD)pNode->getInt(L"Canal"); - pNode->getStr(L"Groupe", canal.groupe); + pNode->getStr(L"Groupe", canal.groupe, _countof(canal.groupe)); canal.frequence = pNode->getInt(L"Frequence"); @@ -473,7 +506,11 @@ canal.TSID = (WORD)pNode->getInt(L"TSID"); canal.SID = (WORD)pNode->getInt(L"SID"); + canal.numero_nit = (WORD)pNode->getInt(L"Numero_initial"); canal.numeroChaine = (WORD)pNode->getInt(L"Numero"); + if (canal.numero_nit==0) + canal.numero_nit = canal.numeroChaine; // pour récupération depuis anciennes versions + canal.pmt_pid = (WORD)pNode->getInt(L"PMT"); canal.pcr_pid = (WORD)pNode->getInt(L"PCR"); @@ -481,24 +518,26 @@ canal.mpeg4_pid = (WORD)pNode->getInt(L"Video_mpeg4"); canal.nb_son = (WORD)pNode->getInt(L"Nb_Son"); for (int i=0;igetInt(var); - canal.son[i].type = (BYTE)pNode->getInt(var2); - pNode->getStr(var3, canal.son[i].lang); + canal_son.pid = (WORD)pNode->getInt(var); + + swprintf_s(var, _countof(var), L"Son%i_Type", i); + canal_son.type = (BYTE)pNode->getInt(var); + + swprintf_s(var, _countof(var), L"Son%i_Lang", i); + pNode->getStr(var, canal_son.lang, _countof(canal_son.lang)); } canal.nb_autre = (WORD)pNode->getInt(L"Nb_Autre"); for (int i=0;igetInt(var); - pNode->getStr(var2, canal.autre[i].lang); + canal_autre.pid = (WORD)pNode->getInt(var); + + swprintf_s(var, _countof(var), L"Autre%i_Lang", i); + pNode->getStr(var, canal_autre.lang, _countof(canal_autre.lang)); } Canaux.push_back(canal); @@ -540,6 +579,7 @@ fprintf(fop, "\t\t%i\n", canal.TSID); fprintf(fop, "\t\t%i\n", canal.SID); + fprintf(fop, "\t\t%i\n", canal.numero_nit); fprintf(fop, "\t\t%i\n", canal.numeroChaine); fprintf(fop, "\t\t%i\n", canal.pmt_pid); fprintf(fop, "\t\t%i\n", canal.pcr_pid); @@ -579,13 +619,6 @@ fclose(fop); } - -struct AssocElement -{ - LPCWSTR str; - DWORD bin; -}; - AssocElement aBoolTable[] = { {L"Faux", 0}, @@ -632,14 +665,7 @@ // Écriture de la chaîne de caractères associée à une valeur fournie : BOOL save_config_assoc(LPCWSTR lpKeyName, const AssocElement * assocTable, DWORD value) { - while (assocTable->str != NULL && assocTable->bin != value) - assocTable++; - - LPCWSTR str = assocTable->str; - - if (str==NULL) - str = L"??"; - return save_config_str(lpKeyName, str); + return save_config_str(lpKeyName, dword2str(assocTable, value)); } BOOL save_config_bool(LPCWSTR lpKeyName, bool value) @@ -708,13 +734,7 @@ wchar_t str[32]; load_config_str(lpKeyName, str, _countof(str)); - - while (assocTable->str != NULL && wcscmp(str, assocTable->str) != 0) - assocTable++; - - if (assocTable->str==NULL) - return bydefault; - return assocTable->bin; + return str2dword(assocTable, str, bydefault); } bool load_config_bool(LPCWSTR lpKeyName, bool bydefault=false) Modified: trunk/ini.h =================================================================== --- trunk/ini.h 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/ini.h 2007-11-17 17:57:47 UTC (rev 101) @@ -90,3 +90,28 @@ // Création : HANDLE Create() const; }; + +/** + * Élement de table d'association chaîne <--> valeur binaire. + * Cette structure est destinée à faire partie d'une table dont le dernier élément + * doit avoir ses deux valeurs à NULL et 0 respectivement. + **/ +struct AssocElement +{ + LPCWSTR str; + DWORD bin; +}; + +/** + * Trouver la valeur binaire associée à une chaîne de caractères fournie, + * selon la table 'assocTable'. Si la chaîne ne se trouve pas dans la table, + * alors la valeur 'bydefault' est retournée. + **/ +DWORD str2dword(const AssocElement * assocTable, LPCWSTR lpStr, DWORD bydefault); + +/** + * Trouver la chaîne de caractères associée à une valeur binaire fournie, + * selon la table 'assocTable'. Si la valeur binaire ne se trouve pas dans la table, + * alors un pointeur sur une chaîne vide est retourné. + **/ +LPCWSTR dword2str(const AssocElement * assocTable, DWORD value); \ No newline at end of file Modified: trunk/main.cpp =================================================================== --- trunk/main.cpp 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/main.cpp 2007-11-17 17:57:47 UTC (rev 101) @@ -516,13 +516,14 @@ HMENU chaines = CreatePopupMenu(); bool _recording = recording(); + int nbChaines = (int)Canaux.size(); - for (size_t i=0; i=0; i--) { - if (Canaux[i].frequence == Canaux[ixChaineCourante].frequence) { - zappe_index(i); - return; - } - } + for (int i = ixChaineCourante+pas; i!=ixChaineCourante; i = (i+nbChaines+pas)%nbChaines) { + const Chaine & canal = Canaux[i]; - for (int i = (int)Canaux.size() -1; i>ixChaineCourante; i--) { - if (Canaux[i].frequence == Canaux[ixChaineCourante].frequence) { - zappe_index(i); - return; - } + if (enregistre && canal.frequence != freq_courante) { + // Si enregistrement, la chaîne doit avoir la même fréquence + continue; } + zappe_index(i); + break; } } -static void chaine_suivante() -{ - if (!recording()) { - int nouvelle = ixChaineCourante + 1; - if (nouvelle >= (int)Canaux.size()) nouvelle=0; - zappe_index(nouvelle); - } else { - // faut trouver une chaîne après avec la même fréq - - for (int i = ixChaineCourante + 1; i < (int)Canaux.size(); i++) { - if (Canaux[i].frequence == Canaux[ixChaineCourante].frequence) { - zappe_index(i); - return; - } - } - - for (int i = 0; igetStr(L"type", version); + pNode->getStr(L"type", version, _countof(version)); if (strcmp(version, "beta") == 0) versTemp = β else if (strcmp(version, "stable") == 0) versTemp = &release; - pNode->getStr(L"type", versTemp->type); - pNode->getStr(L"rev", versTemp->rev); + pNode->getStr(L"type", versTemp->type, _countof(versTemp->type)); + pNode->getStr(L"rev", versTemp->rev, _countof(versTemp->rev)); versTemp->major = pNode->getInt(L"major"); versTemp->minor = pNode->getInt(L"minor"); @@ -2245,11 +2214,11 @@ switch (wParam) { case VK_UP: - chaine_precedente(); + chaine_avance(-1); break; case VK_DOWN: - chaine_suivante(); + chaine_avance(1); break; case '0': @@ -2285,9 +2254,9 @@ short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); if (zDelta >= WHEEL_DELTA) { - chaine_precedente(); + chaine_avance(-1); } else if (zDelta <= -WHEEL_DELTA) { - chaine_suivante(); + chaine_avance(1); } break; } @@ -2647,14 +2616,14 @@ if (!do_init_vmr()) return -1; - int nbr_chaines = Canaux.size(); + int nbChaines = (int)Canaux.size(); - if (ixChaineCourante>=0 && ixChaineCourante=0 && ixChaineCourantebegin(); const BYTE * pdata = psdesc->begin(); - memset(&canal, 0, sizeof(Chaine)); pdata = psdesc->get_str(pdata, canal.groupe); pdata = psdesc->get_str(pdata, canal.nom); canal.canal_no=freq.canal_no; @@ -585,7 +584,7 @@ LPBYTE(plchent)end(); plchent++ ) { chIDs.SID = plchent->sid(); - chIDs.numeroChaine = plchent->logical_channel_number(); + chIDs.numero_nit = plchent->logical_channel_number(); //myprintf(L"parse_nit: ONID=%i, TSID=%i, SID=%i, numero=%i\n", // chIDs.ONID, chIDs.TSID, chIDs.SID, chIDs.numeroChaine); chaineIDs.push_back(chIDs); @@ -750,7 +749,7 @@ for(std::vector::iterator itt=chaineIDs.begin(); itt!=chaineIDs.end(); itt++) { if (canal.ONID==itt->ONID && canal.TSID==itt->TSID && canal.SID==itt->SID) - canal.numeroChaine = itt->numeroChaine; + canal.numeroChaine = canal.numero_nit = itt->numero_nit; } } } Modified: trunk/recprog.cpp =================================================================== --- trunk/recprog.cpp 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/recprog.cpp 2007-11-17 17:57:47 UTC (rev 101) @@ -602,9 +602,9 @@ SetCtlTimeDate(hwndDlg, IDC_DATE_START, IDC_TIME_START, time); SetCtlTimeDate(hwndDlg, IDC_DATE_END, IDC_TIME_END, time); - int nbchaines = (int)Canaux.size(); + int nbChaines = (int)Canaux.size(); - for (i=0; i -#define _cmd_ MAKELONG // -> pour tenter de rendre les choses plus lisibles +#define _cmd_ MAKEWPARAM // -> pour tenter de rendre les choses plus lisibles // Passé via lParam aux fonction "callback" des dialogues pour leur permettre de différencier // entre le mode "property sheet" et le mode dialogue normal (ceci permet d'utiliser, dans @@ -44,6 +45,17 @@ #define DIALOGMODE ((LPARAM)FALSE) #define PROPSHEETMODE ((LPARAM)TRUE) +LRESULT set_check(HWND hDlg, int nID, bool state) +{ + return SendDlgItemMessage(hDlg, nID, + BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0); +} + +bool get_check(HWND hDlg, int nID) +{ + return SendDlgItemMessage(hDlg, nID, BM_GETCHECK, 0, 0)==BST_CHECKED; +} + // Assistance au retour de valeur après notification dans une feuille de propriétés : BOOL psn_result(HWND hDlg, LONG res) { @@ -325,7 +337,7 @@ const Frequence & freq = freq_scan[wParam]; wchar_t szFreq[16]; wchar_t szTemp[128]; - unsigned last_num = Canaux.size(); + int last_num = Canaux.size(); swprintf_s(szFreq, _countof(szFreq), L"%03u,%03u MHz", freq.mhz/1000, freq.mhz%1000); swprintf_s(szTemp, _countof(szTemp), L"Examen canal %u à %s", freq.canal_no, szFreq); @@ -334,16 +346,15 @@ tune(hDlg, freq, &stopping); if (!stopping) { + int nbChaines = (int)Canaux.size(); + // Affichage des canaux qui viennent d'être ajoutés : - for (; last_num < Canaux.size(); last_num++) { + for (; last_num 0) _itoa_s(canal.numeroChaine, strnum, _countof(strnum), 10); @@ -415,19 +426,8 @@ return FALSE; } -LRESULT set_check(HWND hDlg, int nID, bool state) +static INT_PTR CALLBACK OptionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - return SendDlgItemMessage(hDlg, nID, - BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0); -} - -bool get_check(HWND hDlg, int nID) -{ - return SendDlgItemMessage(hDlg, nID, BM_GETCHECK, 0, 0)==BST_CHECKED; -} - -static INT_PTR CALLBACK OptionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch (uMsg) { case WM_INITDIALOG: Modified: trunk/xml.cpp =================================================================== --- trunk/xml.cpp 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/xml.cpp 2007-11-17 17:57:47 UTC (rev 101) @@ -119,7 +119,7 @@ return rep; } -void CXMLNode::getStr(const wchar_t * nom, char * tab) +void CXMLNode::getStr(const wchar_t * nom, wchar_t * str, size_t size) { bool found = false; IXMLDOMNodeList * pChildList = NULL; @@ -142,7 +142,7 @@ found = true; BSTR txt; pNode->get_text(&txt); - WideCharToMultiByte(CP_ACP, 0, txt, -1, tab, 256, NULL, NULL); + wcscpy_s(str, size, txt); SysFreeString(txt); } SysFreeString(bstr); @@ -152,6 +152,14 @@ } } +void CXMLNode::getStr(const wchar_t * nom, char * str, size_t size) +{ + wchar_t buffer[64]; + + getStr(nom, buffer, _countof(buffer)); + WideCharToMultiByte(CP_ACP, 0, buffer, -1, str, size, NULL, NULL); +} + /** * Récupère des enfants à partir d'un nom **/ Modified: trunk/xml.h =================================================================== --- trunk/xml.h 2007-10-29 06:45:34 UTC (rev 100) +++ trunk/xml.h 2007-11-17 17:57:47 UTC (rev 101) @@ -41,7 +41,8 @@ int getInt(const wchar_t * nom); - void getStr(const wchar_t * nom, char * tab); + void getStr(const wchar_t * nom, char * str, size_t size); + void getStr(const wchar_t * nom, wchar_t * str, size_t size); void getChildrenByName(const wchar_t *nom, CXMLNode **noeud); }; From pouchintv-svn at baysse.fr Sat Nov 17 22:25:08 2007 From: pouchintv-svn at baysse.fr (pouchintv-svn at baysse.fr) Date: Sat, 17 Nov 2007 22:25:08 +0100 (CET) Subject: [Pouchintv-dev] [PouchinTVMod] gingko | r102 - trunk Message-ID: <20071117212508.1AC9C5F2E9@mail.baysse.fr> Author: gingko Date: 2007-11-17 22:25:07 +0100 (Sat, 17 Nov 2007) New Revision: 102 Modified: trunk/channels.cpp trunk/channels.h trunk/ini.cpp trunk/ini.h trunk/main.cpp trunk/res.rc trunk/resource.h trunk/settings.cpp Log: Impl?\195?\169mentation du dialogue de r?\195?\169organisation des cha?\195?\174nes, en tant que page suppl?\195?\169mentaire dans le dialogue de configuration. Les cha?\195?\174nes peuvent ?\195?\170tre d?\195?\169plac?\195?\169es dans la liste par glisser-d?\195?\169poser (plusieurs cha?\195?\174nes peuvent ?\195?\170tre d?\195?\169plac?\195?\169es dans le m?\195?\170me mouvement, ?\195?\160 partir d'une s?\195?\169lection multiple). Le num?\195?\169ro de chaque cha?\195?\174ne peut ?\195?\170tre chang?\195?\169 (cliquer sur le num?\195?\169ro pour ouvrir une zone d'?\195?\169dition). Les cha?\195?\174nes peuvent ?\195?\170tre d?\195?\169clar?\195?\169es 'actives', 'inactives' ou 'pr?\195?\169f?\195?\169r?\195?\169es', gr?\195?\162ce ?\195?\160 un menu contextuel (les cha?\195?\174nes 'pr?\195?\169f?\195?\169r?\195?\169es' peuvent ?\195?\170tre s?\195?\169lectionn?\195?\169es par fl?\195?\168ches haut et bas, molette de souris ou par menu, alors que les cha?\195?\174nes "actives" ne peuvent ?\195?\170tre s?\195?\169lectionn?\195?\169es que par menu). On peut trier les cha?\195?\174nes selon diff?\195?\169rents crit?\195?\168res en cliquant sur les en-t?\195?\170tes de colonnes, alternativement en ordre ascendant ou descendant. On peut renum?\195?\169roter les cha?\195?\174nes, soit s?\195?\169quentiellement, soit par restauration du num?\195?\169ro initial, gr?\195?\162ce ?\195?\160 un bouton appropri?\195?\169. La fonction de recherche des cha?\195?\174nes est retir?\195?\169e du menu. Elle est maintenant lanc?\195?\169e avec un bouton dans le dialogue de configuration des cha?\195?\174nes. Autre ajout : En maintenant la touche SHIFT press?\195?\169e au d?\195?\169marrage de l'application, on forcera l'ouverture du dialogue de configuration (utile dans le cas o?\195?\185 la configuration courante fait planter l'application avant m?\195?\170me d'avoir acc?\195?\168s aux menus). Note : On peut d?\195?\169finir une macro "#define MODIFY_CHANNELS_BY_CONTROLS" (id?\195?\169alement au d?\195?\169but de "resource.h", car le fichier de ressources "res.rc" est concern?\195?\169), qui activera un autre mode d'?\195?\169dition des cha?\195?\174nes, avec des items suppl?\195?\169mentaires dans le dialogue. Il s'agit d'un essai d'interface utilisateur que je n'ai finalement pas retenu, mais que j'ai laiss?\195?\169 ainsi provisoirement. Modified: trunk/channels.cpp =================================================================== --- trunk/channels.cpp 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/channels.cpp 2007-11-17 21:25:07 UTC (rev 102) @@ -72,6 +72,7 @@ Chaine::Chaine() { memset(this, 0, sizeof(*this)); + etat=ec_preferee; } HBITMAP Chaine::ChargeIcone() @@ -183,6 +184,8 @@ * Attribution d'un numéro à toutes les chaînes qui n'en ont pas reçu. * Si les chaînes ont été triées auparavant, ce numéro sera attribué selon l'ordre * alphabétique de leur nom. + * Au passage, marque également comme "inactives" les chaînes qui ne diffusent pas + * couramment en norme MPEG2. **/ void attribue_numeros() { @@ -193,6 +196,9 @@ if (canal.numeroChaine==0) canal.numeroChaine = nouveau_numero_chaine(); + + if (canal.norme()!='2') + canal.etat = ec_inactive; } } Modified: trunk/channels.h =================================================================== --- trunk/channels.h 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/channels.h 2007-11-17 21:25:07 UTC (rev 102) @@ -69,12 +69,19 @@ WORD numero_nit;// Numéro de chaîne original }; +enum EtatChaine { + ec_inactive, // Chaîne inactive (non sélectionnable) + ec_active, // Chaîne active (sélectionnable par menu et numéro seulement) + ec_preferee // Chaîne active (sélectionnable aussi par flèches et roulette souris) +}; + struct Chaine : public ChainePMT, public ChaineIDs { char nom[64]; char groupe[64]; WORD canal_no; long frequence; WORD pmt_pid; + EtatChaine etat; WORD numeroChaine;// Numéro de chaîne éventuellement modifié par l'utilisateur Emission emis[2]; private: @@ -146,5 +153,7 @@ * Attribution d'un numéro à toutes les chaînes qui n'en ont pas reçu. * Si les chaînes ont été triées auparavant, ce numéro sera attribué selon l'ordre * alphabétique de leur nom. + * Au passage, marque également comme "inactives" les chaînes qui ne diffusent pas + * couramment en norme MPEG2. **/ void attribue_numeros(); Modified: trunk/ini.cpp =================================================================== --- trunk/ini.cpp 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/ini.cpp 2007-11-17 21:25:07 UTC (rev 102) @@ -467,6 +467,14 @@ fclose(fop); } +AssocElement aChStateTable[] = +{ + {L"Inactive", ec_inactive}, + {L"Active", ec_active}, + {L"Préférée", ec_preferee}, + {NULL, 0} // -> terminateur +}; + /** * Chargement du fichier des chaînes "chaines.xml". * Retourne 0 en cas de succès, 1 si erreur. @@ -511,6 +519,9 @@ if (canal.numero_nit==0) canal.numero_nit = canal.numeroChaine; // pour récupération depuis anciennes versions + pNode->getStr(L"Etat", var, _countof(var)); + canal.etat = (EtatChaine)str2dword(aChStateTable, var, ec_preferee); + canal.pmt_pid = (WORD)pNode->getInt(L"PMT"); canal.pcr_pid = (WORD)pNode->getInt(L"PCR"); @@ -581,6 +592,7 @@ fprintf(fop, "\t\t%i\n", canal.numero_nit); fprintf(fop, "\t\t%i\n", canal.numeroChaine); + fprintf(fop, "\t\t%S\n", dword2str(aChStateTable, canal.etat)); fprintf(fop, "\t\t%i\n", canal.pmt_pid); fprintf(fop, "\t\t%i\n", canal.pcr_pid); Modified: trunk/ini.h =================================================================== --- trunk/ini.h 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/ini.h 2007-11-17 21:25:07 UTC (rev 102) @@ -114,4 +114,9 @@ * selon la table 'assocTable'. Si la valeur binaire ne se trouve pas dans la table, * alors un pointeur sur une chaîne vide est retourné. **/ -LPCWSTR dword2str(const AssocElement * assocTable, DWORD value); \ No newline at end of file +LPCWSTR dword2str(const AssocElement * assocTable, DWORD value); + +/** + * Table d'association pour l'état des chaînes. + **/ +extern AssocElement aChStateTable[]; Modified: trunk/main.cpp =================================================================== --- trunk/main.cpp 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/main.cpp 2007-11-17 21:25:07 UTC (rev 102) @@ -521,6 +521,10 @@ for (int i=0; i=0; do { if (!config_ok) { Modified: trunk/res.rc =================================================================== --- trunk/res.rc 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/res.rc 2007-11-17 21:25:07 UTC (rev 102) @@ -67,7 +67,7 @@ POPUP "&Fichier" BEGIN MENUITEM "&Configuration...", IDM_CONFIG - MENUITEM "&Recherche de chaînes...", IDM_CHANNELS + //MENUITEM "&Recherche de chaînes...", IDM_CHANNELS MENUITEM SEPARATOR MENUITEM "&Quitter", IDM_QUIT END @@ -216,10 +216,10 @@ RTEXT "Ville :",IDC_STATIC,64,7,24,12,SS_CENTERIMAGE COMBOBOX IDC_COMBO_VILLE,92,7,190,99,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Chaînes trouvées :",IDC_STATIC,7,18,79,10 - LTEXT "Canal",IDC_STATIC,10,28,19,8 - LTEXT "Fréquence",IDC_STATIC,32,28,35,8 - LTEXT "Groupe",IDC_STATIC,87,28,24,8 - LTEXT "N°",IDC_STATIC,138,28,9,8 + LTEXT "N°",IDC_STATIC,10,28,9,8 + LTEXT "Canal",IDC_STATIC,26,28,19,8 + LTEXT "Fréquence",IDC_STATIC,48,28,35,8 + LTEXT "Groupe",IDC_STATIC,104,28,24,8 LTEXT "Nom de la chaîne",IDC_STATIC,154,28,58,8 LTEXT "Mpeg",IDC_STATIC,253,28,18,8 LISTBOX IDC_CHANNEL_LIST,7,36,276,128,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP @@ -334,7 +334,40 @@ PUSHBUTTON "Annuler",IDCANCEL,78,54,54,14 END +IDD_CHANNELS DIALOGEX 0, 0, 290, 214 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Chaînes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +#if defined(MODIFY_CHANNELS_BY_CONTROLS) + CONTROL "",IDC_CHANNEL_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_AUTOARRANGE | LVS_EDITLABELS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,275,158 + GROUPBOX "Modifier :",IDC_STATIC_MODIFY,7,167,275,25,WS_DISABLED + RTEXT "N°",IDC_STATIC_NUMBER,8,179,12,8,WS_DISABLED + EDITTEXT IDC_NUMBER,23,176,30,14,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER | WS_DISABLED + CONTROL "Active",IDC_ACTIVE,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,169,178,33,10 + CONTROL "Préférée",IDC_PREFERRED,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,235,178,40,10 +#else + CONTROL "",IDC_CHANNEL_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_AUTOARRANGE | LVS_EDITLABELS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,275,182 +#endif + PUSHBUTTON "Renuméroter ...",IDC_RENUMBER,7,193,59,14 + PUSHBUTTON "Recherche de chaînes ...",IDC_SCAN,169,193,113,14,BS_MULTILINE +END +IDD_RENUMBER DIALOGEX 0, 0, 187, 70 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Renumérotation des chaînes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Méthode :",IDC_STATIC,7,7,173,39 + CONTROL "Séquentiellement, dans l'ordre actuel de la liste",IDC_RENUM_SEQ, + "Button",BS_AUTORADIOBUTTON | BS_NOTIFY,10,18,164,10 + CONTROL "Restauration des numéros initialement attribués",IDC_RENUM_REST, + "Button",BS_AUTORADIOBUTTON,10,30,165,10 + DEFPUSHBUTTON "OK",IDOK,7,49,50,14 + PUSHBUTTON "Annuler",IDCANCEL,130,49,50,14 +END + + ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO @@ -429,6 +462,22 @@ TOPMARGIN, 7 BOTTOMMARGIN, 68 END + + IDD_CHANNELS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 282 + TOPMARGIN, 7 + BOTTOMMARGIN, 207 + END + + IDD_RENUMBER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 180 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END END #endif // APSTUDIO_INVOKED Modified: trunk/resource.h =================================================================== --- trunk/resource.h 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/resource.h 2007-11-17 21:25:07 UTC (rev 102) @@ -21,6 +21,8 @@ #define IDD_PASWD 128 #define IDD_DELAYED_STOP 129 #define IDD_CLOSE_CONFIRM 130 +#define IDD_CHANNELS 131 +#define IDD_RENUMBER 132 #define IDC_COMBO_VILLE 1001 #define IDC_COMBO_MPEG2 1002 #define IDC_COMBO_AUDIO 1003 @@ -84,6 +86,17 @@ #define IDC_REP_VENDREDI 1105 #define IDC_REP_SAMEDI 1106 +#define IDC_SCAN 1107 +#if defined(MODIFY_CHANNELS_BY_CONTROLS) + #define IDC_STATIC_MODIFY 1108 + #define IDC_ACTIVE 1109 + #define IDC_PREFERRED 1110 + #define IDC_STATIC_NUMBER 1111 + #define IDC_NUMBER 1112 +#endif +#define IDC_RENUMBER 1113 +#define IDC_RENUM_SEQ 1114 +#define IDC_RENUM_REST 1115 #define MY_TRAY_ICON_ID 20001 #define MY_TRAY_ICON_MESSAGE 20002 #define IDM_QUIT 40001 @@ -96,7 +109,8 @@ #define IDM_DEMUX 40008 #define IDM_AC3 40009 #define IDM_DSOUNDAC3 40010 -#define IDM_CHANNELS 40011 +//#define IDM_CHANNELS 40011 + #if defined(USE_RECORD_STREAM) #define IDM_RECORD_STREAM 40012 #endif @@ -111,6 +125,12 @@ #define IDM_MINIMIZE 40021 #define IDM_RESTORE 40022 #define IDM_ALWAYS_ON_TOP 40029 + +#define IDM_CHAN_STATE_BASE 40030 // Offset de base pour les trois suivants +#define IDM_CHAN_INACTIVE 40030 +#define IDM_CHAN_ACTIVE 40031 +#define IDM_CHAN_PREFERRED 40032 + #define IDM_REALTIME_PRIORITY 40034 #define IDM_HIGH_PRIORITY 40035 #define IDM_ABOVE_NORMAL_PRIORITY 40036 @@ -139,9 +159,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_RESOURCE_VALUE 133 #define _APS_NEXT_COMMAND_VALUE 40109 -#define _APS_NEXT_CONTROL_VALUE 1107 +#define _APS_NEXT_CONTROL_VALUE 1116 #define _APS_NEXT_SYMED_VALUE 121 #endif #endif Modified: trunk/settings.cpp =================================================================== --- trunk/settings.cpp 2007-11-17 17:57:47 UTC (rev 101) +++ trunk/settings.cpp 2007-11-17 21:25:07 UTC (rev 102) @@ -212,7 +212,7 @@ static INT_PTR CALLBACK ScanDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - static INT a_tabs[5] = {24, 79, 130, 146, 245}; // Positions des tabulations dans la list box + static INT a_tabs[5] = {24, 40, 95, 146, 245}; // Positions des tabulations dans la list box static enum { ss_idle, ss_cancelling, @@ -358,8 +358,8 @@ if (canal.numeroChaine > 0) _itoa_s(canal.numeroChaine, strnum, _countof(strnum), 10); - swprintf_s(szTemp, _countof(szTemp), L"%u\t%s\t%S\t%S\t%S\t%c", canal.canal_no, szFreq, - *canal.groupe ? canal.groupe : "", strnum, canal.nom, norme); + swprintf_s(szTemp, _countof(szTemp), L"%S\t%u\t%s\t%S\t%S\t%c", strnum, canal.canal_no, szFreq, + *canal.groupe ? canal.groupe : "", canal.nom, norme); AddLineToList(hListItem, szTemp); } } @@ -426,6 +426,917 @@ return FALSE; } +#define CHAN_COL_NO 0 +#define CHAN_COL_CANAL 1 +#define CHAN_COL_FREQ 2 +#define CHAN_COL_GROUP 3 +#define CHAN_COL_NAME 4 +#define CHAN_COL_STATE 5 +#define CHAN_COL_MPEG 6 + +static void State2LV(HWND hListItem, int iItem, EtatChaine etat) +{ + const LVITEM item = { + LVIF_TEXT, // mask + iItem, // iItem + CHAN_COL_STATE, // iSubItem + 0, 0, // state, stateMask + LPWSTR(dword2str(aChStateTable, etat)) // pszText + // Le reste à zéro (implicite) + }; + + ListView_SetItem(hListItem, &item); +} + +void InsertChannelItem(HWND hListItem, int iItem, const Chaine & canal) +{ + wchar_t buffer[64]; + LVITEM item = { + LVIF_PARAM | LVIF_TEXT, // mask + iItem, // iItem + CHAN_COL_NO, // iSubItem + 0, 0, // state, stateMask + buffer, // pszText + 0, 0, // cchTextMax, iImage + canal.SID // lParam + // Le reste à zéro (implicite) + }; + + swprintf_s(buffer, _countof(buffer), L"%u", canal.numeroChaine); + ListView_InsertItem(hListItem, &item); + + item.mask = LVIF_TEXT; // On ne met que le texte dans les colonnes suivantes + item.lParam = 0; + + item.iSubItem = CHAN_COL_CANAL; + swprintf_s(buffer, _countof(buffer), L"%u", canal.canal_no); + ListView_SetItem(hListItem, &item); + + item.iSubItem = CHAN_COL_FREQ; + swprintf_s(buffer, _countof(buffer), L"%03u,%03u", canal.frequence/1000, canal.frequence%1000); + ListView_SetItem(hListItem, &item); + + item.iSubItem = CHAN_COL_GROUP; + swprintf_s(buffer, _countof(buffer), L"%S", canal.groupe); + ListView_SetItem(hListItem, &item); + + item.iSubItem = CHAN_COL_NAME; + swprintf_s(buffer, _countof(buffer), L"%S", canal.nom); + ListView_SetItem(hListItem, &item); + + State2LV(hListItem, iItem, canal.etat); + + item.iSubItem = CHAN_COL_MPEG; + swprintf_s(buffer, _countof(buffer), L"%c", canal.norme()); + ListView_SetItem(hListItem, &item); +} + +static void remplit_liste_chaines(HWND hListItem) +{ + ListView_DeleteAllItems(hListItem); + + int nbChaines = (int)Canaux.size(); + + for (int i = 0; iiSubItem; // N° colonne + wchar_t buf1[64] = L""; + wchar_t buf2[64] = L""; + LVITEM item1 = { + LVIF_PARAM | LVIF_TEXT, // mask + (int)lParam1, // iItem + iCol, // iSubItem + 0, 0, // state, stateMask + buf1, // pszText + _countof(buf1) // cchTextMax + // le reste à zéro (implicite) + }; + LVITEM item2 = { + LVIF_PARAM | LVIF_TEXT, // mask + (int)lParam2, // iItem + iCol, // iSubItem + 0, 0, // state, stateMask + buf2, // pszText + _countof(buf2) // cchTextMax + // le reste à zéro (implicite) + }; + int res = 0; // résultat + + ListView_GetItem(pnml->hdr.hwndFrom, &item1); + ListView_GetItem(pnml->hdr.hwndFrom, &item2); + + switch (iCol) { + + case CHAN_COL_NO: { // N° + case CHAN_COL_CANAL: // Canal + int no1 =_wtoi(buf1); + int no2 =_wtoi(buf2); + + res = no1no2 ? 1 : 0; + break; } + + case CHAN_COL_FREQ: // Fréquence + case CHAN_COL_GROUP: // Groupe + case CHAN_COL_NAME: // Nom + case CHAN_COL_STATE: // État + case CHAN_COL_MPEG: // MPEG + res = _wcsicmp(buf1, buf2); + } + + // Tri descendant si pnml->lParam != 0 + if (pnml->lParam!=0) + res = -res; + return res; +} + +void DrawArrow(HDC hDC, LONG x, LONG y, COLORREF color, COLORREF fill) +{ + POINT ptv[] = { + { 0, 0}, + { -4, 4}, + { -5, 4}, + { -5, 2}, + {-10, 2}, + {-10, -4}, + { -7, -4}, + { -7, -2}, + { -5, -2}, + { -5, -4}, + { -4, -4} + }; + + for (int i=0; i<_countof(ptv); i++) { + ptv[i].x += x; + ptv[i].y += y; + } + + HPEN hPen = CreatePen(PS_SOLID, 1, color); + HBRUSH hBrush = CreateSolidBrush(fill); + + hPen = HPEN(SelectObject(hDC, hPen)); + hBrush = HBRUSH(SelectObject(hDC, hBrush)); + Polygon(hDC, ptv, _countof(ptv)); + DeleteObject(SelectObject(hDC, hBrush)); + DeleteObject(SelectObject(hDC, hPen)); +} + +class ChannelsLVDrag { + HWND hDlg; + HWND hListItem; + HIMAGELIST hDragImageList; + int iItemMrq; // Item couramment pointé par le marqueur d'insertion + int iItemEmpty; // Item vide inséré à la fin + DWORD iSaveTicks; + bool scrolldown; + // + HIMAGELIST CreateDragImage(); + void DrawInsertMark(int iItem, bool erase); + int ItemHit(LONG x, LONG y); +public: + ChannelsLVDrag(HWND hDg, HWND hLstItem); + void Begin(LONG x, LONG y); + void Move(LONG x, LONG y); + int End(LONG x, LONG y); // (retourne le point d'insertion) + void Scroll(); + ~ChannelsLVDrag(); +} * m_Drag = NULL; + +ChannelsLVDrag::ChannelsLVDrag(HWND hDg, HWND hLstItem) : + hDlg(hDg), + hListItem(hLstItem), + hDragImageList(NULL), + iItemMrq(-1), + iItemEmpty(0), + iSaveTicks(0), + scrolldown(false) +{ + if (iItemEmpty==0) { + // Ajout d'un item vide à la fin pour permettre l'insertion après le dernier item : + LVITEM item = { + LVIF_PARAM | LVIF_TEXT, // mask + 0x7fffffff, // iItem (assignation à la fin) + 0, // iSubItem + 0, 0, // state, stateMask + L"", // pszText + // Le reste à zéro (implicite) + }; + + iItemEmpty = ListView_InsertItem(hListItem, &item); + } + myprintf(L"ChannelsLVDrag construit\n"); +} + +HIMAGELIST ChannelsLVDrag::CreateDragImage() +{ + HIMAGELIST hDragImageList = NULL; + int iHeight = 0; + + // Création d'une image "drag" pour chacun des items sélectionnés + + for (int iPos = ListView_GetNextItem(hListItem, -1, LVNI_SELECTED); + iPos!= -1; iPos = ListView_GetNextItem(hListItem, iPos, LVNI_SELECTED) + ) { + POINT p; + + if (hDragImageList==NULL) { + // For the first selected item, + // we simply create a single-line drag image + hDragImageList = ListView_CreateDragImage(hListItem, iPos, &p); + } else { + // For the rest selected items, + // we create a single-line drag image, then + // append it to the bottom of the complete drag image + HIMAGELIST hTempImageList = hDragImageList; + HIMAGELIST hOneImageList = + ListView_CreateDragImage(hListItem, iPos, &p); + + hDragImageList = + ImageList_Merge(hTempImageList, 0, hOneImageList, 0, 0, iHeight); + ImageList_Destroy(hTempImageList); + ImageList_Destroy(hOneImageList); + } + + IMAGEINFO imf; + + ImageList_GetImageInfo(hDragImageList, 0, &imf); + iHeight = imf.rcImage.bottom; + } + + return hDragImageList; +} + +void ChannelsLVDrag::Begin(LONG x, LONG y) +{ + // Initialisation et démarrarage du glisser-déposer + // (implémenté d'après http://www.codeproject.com/listctrl/jianghong.asp) + + hDragImageList = CreateDragImage(); + + ImageList_BeginDrag(hDragImageList, 0, 0, 0); + + POINT p = {x, y}; + ClientToScreen(hListItem, &p); + ImageList_DragEnter(GetDesktopWindow(), p.x, p.y); + + SetCapture(hDlg); // Capture de la souris + myprintf(L"ChannelsLVDrag::Begin\n"); +} + +void ChannelsLVDrag::DrawInsertMark(int iItem, bool erase) +{ + if (iItem<0) + return; + + RECT rc; + + // Déterminer la position du marqueur, dans la marge de gauche du dialogue : + ListView_GetItemRect(hListItem, iItem, &rc, LVIR_BOUNDS); // Position dans la LV + POINT pt = {rc.left-4, rc.top}; // Position de la pointe du marqueur + ClientToScreen(hListItem, &pt); // Conversion de coordonnées + ScreenToClient(hDlg, &pt); + + if (erase) { + // Effacement : + RECT irc = {pt.x-10, pt.y-4, pt.x+1, pt.y+5}; + + InvalidateRect(hDlg, &irc, true); + } else { + // Dessin du marqueur : + HDC hDC = GetDC(hDlg); + + POINT ptv[] = { + {pt.x, pt.y}, + {pt.x-4, pt.y+4}, + {pt.x-5, pt.y+4}, + {pt.x-5, pt.y+2}, + {pt.x-10, pt.y+2}, + {pt.x-10, pt.y-4}, + {pt.x-7, pt.y-4}, + {pt.x-7, pt.y-2}, + {pt.x-5, pt.y-2}, + {pt.x-5, pt.y-4}, + {pt.x-4, pt.y-4} + }; + + HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255)); // blanc + HBRUSH hBrush = CreateSolidBrush(RGB(0,0,0)); // noir + + hPen = HPEN(SelectObject(hDC, hPen)); + hBrush = HBRUSH(SelectObject(hDC, hBrush)); + Polygon(hDC, ptv, _countof(ptv)); + DeleteObject(SelectObject(hDC, hBrush)); + DeleteObject(SelectObject(hDC, hPen)); + + ReleaseDC(hDlg, hDC); + } +} + +// Fonction retournant le numéro de l'item pointé par la souris, ou bien : +// -2 si on pointe au-dessus +// -3 si on pointe en-dessous +// -1 si la souris ne pointe pas un item +int ChannelsLVDrag::ItemHit(LONG x, LONG y) +{ + // Trouver l'item sous la souris : + LVHITTESTINFO lvhti = { + {x, y} + // Le reste à zéro (implicite) + }; + + ClientToScreen(hDlg, &lvhti.pt); + ScreenToClient(hListItem, &lvhti.pt); + ListView_HitTest(hListItem, &lvhti); + + // En dehors de la liste, ni dans un item ? + if (lvhti.iItem != -1) { + // Vérifier si on ne pointe pas dans les en-têtes de colonnes : + RECT hdrect; + + GetClientRect(ListView_GetHeader(hListItem), &hdrect); + if (PtInRect(&hdrect, lvhti.pt)) + return -2; // si dans les en-têtes + return lvhti.iItem; // item trouvé + } + if (lvhti.flags & LVHT_ABOVE) + return -2; // trop en haut + if (lvhti.flags & LVHT_BELOW) + return -3; // trop en haut + return -1; // ailleurs +} + +void ChannelsLVDrag::Scroll() +{ + ListView_Scroll(hListItem, 0, scrolldown ? 16 : -16); + InvalidateRect(hListItem, NULL, TRUE); // Ajouté à cause d'un certain problème de corruption d'affichage ... + iSaveTicks = GetTickCount(); + SetTimer(hDlg, 0, 200, NULL); +} + +void ChannelsLVDrag::Move(LONG x, LONG y) +{ + POINT pt = {x, y}; + + ClientToScreen(hDlg, &pt); + ScreenToClient(hListItem, &pt); + pt.x = 0; + ClientToScreen(hListItem, &pt); + ImageList_DragMove(pt.x, pt.y); + + int iItem = ItemHit(x, y); // Déterminer l'item choisi + + if (iItemMrq!=iItem) { // si item pointé modifié : + DrawInsertMark(iItemMrq, true); // Retirer le marqueur de sélection existant + iItemMrq = -1; + } + + switch (iItem) { + case -1: + KillTimer(hDlg, 0); + return; // Aucun item pointé + case -2: + case -3: + // Trop haut ou trop bas + if (GetTickCount() - iSaveTicks >= 250) { + scrolldown = iItem==-3; + Scroll(); + } + break; + default: + if (iItemMrq!=iItem) { // si item pointé modifié : + KillTimer(hDlg, 0); + DrawInsertMark(iItem, false); // Dessiner le nouveau marqueur de sélection + iItemMrq = iItem; + } + } +} + +int ChannelsLVDrag::End(LONG x, LONG y) +{ + myprintf(L"ChannelsLVDrag::End\n"); + + KillTimer(hDlg, 0); + ImageList_DragLeave(hListItem); + ImageList_EndDrag(); + ImageList_Destroy(hDragImageList); + + ReleaseCapture(); + + // Effacement du dernier marqueur d'insertion : + DrawInsertMark(iItemMrq, true); + iItemMrq = -1; + + int iItem = ItemHit(x, y); + if (iItem <0) + return -1; + + // Est-ce que l'item de chute est sélectionné ? + LVITEM lvi = { + LVIF_STATE, // mask + iItem, // iItem + 0, // iSubItem + 0, // state, + LVIS_SELECTED // stateMask + // Le reste à zéro (implicite) + }; + + ListView_GetItem(hListItem, &lvi); + + if (lvi.state & LVIS_SELECTED) + return -1; + return iItem; + +} + +ChannelsLVDrag::~ChannelsLVDrag() +{ + if (iItemEmpty>0) { + // Suppression de l'item vide qui avait été ajouté à la fin au début de l'opération : + ListView_DeleteItem(hListItem, iItemEmpty); + iItemEmpty = 0; + } + // Redessiner tout le contrôle contenant les chaînes : + InvalidateRect(hListItem, NULL, TRUE); + myprintf(L"ChannelsLVDrag détruit\n"); +} + +#ifdef MODIFY_CHANNELS_BY_CONTROLS +static void EnableModifications(HWND hDlg, BOOL bEnable) +{ + EnableWindow(GetDlgItem(hDlg, IDC_STATIC_MODIFY), bEnable); + EnableWindow(GetDlgItem(hDlg, IDC_ACTIVE), bEnable); + EnableWindow(GetDlgItem(hDlg, IDC_PREFERRED), bEnable); + EnableWindow(GetDlgItem(hDlg, IDC_STATIC_NUMBER), bEnable); + EnableWindow(GetDlgItem(hDlg, IDC_NUMBER), bEnable); +} +#endif + +// Application d'un changement de numéro de chaîne : +static void channel_number_changed(HWND hListItem, int iItem, LPCWSTR str_newnum) +{ + WORD canal_no = (WORD)_wtoi(str_newnum); + + if (canal_no>0 && canal_no<1000) { + wchar_t buf1[16], buf2[16]; + bool confirm = true; + HWND hDlg = GetParent(hListItem); + + swprintf_s(buf1, _countof(buf1), L"%u", canal_no); // reformater + LVFINDINFO lvfi = { + LVFI_STRING, + buf1 + // Le reste à zéro (implicite) + }; + + int iItemFound = ListView_FindItem(hListItem, -1, &lvfi); + + if (iItemFound>=0 && iItemFound != iItem) { + wchar_t chnom1[32], chnom2[32], bufmsg[256]; + + ListView_GetItemText(hListItem, iItem, CHAN_COL_NAME, chnom1, _countof(chnom1)); + ListView_GetItemText(hListItem, iItemFound, CHAN_COL_NAME, chnom2, _countof(chnom2)); + ListView_GetItemText(hListItem, iItem, CHAN_COL_NO, buf2, _countof(buf2)); + swprintf_s(bufmsg, _countof(bufmsg), + L"Le nouveau numéro que vous assignez à la chaîne %s est déjà attribué " + L"à la chaîne %s.\nConfirmez-vous le changement ?\n" + L"(les numéros seront alors échangés : la chaîne %s recevra le numéro %s)", + chnom1, chnom2, chnom2, buf2); + confirm = MessageBox(hDlg, bufmsg, + L"Renumérotation de chaîne", MB_OKCANCEL)==IDOK; + if (confirm) + ListView_SetItemText(hListItem, iItemFound, CHAN_COL_NO, buf2); + } + + if (confirm) { + ListView_SetItemText(hListItem, iItem, CHAN_COL_NO, buf1); + PropSheet_Changed(GetParent(hDlg), hDlg); + } + } +} + +static EtatChaine ItemToEtat(HWND hListItem, int iItem) +{ + wchar_t buf[16]; + + // Mise à jour des cases à cocher liées à l'état : + ListView_GetItemText(hListItem, iItem, CHAN_COL_STATE, buf, _countof(buf)); + + return EtatChaine(str2dword(aChStateTable, buf, ec_preferee)); +} + +#ifdef MODIFY_CHANNELS_BY_CONTROLS +static void ItemToEdit (HWND hDlg, HWND hListItem, int iItem) +{ + wchar_t buf[16]; + + // Mise à jour du numéro dans l'item d'édition: + ListView_GetItemText(hListItem, iItem, CHAN_COL_NO, buf, _countof(buf)); + SetWindowText(GetDlgItem(hDlg, IDC_NUMBER), buf); + + // Mise à jour des cases à cocher liées à l'état : + ListView_GetItemText(hListItem, iItem, CHAN_COL_STATE, buf, _countof(buf)); + + EtatChaine etat = ItemToEtat(hListItem, iItem); + + set_check(hDlg, IDC_ACTIVE, etat!=ec_inactive); + set_check(hDlg, IDC_PREFERRED, etat==ec_preferee); +} +#endif + +static INT_PTR CALLBACK RenumberDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_INITDIALOG: + // Initialisation de la fenêtre + SendDlgItemMessage(hDlg, IDC_RENUM_SEQ, BM_SETCHECK, WPARAM(BST_CHECKED), 0); + return TRUE; + + case WM_COMMAND: + // Commande reçue (clic, etc...) + switch(LOWORD(wParam)) { + + case IDOK: + // Clic sur le bouton OK + EndDialog(hDlg, + SendDlgItemMessage(hDlg, IDC_RENUM_REST, BM_GETCHECK, 0, 0)==BST_CHECKED ? + IDC_RENUM_REST : IDC_RENUM_SEQ); + return TRUE; + + case IDCANCEL: + // Clic sur le bouton Annuler + EndDialog(hDlg, IDCANCEL); + return TRUE; + } + } + + // Autre message, traité par Windows + return FALSE; +} + +static INT_PTR renumber_dialog() +{ + INT_PTR res = + DialogBoxParam(hAppInstance, MAKEINTRESOURCE(IDD_RENUMBER), hMainWnd, + RenumberDialogProc, DIALOGMODE); + return res; +} + +static INT_PTR CALLBACK ChannelsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static const struct Tab_cols { + LPCWSTR nom; + int taille; + } t_cols[] = { + {L"N°", 30}, + {L"C.", 30}, + {L"MHz", 55}, + {L"Groupe", 60}, + {L"Nom de la chaîne", 120}, + {L"État", 60}, + {L"Mp", 30} + }; + + static HWND hListItem = NULL; + static int iItemEdit = -1; + static WORD sidSauveChaineCourante = 0; + wchar_t buf[16]; + int i; + + switch (uMsg) { + + case WM_INITDIALOG: { + if (ixChaine_ok(ixChaineCourante)) + sidSauveChaineCourante = Canaux[ixChaineCourante].SID; + hListItem = GetDlgItem(hDlg, IDC_CHANNEL_LIST); + ListView_SetExStyle(hListItem, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); + + for (i=0; i<_countof(t_cols); i++) { + const Tab_cols & col = t_cols[i]; + LVCOLUMN colonne = { + LVCF_TEXT|LVCF_WIDTH, // mask + 0, // fmt + col.taille, // cx + const_cast(col.nom) // pszText + // le reste à zéro (implicite) + }; + + ListView_InsertColumn(hListItem, i, &colonne); + } + + remplit_liste_chaines(hListItem); + return TRUE; } + + case WM_COMMAND: + switch(wParam) { + +#ifdef MODIFY_CHANNELS_BY_CONTROLS + case _cmd_(IDC_NUMBER, EN_KILLFOCUS): + if (iItemEdit>=0){ + // Modification du numéro : + GetWindowText(GetDlgItem(hDlg, IDC_NUMBER), buf, _countof(buf)); + channel_number_changed(hListItem, iItemEdit, buf); + } + break; + + case _cmd_(IDC_ACTIVE, BN_CLICKED): + case _cmd_(IDC_PREFERRED, BN_CLICKED): + if (iItemEdit>=0) { + bool active = get_check(hDlg, IDC_ACTIVE); + bool preferee = get_check(hDlg, IDC_PREFERRED); + EtatChaine etat = + active ? + (preferee ? ec_preferee : ec_active) : + ec_inactive; + + State2LV(hListItem, iItemEdit, etat); + if (!active && preferee) + set_check(hDlg, IDC_PREFERRED, false); + PropSheet_Changed(GetParent(hDlg), hDlg); + } + break; +#endif + + case _cmd_(IDC_SCAN, BN_CLICKED): { + static bool rechercheEncours = false; + + if (!rechercheEncours) { + rechercheEncours = true; + BYTE res = scan_dialog(); + + if (res==IDABORT) + MessageBox(hDlg, + L"Annulation : La configuration précédente des chaînes a été rétablie", + L"Recherche annulée", MB_ICONINFORMATION | MB_OK); + rechercheEncours = false; + remplit_liste_chaines(hListItem); + } + break; } + + case _cmd_(IDC_RENUMBER, BN_CLICKED): { + int res = renumber_dialog(); + if (res==IDC_RENUM_SEQ || res==IDC_RENUM_REST) { + int nbItems = ListView_GetItemCount(hListItem); + WORD iCount = 0; + + for (i=0; i=0) { + State2LV(hListItem, iItemEdit, EtatChaine(LOWORD(wParam)-IDM_CHAN_STATE_BASE)); +#ifdef MODIFY_CHANNELS_BY_CONTROLS + EnableModifications(hDlg, TRUE); + ItemToEdit(hDlg, hListItem, iItemEdit); +#endif + } + break; + } + return FALSE; + + case WM_NOTIFY: + switch (((NMHDR FAR *) lParam)->code) { + +#ifdef MODIFY_CHANNELS_BY_CONTROLS + case NM_CLICK: + // Notification de clic dans un item de la liste. + if (wParam==IDC_CHANNEL_LIST) { + NMITEMACTIVATE & nmia = *(LPNMITEMACTIVATE)lParam; + UINT nbit = ListView_GetSelectedCount(hListItem); + + if (nbit==1) { + iItemEdit = nmia.iItem; + EnableModifications(hDlg, TRUE); + ItemToEdit(hDlg, hListItem, nmia.iItem); + } else { + iItemEdit = -1; + EnableModifications(hDlg, FALSE); + } + } + break; +#endif + + case LVN_ENDLABELEDIT: { + NMLVDISPINFO & nmlvdi = *(NMLVDISPINFO *)lParam; + + if (nmlvdi.item.iItem>=0 && nmlvdi.item.pszText!=NULL) + channel_number_changed(hListItem, nmlvdi.item.iItem, nmlvdi.item.pszText); + break; } + + case LVN_BEGINDRAG: + if (wParam==IDC_CHANNEL_LIST) { + if (m_Drag==NULL) + m_Drag = new ChannelsLVDrag(hDlg, hListItem); + if (m_Drag) + m_Drag->Begin(LPNMLISTVIEW(lParam)->ptAction.x, LPNMLISTVIEW(lParam)->ptAction.y); + } + break; + + case LVN_COLUMNCLICK: + if (wParam==IDC_CHANNEL_LIST) { + // Tri de la liste par clic sur un en-tête de colonne : + static BOOL tblSortDir[_countof(t_cols)] = {0}; + LPNMLISTVIEW pnml = ((LPNMLISTVIEW)lParam); + int iCol = pnml->iSubItem; // N° de colonne concernée + BOOL & bColFlag = tblSortDir[iCol]; + + pnml->lParam = bColFlag; // on réquisitionne ce champ pour transmettre le sens du tri + bColFlag = !bColFlag; // le tri suivant sera dans le sens inverse + ListView_SortItemsEx(hListItem, LVCompareProc, lParam); + PropSheet_Changed(GetParent(hDlg), hDlg); + iItemEdit = -1; +#ifdef MODIFY_CHANNELS_BY_CONTROLS + EnableModifications(hDlg, FALSE); +#endif + } + break; + + case PSN_APPLY: { + // Application des changements + int nbItems = ListView_GetItemCount(hListItem); + + for (i=0; i0) + ixChaineCourante = trouve_chaine_par_sid(sidSauveChaineCourante); + + sauve_chaines(); // sauvegarde les chaînes dans le ini + iItemEdit = -1; +#ifdef MODIFY_CHANNELS_BY_CONTROLS + EnableModifications(hDlg, FALSE); +#endif + break; } + } + return FALSE; + + case WM_MOUSEMOVE: + // Gestion du mouvement de la souris dans le glisser-déposer : + if (m_Drag) + m_Drag->Move(LOWORD(lParam), HIWORD(lParam)); + + break; + + case WM_TIMER: + if (m_Drag) + m_Drag->Scroll(); + break; + + case WM_LBUTTONUP: { + // Fin du processus de glisser-déposer + int iItem = -1; + + if (m_Drag) { + iItem = m_Drag->End(LOWORD(lParam), HIWORD(lParam)); + delete m_Drag; + m_Drag = NULL; + } + + if (iItem<0) + break; + + // Réarrangement des items + for (int iPos = ListView_GetNextItem(hListItem, -1, LVNI_SELECTED); + iPos != -1; iPos = ListView_GetNextItem(hListItem, -1, LVNI_SELECTED) + ) { + wchar_t buf[64]; + LVITEM lvi = { + LVIF_STATE | LVIF_IMAGE | LVIF_INDENT | LVIF_PARAM | LVIF_TEXT, // mask + iPos, // iItem + 0, // iSubItem + 0, // state + (UINT)~LVIS_SELECTED, // stateMask + buf, // pszText + _countof(buf), // cchTextMax + // Le reste à zéro (implicite) + }; + + // Premièrement, copier un item + ListView_GetItem(hListItem, &lvi); + lvi.iItem = iItem; + // Insérer l'item principal + int iRet = ListView_InsertItem(hListItem, &lvi); + if (lvi.iItem < iPos) + iItem++; + if (iRet <= iPos) + iPos++; + // Insérer le texte des sous-items : + for (i = 1; i < _countof(t_cols); i++) { + ListView_GetItemText(hListItem, iPos, i, buf, _countof(buf)); + ListView_SetItemText(hListItem, iRet, i, buf); + } + // Supprimer de la position initiale + ListView_DeleteItem(hListItem, iPos); + } + PropSheet_Changed(GetParent(hDlg), hDlg); + break; } + case WM_CONTEXTMENU: { + POINT pt; + + GetCursorPos(&pt); + + LVHITTESTINFO lvhti = { + pt + // Le reste à zéro (implicite) + }; + + ScreenToClient(hListItem, &lvhti.pt); + if (ListView_SubItemHitTest(hListItem, &lvhti)>=0 && lvhti.iSubItem==CHAN_COL_STATE) { + static const struct Tab_states { + LPCWSTR nom; + UINT uID; + } t_states[] = { + {L"&Inactive", IDM_CHAN_INACTIVE}, + {L"&Active", IDM_CHAN_ACTIVE}, + {L"&Préférée", IDM_CHAN_PREFERRED} + }; + EtatChaine etat = ItemToEtat(hListItem, lvhti.iItem); + HMENU pop = CreatePopupMenu(); + for (i=0; i<_countof(t_states); i++) { + const Tab_states e_stat = t_states[i]; + + AppendMenu(pop, i==etat ? MF_STRING|MF_CHECKED : MF_STRING, + e_stat.uID, e_stat.nom); + } + iItemEdit = lvhti.iItem; // se souvenir quel item on traite + TrackPopupMenuEx(pop, TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, hDlg, NULL); + + DestroyMenu(pop); + } + break; } + } + return FALSE; +} + static INT_PTR CALLBACK OptionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -565,6 +1476,17 @@ VOID do_config(HWND hwndOwner) { const PROPSHEETPAGE psp[] = { + { // Page des chaînes + sizeof(PROPSHEETPAGE), // dwSize + PSP_USETITLE, // dwFlags + hAppInstance, // hInstance + {MAKEINTRESOURCE(IDD_CHANNELS)},// pszTemplate (union) + {NULL}, // pszIcon (union) + L"Chaînes", // pszTitle + ChannelsDialogProc, // pfnDlgProc + PROPSHEETMODE, // lParam + NULL // pfnCallback + }, { // Page des ressources (cartes et codecs) sizeof(PROPSHEETPAGE), // dwSize PSP_USETITLE, // dwFlags