schemes.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 16k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "pch.hxx" // pch
  2. #pragma hdrstop
  3. #include "Schemes.h"
  4. // To use the old way of enumerating fonts to get the font list,
  5. // and reading schemes from the registry, remove the comments from
  6. // the two lines below
  7. //#define ENUMERATEFONTS
  8. //#define READSCHEMESFROMREGISTRY
  9. /////////////////////////////////////////////////////////////////////
  10. //  New way of enumerating fonts
  11. #ifndef ENUMERATEFONTS
  12. static LPCTSTR g_lpszFontNames[] =
  13. {
  14. __TEXT("Arial"),
  15. __TEXT("MS Sans Serif"),
  16. __TEXT("Tahoma"),
  17. __TEXT("Times New Roman")
  18. };
  19. int GetFontCount()
  20. {
  21. return ARRAYSIZE(g_lpszFontNames);
  22. }
  23. void GetFontLogFont(int nIndex, LOGFONT *pLogFont)
  24. {
  25. _ASSERTE(nIndex < ARRAYSIZE(g_lpszFontNames));
  26. memset(pLogFont, 0, sizeof(*pLogFont));
  27. lstrcpy(pLogFont->lfFaceName, g_lpszFontNames[nIndex]);
  28. }
  29. #endif // ENUMERATEFONTS
  30. //
  31. /////////////////////////////////////////////////////////////////////
  32. /////////////////////////////////////////////////////////////////////
  33. //  New way of storing schemes as hard coded values
  34. #ifndef READSCHEMESFROMREGISTRY
  35. #include "resource.h"
  36. static SCHEMEDATALOCAL g_rgSchemeData[] = 
  37. {
  38. {
  39. IDS_SCHEME_HIGHCONTRASTBLACK,
  40. 25,
  41. {
  42. RGB(  0,   0,   0), // Scrollbar
  43. RGB(  0,   0,   0), // Background
  44. RGB(128,   0, 128), // ActiveTitle
  45. RGB(  0, 128,   0), // InactiveTitle
  46. RGB(  0,   0,   0), // Menu
  47. RGB(  0,   0,   0), // Window
  48. RGB(255, 255, 255), // WindowFrame
  49. RGB(255, 255, 255), // MenuText
  50. RGB(255, 255, 255), // WindowText
  51. RGB(255, 255, 255), // TitleText
  52. RGB(255, 255,   0), // ActiveBorder
  53. RGB(  0, 128,   0), // InactiveBorder
  54. RGB(  0,   0,   0), // AppWorkspace
  55. RGB(128,   0, 128), // Hilight
  56. RGB(255, 255, 255), // HilightText
  57. RGB(  0,   0,   0), // ButtonFace
  58. RGB(128, 128, 128), // ButtonShadow
  59. RGB(  0, 255,   0), // GrayText
  60. RGB(255, 255, 255), // ButtonText
  61. RGB(255, 255, 255), // InactiveTitleText
  62. RGB(192, 192, 192), // ButtonHilight
  63. RGB(255, 255, 255), // ButtonDkShadow
  64. RGB(255, 255, 255), // ButtonLight
  65. RGB(255, 255, 255), // InfoText
  66. RGB(  0,   0,   0), // InfoWindow
  67. }
  68. },
  69. {
  70. IDS_SCHEME_HIGHCONTRASTWHITE,
  71. 25,
  72. {
  73. RGB(255, 255, 255), // Scrollbar
  74. RGB(255, 255, 255), // Background
  75. RGB(  0,   0,   0), // ActiveTitle
  76. RGB(255, 255, 255), // InactiveTitle
  77. RGB(255, 255, 255), // Menu
  78. RGB(255, 255, 255), // Window
  79. RGB(  0,   0,   0), // WindowFrame
  80. RGB(  0,   0,   0), // MenuText
  81. RGB(  0,   0,   0), // WindowText
  82. RGB(255, 255, 255), // TitleText
  83. RGB(128, 128, 128), // ActiveBorder
  84. RGB(192, 192, 192), // InactiveBorder
  85. RGB(128, 128, 128), // AppWorkspace
  86. RGB(  0,   0,   0), // Hilight
  87. RGB(255, 255, 255), // HilightText
  88. RGB(255, 255, 255), // ButtonFace
  89. RGB(128, 128, 128), // ButtonShadow
  90. RGB(  0, 255,   0), // GrayText
  91. RGB(  0,   0,   0), // ButtonText
  92. RGB(  0,   0,   0), // InactiveTitleText
  93. RGB(192, 192, 192), // ButtonHilight
  94. RGB(  0,   0,   0), // ButtonDkShadow
  95. RGB(192, 192, 192), // ButtonLight
  96. RGB(  0,   0,   0), // InfoText
  97. RGB(255, 255, 255), // InfoWindow
  98. }
  99. },
  100. {
  101. IDS_SCHEME_HIGHCONTRASTBLACKALTERNATE,
  102. 25,
  103. {
  104. RGB(  0,   0,   0), // Scrollbar
  105. RGB(  0,   0,   0), // Background
  106. RGB(  0,   0, 255), // ActiveTitle
  107. RGB(  0, 255, 255), // InactiveTitle
  108. RGB(  0,   0,   0), // Menu
  109. RGB(  0,   0,   0), // Window
  110. RGB(255, 255, 255), // WindowFrame
  111. RGB(255, 255, 255), // MenuText
  112. RGB(255, 255,   0), // WindowText
  113. RGB(255, 255, 255), // TitleText
  114. RGB(  0,   0, 255), // ActiveBorder
  115. RGB(  0, 255, 255), // InactiveBorder
  116. RGB(  0,   0,   0), // AppWorkspace
  117. RGB(  0, 128,   0), // Hilight
  118. RGB(255, 255, 255), // HilightText
  119. RGB(  0,   0,   0), // ButtonFace
  120. RGB(128, 128, 128), // ButtonShadow
  121. RGB(  0, 255,   0), // GrayText
  122. RGB(255, 255, 255), // ButtonText
  123. RGB(  0,   0,   0), // InactiveTitleText
  124. RGB(192, 192, 192), // ButtonHilight
  125. RGB(255, 255, 255), // ButtonDkShadow
  126. RGB(255, 255, 255), // ButtonLight
  127. RGB(255, 255,   0), // InfoText
  128. RGB(  0,   0,   0), // InfoWindow
  129. }
  130. },
  131. {
  132. IDS_SCHEME_HIGHCONTRASTWHITEALTERNATE,
  133. 25,
  134. {
  135. #if 1
  136. RGB(  0,   0,   0), // Scrollbar
  137. RGB(  0,   0,   0), // Background
  138. RGB(  0, 255, 255), // ActiveTitle
  139. RGB(  0,   0, 255), // InactiveTitle
  140. RGB(  0,   0,   0), // Menu
  141. RGB(  0,   0,   0), // Window
  142. RGB(255, 255, 255), // WindowFrame
  143. RGB(  0, 255,   0), // MenuText
  144. RGB(  0, 255,   0), // WindowText
  145. RGB(  0,   0,   0), // TitleText
  146. RGB(  0, 255, 255), // ActiveBorder
  147. RGB(  0,   0, 255), // InactiveBorder
  148. RGB(255, 251, 240), // AppWorkspace
  149. RGB(  0,   0, 255), // Hilight
  150. RGB(255, 255, 255), // HilightText
  151. // RGB(255, 255, 255), // ButtonFace
  152. RGB(0, 0, 0), // ButtonFace
  153. RGB(128, 128, 128), // ButtonShadow
  154. RGB(  0, 255,   0), // GrayText
  155. RGB(  0, 255,   0), // ButtonText
  156. RGB(255, 255, 255), // InactiveTitleText
  157. RGB(192, 192, 192), // ButtonHilight
  158. RGB(255, 255, 255), // ButtonDkShadow
  159. RGB(255, 255, 255), // ButtonLight
  160. RGB(  0,   0,   0), // InfoText
  161. RGB(255, 255,   0), // InfoWindow
  162. #else
  163. RGB(  0,   0,   0), // Scrollbar
  164. RGB(  0,   0,   0), // Background
  165. RGB(  0, 255, 255), // ActiveTitle
  166. RGB(  0,   0, 255), // InactiveTitle
  167. RGB(  0,   0,   0), // Menu
  168. RGB(255, 251, 240), // Window
  169. RGB(255, 255, 255), // WindowFrame
  170. RGB(  0, 255,   0), // MenuText
  171. RGB(  0,   0,   0), // WindowText
  172. RGB(  0,   0,   0), // TitleText
  173. RGB(  0, 255, 255), // ActiveBorder
  174. RGB(  0,   0, 255), // InactiveBorder
  175. RGB(255, 251, 240), // AppWorkspace
  176. RGB(  0,   0, 255), // Hilight
  177. RGB(255, 255, 255), // HilightText
  178. RGB(255, 255,   0), // ButtonFace
  179. RGB(128, 128, 128), // ButtonShadow
  180. RGB(  0, 255,   0), // GrayText
  181. RGB(255, 255,   0), // ButtonText
  182. RGB(255, 255, 255), // InactiveTitleText
  183. RGB(192, 192, 192), // ButtonHilight
  184. RGB(255, 255, 255), // ButtonDkShadow
  185. RGB(255, 255, 255), // ButtonLight
  186. RGB(255, 255,   0), // InfoText
  187. RGB(  0,   0,   0), // InfoWindow
  188. #endif
  189. }
  190. },
  191. {
  192. IDS_SCHEME_WINDOWSSTANDARD,
  193. 25,
  194. {
  195. RGB(192, 192, 192), // Scrollbar
  196. RGB(  0, 128, 128), // Background
  197. RGB(  0,   0, 128), // ActiveTitle
  198. RGB(128, 128, 128), // InactiveTitle
  199. RGB(192, 192, 192), // Menu
  200. RGB(255, 255, 255), // Window
  201. RGB(  0,   0,   0), // WindowFrame
  202. RGB(  0,   0,   0), // MenuText
  203. RGB(  0,   0,   0), // WindowText
  204. RGB(255, 255, 255), // TitleText
  205. RGB(192, 192, 192), // ActiveBorder
  206. RGB(192, 192, 192), // InactiveBorder
  207. RGB(128, 128, 128), // AppWorkspace
  208. RGB(  0,   0, 128), // Hilight
  209. RGB(255, 255, 255), // HilightText
  210. RGB(192, 192, 192), // ButtonFace
  211. RGB(128, 128, 128), // ButtonShadow
  212. RGB(128, 128, 128), // GrayText
  213. RGB(  0,   0,   0), // ButtonText
  214. RGB(192, 192, 192), // InactiveTitleText
  215. RGB(255, 255, 255), // ButtonHilight
  216. RGB(  0,   0,   0), // ButtonDkShadow
  217. RGB(223, 223, 223), // ButtonLight
  218. RGB(  0,   0,   0), // InfoText
  219. RGB(255, 255, 225), // InfoWindow
  220. }
  221. }
  222. };
  223. int GetSchemeCount()
  224. {
  225. return ARRAYSIZE(g_rgSchemeData);
  226. }
  227. void GetSchemeName(int nIndex, LPTSTR lpszName, int nLen) // JMC: HACK - You must allocate enough space
  228. {
  229. _ASSERTE(nIndex < ARRAYSIZE(g_rgSchemeData));
  230. LoadString(g_hInstDll, g_rgSchemeData[nIndex].nNameStringId, lpszName, nLen);
  231. }
  232. SCHEMEDATALOCAL &GetScheme(int nIndex)
  233. {
  234. _ASSERTE(nIndex < ARRAYSIZE(g_rgSchemeData));
  235. return g_rgSchemeData[nIndex];
  236. }
  237. #endif // READSCHEMESFROMREGISTRY
  238. //
  239. /////////////////////////////////////////////////////////////////////
  240. /////////////////////////////////////////////////////////////////////
  241. // Below this point in the file, we have the old way we use
  242. // to enumerate fonts and schemes.
  243. /////////////////////////////////////////////////////////////////////
  244. //  Old way of enumerating fonts
  245. #ifdef ENUMERATEFONTS
  246. // Global Variables
  247. static ENUMLOGFONTEX g_rgFonts[200]; // JMC: HACK - At Most 200 Fonts
  248. static int g_nFontCount = 0;
  249. static BOOL bFontsAlreadyInit = FALSE;
  250. void Font_Init();
  251. int GetFontCount()
  252. {
  253. if(!bFontsAlreadyInit)
  254. Font_Init();
  255. return g_nFontCount;
  256. }
  257. void GetFontLogFont(int nIndex, LOGFONT *pLogFont)
  258. {
  259. if(!bFontsAlreadyInit)
  260. Font_Init();
  261. *pLogFont = g_rgFonts[nIndex].elfLogFont;
  262. }
  263. int CALLBACK EnumFontFamExProc(
  264.     ENUMLOGFONTEX *lpelfe, // pointer to logical-font data
  265.     NEWTEXTMETRICEX *lpntme, // pointer to physical-font data
  266.     int FontType, // type of font
  267.     LPARAM lParam // application-defined data 
  268.    )
  269. {
  270. if(g_nFontCount>200)
  271. return 0; // JMC: HACK - Stop enumerating if more than 200 families
  272. // Don't use if we already have this font name
  273. BOOL bHave = FALSE;
  274. for(int i=0;i<g_nFontCount;i++)
  275. if(0 == lstrcmp((TCHAR *)g_rgFonts[i].elfFullName, (TCHAR *)lpelfe->elfFullName))
  276. {
  277. bHave = TRUE;
  278. break;
  279. }
  280. if(!bHave)
  281. g_rgFonts[g_nFontCount++] = *lpelfe;
  282. return 1;
  283. }
  284. void Font_Init()
  285. {
  286. // Only do the stuff in this function once.
  287. if(bFontsAlreadyInit)
  288. return;
  289. bFontsAlreadyInit = TRUE;
  290. LOGFONT lf;
  291. memset(&lf, 0, sizeof(lf));
  292. // lf.lfCharSet = DEFAULT_CHARSET;
  293. lf.lfCharSet = OEM_CHARSET;
  294. HDC hdc = GetDC(NULL);
  295. EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC)EnumFontFamExProc, 0, 0);
  296. ReleaseDC(NULL, hdc);
  297. // JMC: Make sure there is at least one font
  298. }
  299. #endif ENUMERATEFONTS
  300. //
  301. /////////////////////////////////////////////////////////////////////
  302. /////////////////////////////////////////////////////////////////////
  303. //  Old way of reading schemes from the registry
  304. #ifdef READSCHEMESFROMREGISTRY
  305. extern PTSTR s_pszColorNames[]; // JMC: HACK
  306. // Scheme data for Windows 95
  307. typedef struct {
  308.     SHORT version;
  309. //    NONCLIENTMETRICSA ncm;
  310. //    LOGFONTA lfIconTitle;
  311. BYTE rgDummy[390]; // This is the size of NONCLIENTMETRICSA and LOGFONTA in 16 bit Windows!!!
  312.     COLORREF rgb[COLOR_MAX_95_NT4];
  313. } SCHEMEDATA_95;
  314. // New scheme data for Windows 97
  315. typedef struct {
  316.     SHORT version;
  317. //    NONCLIENTMETRICSA ncm;
  318. //    LOGFONTA lfIconTitle;
  319. BYTE rgDummy[390]; // This is the size of NONCLIENTMETRICSA and LOGFONTA in 16 bit Windows!!!
  320.     COLORREF rgb[COLOR_MAX_97_NT5];
  321. } SCHEMEDATA_97;
  322. // Scheme data for Windows NT 4.0
  323. typedef struct {
  324.     SHORT version;
  325.     WORD  wDummy;               // for alignment
  326.     NONCLIENTMETRICSW ncm;
  327.     LOGFONTW lfIconTitle;
  328.     COLORREF rgb[COLOR_MAX_95_NT4];
  329. } SCHEMEDATA_NT4;
  330. // Scheme data for Windows NT 5.0
  331. typedef struct {
  332.     SHORT version;
  333.     WORD  wDummy;               // for alignment
  334.     NONCLIENTMETRICSW ncm;
  335.     LOGFONTW lfIconTitle;
  336.     COLORREF rgb[COLOR_MAX_97_NT5];
  337. } SCHEMEDATA_NT5;
  338. static SCHEMEDATALOCAL g_rgSchemeData[100]; // JMC: HACK - At Most 100 schemes
  339. static TCHAR g_rgSchemeNames[100][100];
  340. static int g_nSchemeCount = 0;
  341. static BOOL bSchemesAlreadyInit = FALSE;
  342. void Scheme_Init();
  343. int GetSchemeCount()
  344. {
  345. if(!bSchemesAlreadyInit)
  346. Scheme_Init();
  347. return g_nSchemeCount;
  348. }
  349. void GetSchemeName(int nIndex, LPTSTR lpszName, int nLen) // JMC: HACK - You must allocate enough space
  350. {
  351. if(!bSchemesAlreadyInit)
  352. Scheme_Init();
  353. _tcsncpy(lpszName, g_rgSchemeNames[i], nLen - 1);
  354. lpstName[nLen - 1] = 0; // Guarantee NULL termination
  355. }
  356. SCHEMEDATALOCAL &GetScheme(int nIndex)
  357. {
  358. if(!bSchemesAlreadyInit)
  359. Scheme_Init();
  360. return g_rgSchemeData[nIndex];
  361. }
  362. void Scheme_Init()
  363. {
  364. // Only do the stuff in this function once.
  365. if(bSchemesAlreadyInit)
  366. return;
  367. bSchemesAlreadyInit = TRUE;
  368.     HKEY hkSchemes;
  369.     DWORD dw, dwSize;
  370.     TCHAR szBuf[100];
  371. g_nSchemeCount = 0;
  372.     if (RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_LOOKSCHEMES, &hkSchemes) != ERROR_SUCCESS)
  373.         return;
  374.     for (dw=0; ; dw++)
  375.     {
  376. if(g_nSchemeCount>99)
  377. break; //JMC: HACK - At Most 100 schemes
  378.         dwSize = ARRAYSIZE(szBuf);
  379.         if (RegEnumValue(hkSchemes, dw, szBuf, &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
  380.             break;  // Bail if no more values
  381. DWORD dwType;
  382. DWORD dwSize;
  383. RegQueryValueEx(hkSchemes, szBuf, NULL, &dwType, NULL, &dwSize);
  384. if(dwType == REG_BINARY)
  385. {
  386. // Always copy the current name to the name array - if there
  387. // is an error in the data, we just won't upcount g_nSchemeCount
  388. lstrcpy(g_rgSchemeNames[g_nSchemeCount], szBuf);
  389. // Find out which type of scheme this is, and convert to the
  390. // SCHEMEDATALOCAL type
  391. switch(dwSize)
  392. {
  393. case sizeof(SCHEMEDATA_95):
  394. {
  395. SCHEMEDATA_95 sd;
  396. RegQueryValueEx(hkSchemes, szBuf, NULL, &dwType, (BYTE *)&sd, &dwSize);
  397. if(1 != sd.version)
  398. break; // We have the wrong version even though the size was correct
  399. // Copy the color information from the registry info to g_rgSchemeData
  400. g_rgSchemeData[g_nSchemeCount].nColorsUsed = COLOR_MAX_95_NT4;
  401. // Copy the color array
  402. for(int i=0;i<g_rgSchemeData[g_nSchemeCount].nColorsUsed;i++)
  403. g_rgSchemeData[g_nSchemeCount].rgb[i] = sd.rgb[i];
  404. g_nSchemeCount++;
  405. }
  406. break;
  407. case sizeof(SCHEMEDATA_NT4):
  408. {
  409. SCHEMEDATA_NT4 sd;
  410. RegQueryValueEx(hkSchemes, szBuf, NULL, &dwType, (BYTE *)&sd, &dwSize);
  411. if(2 != sd.version)
  412. break; // We have the wrong version even though the size was correct
  413. // Copy the color information from the registry info to g_rgSchemeData
  414. g_rgSchemeData[g_nSchemeCount].nColorsUsed = COLOR_MAX_95_NT4;
  415. // Copy the color array
  416. for(int i=0;i<g_rgSchemeData[g_nSchemeCount].nColorsUsed;i++)
  417. g_rgSchemeData[g_nSchemeCount].rgb[i] = sd.rgb[i];
  418. g_nSchemeCount++;
  419. }
  420. break;
  421. case sizeof(SCHEMEDATA_97):
  422. {
  423. SCHEMEDATA_97 sd;
  424. RegQueryValueEx(hkSchemes, szBuf, NULL, &dwType, (BYTE *)&sd, &dwSize);
  425. if(3 != sd.version)
  426. break; // We have the wrong version even though the size was correct
  427. // Copy the color information from the registry info to g_rgSchemeData
  428. g_rgSchemeData[g_nSchemeCount].nColorsUsed = COLOR_MAX_97_NT5;
  429. // Copy the color array
  430. for(int i=0;i<g_rgSchemeData[g_nSchemeCount].nColorsUsed;i++)
  431. g_rgSchemeData[g_nSchemeCount].rgb[i] = sd.rgb[i];
  432. g_nSchemeCount++;
  433. }
  434. break;
  435. case sizeof(SCHEMEDATA_NT5):
  436. {
  437. SCHEMEDATA_NT5 sd;
  438. RegQueryValueEx(hkSchemes, szBuf, NULL, &dwType, (BYTE *)&sd, &dwSize);
  439. if(2 != sd.version)
  440. break; // We have the wrong version even though the size was correct
  441. // Copy the color information from the registry info to g_rgSchemeData
  442. g_rgSchemeData[g_nSchemeCount].nColorsUsed = COLOR_MAX_97_NT5;
  443. // Copy the color array
  444. for(int i=0;i<g_rgSchemeData[g_nSchemeCount].nColorsUsed;i++)
  445. g_rgSchemeData[g_nSchemeCount].rgb[i] = sd.rgb[i];
  446. g_nSchemeCount++;
  447. }
  448. break;
  449. default:
  450. // We had an unknown sized structure in the registry - IGNORE IT
  451. #ifdef _DEBUG
  452. TCHAR sz[200];
  453. wsprintf(sz, __TEXT("Scheme - %s, size = %i, sizeof(95) = %i, sizeof(NT4) = %i, sizeof(97) = %i, sizeof(NT5) = %i"), szBuf, dwSize,
  454. sizeof(SCHEMEDATA_95),
  455. sizeof(SCHEMEDATA_NT4),
  456. sizeof(SCHEMEDATA_97),
  457. sizeof(SCHEMEDATA_NT5)
  458. );
  459. MessageBox(NULL, sz, NULL, MB_OK);
  460. #endif // _DEBUG
  461. break;
  462. }
  463. }
  464.     }
  465.     RegCloseKey(hkSchemes);
  466. // JMC: Verify that there are at least one schemes
  467. #if 0
  468. // JMC: HACK to write these to the clipboard
  469. TCHAR sz[20000];
  470. sz[0] = 0;
  471. for(int i=0;i<g_nSchemeCount;i++)
  472. {
  473. TCHAR sz2[100];
  474. wsprintf(sz2, g_rgSchemeNames[i]);
  475. lstrcat(sz, sz2);
  476. lstrcat(sz, __TEXT("rn"));
  477. for(int j=0;j<g_rgSchemeData[i].nColorsUsed;j++)
  478. {
  479. wsprintf(sz2, __TEXT("RGB(%3i, %3i, %3i), // %srn"),
  480. (int)GetRValue(g_rgSchemeData[i].rgb[j]),
  481. (int)GetGValue(g_rgSchemeData[i].rgb[j]),
  482. (int)GetBValue(g_rgSchemeData[i].rgb[j]),
  483. s_pszColorNames[j]);
  484. lstrcat(sz, sz2);
  485. }
  486. lstrcat(sz, __TEXT("rnrn"));
  487. }
  488. int nLen = lstrlen(sz);
  489. HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (nLen + 1) * sizeof(TCHAR));
  490. LPTSTR lpsz = (LPTSTR)GlobalLock(hGlobal);
  491. lstrcpy(lpsz, sz);
  492. GlobalUnlock(hGlobal);
  493. OpenClipboard(NULL);
  494. EmptyClipboard();
  495. #ifdef _UNICODE
  496. SetClipboardData(CF_UNICODETEXT, hGlobal);
  497. #else
  498. SetClipboardData(CF_TEXT, hGlobal);
  499. #endif
  500. CloseClipboard();
  501. #endif
  502. }
  503. #endif // READSCHEMESFROMREGISTRY
  504. //
  505. /////////////////////////////////////////////////////////////////////