/* Miranda IM Country Flags Plugin Copyright (C) 2006-1007 H. Herkenrath This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program (Flags-License.txt); if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdafx.h" /************************* Bitmap Access **************************/ static HANDLE *phIconHandles; /* country number indices (same order as in flags.bmp) */ const int BitmapIndexMap[232] = { 0, 1, 7, 20, 27, 30, 31, 32, 33, 34, 36, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 81, 82, 84, 86, 90, 91, 92, 93, 94, 95, 98, 101, 102, 103, 104, 105, 106, 107, 178, 108, 109, 110, 111, 112, 113, 116, 117, 118, 121, 122, 123, 212, 213, 216, 218, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 260, 261, 263, 264, 265, 266, 267, 268, 269, 290, 291, 297, 298, 299, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 385, 386, 387, 389, 420, 421, 441, 442, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 590, 591, 592, 593, 595, 596, 597, 598, 599, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 705, 706, 708, 709, 711, 850, 852, 853, 855, 856, 880, 886, 960, 961, 962, 963, 964, 965, 966, 967, 968, 971, 972, 973, 974, 975, 976, 977, 994, 995,1141,2691,3811,4101,6101,6722 }; static int __fastcall CountryNumberToBitmapIndex(int countryNumber) { /* shared flags by multiple countries */ switch (countryNumber) { case 262: /* Reunion Island */ case 594: /* French Guiana */ case 5901: /* French Antilles */ countryNumber = 33; /* France */ break; case 120: /* Barbuda */ countryNumber = 102; /* Antigua and Barbuda */ break; case 6702: /* Tinian Island */ case 6701: /* Rota Island */ countryNumber = 670; /* Saipan Island (Northern Mariana Islands) */ break; case 115: /* Saint Kitts */ case 114: /* Nevis */ countryNumber = 1141; /* Saint Kitts and Nevis */ break; case 247: /* Ascension Island */ countryNumber = 44; /* United Kingdom */ break; case 6721: /* Australian Antarctic Territory */ countryNumber = 61; /* Australia */ break; case 5399: /* Guantanamo Bay */ countryNumber = 1; /* USA */ } /* binary search in index array */ int low = 0, i, high = _countof(BitmapIndexMap) - 1; if (countryNumber <= BitmapIndexMap[high]) while (low <= high) { i = low + ((high - low) / 2); if (BitmapIndexMap[i] == countryNumber) return i; if (countryNumber > BitmapIndexMap[i]) low = i + 1; else high = i - 1; } /* Other,Unknown,Unspecified */ return 0; } // return value needs to be released using DestroyIcon() // only operates on color icons, which isn't a problem here static HICON __fastcall ResizeIconCentered(HICON hIcon, int cx, int cy) { HICON hResIcon = nullptr; HDC hdc = CreateCompatibleDC(nullptr); if (hdc != nullptr) { ICONINFO icoi; if (GetIconInfo(hIcon, &icoi)) { BITMAP bm; if (GetObject(icoi.hbmColor, sizeof(bm), &bm) && bm.bmWidth <= cx && bm.bmHeight <= cy) { POINT pt; pt.x = (cx - bm.bmWidth) / 2; pt.y = (cy - bm.bmHeight) / 2; HBITMAP hbmPrev = (HBITMAP)SelectObject(hdc, icoi.hbmColor); if (hbmPrev != nullptr) { /* error on select? */ HBITMAP hbm = icoi.hbmColor; icoi.hbmColor = CreateCompatibleBitmap(hdc, cx, cy); if (icoi.hbmColor != nullptr) if (SelectObject(hdc, icoi.hbmColor) != nullptr) { /* error on select? */ DeleteObject(hbm); /* delete prev color (XOR) */ if (BitBlt(hdc, 0, 0, cx, cy, nullptr, 0, 0, BLACKNESS)) /* transparency: AND=0, XOR=1 */ if (DrawIconEx(hdc, pt.x, pt.y, hIcon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_IMAGE | DI_NOMIRROR)) { if (SelectObject(hdc, icoi.hbmMask) != nullptr) { /* error on select? */ hbm = icoi.hbmMask; icoi.hbmMask = CreateBitmap(cx, cy, 1, 1, nullptr); /* mono */ if (icoi.hbmMask != nullptr) if (SelectObject(hdc, icoi.hbmMask) != nullptr) { /* error on select? */ DeleteObject(hbm); /* delete prev mask (AND) */ if (BitBlt(hdc, 0, 0, cx, cy, nullptr, 0, 0, WHITENESS)) /* transparency: AND=0, XOR=1 */ if (DrawIconEx(hdc, pt.x, pt.y, hIcon, 0, 0, 0, nullptr, DI_MASK | DI_NOMIRROR)) { SelectObject(hdc, hbmPrev); hResIcon = CreateIconIndirect(&icoi); /* bitmaps must not be selected */ } } } } } SelectObject(hdc, hbmPrev); } } DeleteObject(icoi.hbmColor); DeleteObject(icoi.hbmMask); } DeleteDC(hdc); } return hResIcon; } /************************* Utils **********************************/ HICON __fastcall LoadFlagIcon(int countryNumber) { /* create identifier */ char *szCountry = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, countryNumber, 0); if (szCountry == nullptr) szCountry = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, countryNumber = 0xFFFF, 0); char szId[20]; mir_snprintf(szId, (countryNumber == 0xFFFF) ? "%s0x%X" : "%s%i", "flags_", countryNumber); /* buffer safe */ return IcoLib_GetIcon(szId); } int __fastcall CountryNumberToIndex(int countryNumber) { int nf = 0; for (int i = 0; i < nCountriesCount; ++i) { if (countries[i].id == countryNumber) return i; if (countries[i].id == 0xFFFF) nf = i; } return nf; /* Unknown */ } /************************* Services *******************************/ static INT_PTR ServiceLoadFlagIcon(WPARAM wParam, LPARAM lParam) { /* return handle */ if ((BOOL)lParam) { if (phIconHandles == nullptr) return 0; return (INT_PTR)phIconHandles[CountryNumberToIndex((int)wParam)]; } /* return icon */ return (INT_PTR)LoadFlagIcon(wParam); } static INT_PTR ServiceCreateMergedFlagIcon(WPARAM wParam, LPARAM lParam) { HICON hIcon = nullptr; /* load both icons */ HICON hLowerIcon = (HICON)ServiceLoadFlagIcon((WPARAM)lParam, 0); if (hLowerIcon == nullptr) return 0; HICON hUpperIcon = (HICON)ServiceLoadFlagIcon(wParam, 0); /* merge them */ ICONINFO icoi; if (GetIconInfo(hLowerIcon, &icoi)) { BITMAP bm; if (hUpperIcon != nullptr && GetObject(icoi.hbmColor, sizeof(bm), &bm)) { HDC hdc = CreateCompatibleDC(nullptr); if (hdc != nullptr) { POINT aptTriangle[3] = { 0 }; aptTriangle[1].y = bm.bmHeight - 1; aptTriangle[2].x = bm.bmWidth - 1; HRGN hrgn = CreatePolygonRgn(aptTriangle, _countof(aptTriangle), WINDING); if (hrgn != nullptr) { SelectClipRgn(hdc, hrgn); DeleteObject(hrgn); HBITMAP hbmPrev = (HBITMAP)SelectObject(hdc, icoi.hbmColor); if (hbmPrev != nullptr) { /* error on select? */ if (DrawIconEx(hdc, 0, 0, hUpperIcon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NOMIRROR | DI_IMAGE)) if (SelectObject(hdc, icoi.hbmMask) != nullptr) /* error on select? */ DrawIconEx(hdc, 0, 0, hUpperIcon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NOMIRROR | DI_MASK); SelectObject(hdc, hbmPrev); } } DeleteDC(hdc); } } /* create icon */ hIcon = CreateIconIndirect(&icoi); DeleteObject(icoi.hbmColor); DeleteObject(icoi.hbmMask); } return (INT_PTR)hIcon; } /************************* Misc ***********************************/ void InitIcons(void) { char szId[20]; /* register icons */ SKINICONDESC sid = {}; sid.pszName = szId; sid.cx = GetSystemMetrics(SM_CXSMICON); sid.cy = GetSystemMetrics(SM_CYSMICON); sid.flags = SIDF_SORTED; sid.section.a = "Country Flags"; /* all those flag icons do not need any transparency mask (flags are always opaque), * storing them in a large bitmap to reduce file size */ HIMAGELIST himl = ImageList_LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDB_FLAGS), sid.cx, 0, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION); if (himl != nullptr) { phIconHandles = (HANDLE*)mir_alloc(nCountriesCount * sizeof(HANDLE)); if (phIconHandles != nullptr) { for (int i = 0; i < nCountriesCount; ++i) { sid.description.a = (char*)countries[i].szName; /* create identifier */ mir_snprintf(szId, (countries[i].id == 0xFFFF) ? "%s0x%X" : "%s%i", "flags_", countries[i].id); /* buffer safe */ int index = CountryNumberToBitmapIndex(countries[i].id); /* create icon */ HICON hIcon = ImageList_GetIcon(himl, index, ILD_NORMAL); if (hIcon) { sid.hDefaultIcon = ResizeIconCentered(hIcon, sid.cx, sid.cy); DestroyIcon(hIcon); } else sid.hDefaultIcon = nullptr; index = CountryNumberToIndex(countries[i].id); phIconHandles[index] = g_plugin.addIcon(&sid); if (sid.hDefaultIcon != nullptr) DestroyIcon(sid.hDefaultIcon); } } ImageList_Destroy(himl); } /* create services */ CreateServiceFunction(MS_FLAGS_LOADFLAGICON, ServiceLoadFlagIcon); CreateServiceFunction(MS_FLAGS_CREATEMERGEDFLAGICON, ServiceCreateMergedFlagIcon); }