2 * @file LanguageSelect.cpp
4 * @brief Implements the Language Selection dialog class (which contains the language data)
6 // ID line follows -- this is updated by SVN
7 // $Id: LanguageSelect.cpp 6499 2009-02-25 13:31:52Z kimmov $
10 #include "LanguageSelect.h"
13 #include "OptionsDef.h"
14 #include "OptionsMgr.h"
24 #include "Environment.h"
27 // Escaped character constants in range 0x80-0xFF are interpreted in current codepage
28 // Using C locale gets us direct mapping to Unicode codepoints
29 #pragma setlocale("C")
34 static char THIS_FILE[] = __FILE__;
37 /** @brief Relative path to WinMerge executable for lang files. */
38 static const TCHAR szRelativePath[] = _T("Languages");
40 static char *EatPrefix(char *text, const char *prefix);
41 static void unslash(unsigned codepage, std::string &s);
42 static HANDLE NTAPI FindFile(HANDLE h, LPCTSTR path, WIN32_FIND_DATA *fd);
45 * @brief A class holding information about language file.
50 LANGID id; /**< Language ID. */
52 static LANGID LangId(const char *lang, const char *sublang);
55 * A constructor taking a language id as parameter.
56 * @param [in] id Language ID to use.
58 LangFileInfo(LANGID id): id(id) { };
60 LangFileInfo(LPCTSTR path);
61 String GetString(LCTYPE type) const;
69 static const struct rg rg[];
73 * @brief An array holding language IDs and names.
75 const struct LangFileInfo::rg LangFileInfo::rg[] =
78 LANG_AFRIKAANS, "AFRIKAANS\0"
81 LANG_ALBANIAN, "ALBANIAN\0"
84 LANG_ARABIC, "ARABIC\0" "SAUDI_ARABIA\0"
102 LANG_ARMENIAN, "ARMENIAN\0"
105 LANG_ASSAMESE, "ASSAMESE\0"
108 LANG_AZERI, "AZERI\0" "LATIN\0"
112 LANG_BASQUE, "BASQUE\0"
115 LANG_BELARUSIAN, "BELARUSIAN\0"
118 LANG_BENGALI, "BENGALI\0"
121 LANG_BULGARIAN, "BULGARIAN\0"
124 LANG_CATALAN, "CATALAN\0"
127 LANG_CHINESE, "CHINESE\0" "TRADITIONAL\0"
134 LANG_CROATIAN, "CROATIAN\0"
137 LANG_CZECH, "CZECH\0"
140 LANG_DANISH, "DANISH\0"
143 LANG_DIVEHI, "DIVEHI\0"
146 MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), "DUTCH\0"
150 LANG_ENGLISH, "ENGLISH\0" "US\0"
165 LANG_ESTONIAN, "ESTONIAN\0"
168 LANG_FAEROESE, "FAEROESE\0"
171 LANG_FARSI, "FARSI\0"
174 LANG_FINNISH, "FINNISH\0"
177 MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH), "FRENCH\0"
185 LANG_GALICIAN, "GALICIAN\0"
188 LANG_GEORGIAN, "GEORGIAN\0"
191 MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), "GERMAN\0"
198 LANG_GREEK, "GREEK\0"
201 LANG_GUJARATI, "GUJARATI\0"
204 LANG_HEBREW, "HEBREW\0"
207 LANG_HINDI, "HINDI\0"
210 LANG_HUNGARIAN, "HUNGARIAN\0"
213 LANG_ICELANDIC, "ICELANDIC\0"
216 LANG_INDONESIAN, "INDONESIAN\0"
219 MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), "ITALIAN\0"
223 LANG_JAPANESE, "JAPANESE\0"
226 LANG_KANNADA, "KANNADA\0"
229 MAKELANGID(LANG_KASHMIRI, SUBLANG_DEFAULT), "KASHMIRI\0"
233 LANG_KAZAK, "KAZAK\0"
236 LANG_KONKANI, "KONKANI\0"
239 MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), "KOREAN\0"
242 LANG_KYRGYZ, "KYRGYZ\0"
245 LANG_LATVIAN, "LATVIAN\0"
248 MAKELANGID(LANG_LITHUANIAN, SUBLANG_LITHUANIAN), "LITHUANIAN\0"
251 LANG_MACEDONIAN, "MACEDONIAN\0"
254 LANG_MALAY, "MALAY\0" "MALAYSIA\0"
255 "BRUNEI_DARUSSALAM\0"
258 LANG_MALAYALAM, "MALAYALAM\0"
261 LANG_MANIPURI, "MANIPURI\0"
264 LANG_MARATHI, "MARATHI\0"
267 LANG_MONGOLIAN, "MONGOLIAN\0"
270 MAKELANGID(LANG_NEPALI, SUBLANG_DEFAULT), "NEPALI\0"
274 LANG_NORWEGIAN, "NORWEGIAN\0" "BOKMAL\0"
278 LANG_ORIYA, "ORIYA\0"
281 LANG_POLISH, "POLISH\0"
284 MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE), "PORTUGUESE\0"
288 LANG_PUNJABI, "PUNJABI\0"
291 LANG_ROMANIAN, "ROMANIAN\0"
294 LANG_RUSSIAN, "RUSSIAN\0"
297 LANG_SANSKRIT, "SANSKRIT\0"
300 MAKELANGID(LANG_SERBIAN, SUBLANG_DEFAULT), "SERBIAN\0"
305 LANG_SINDHI, "SINDHI\0"
308 LANG_SLOVAK, "SLOVAK\0"
311 LANG_SLOVENIAN, "SLOVENIAN\0"
314 MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), "SPANISH\0"
320 "DOMINICAN_REPUBLIC\0"
336 LANG_SWAHILI, "SWAHILI\0"
339 MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), "SWEDISH\0"
343 LANG_SYRIAC, "SYRIAC\0"
346 LANG_TAMIL, "TAMIL\0"
349 LANG_TATAR, "TATAR\0"
352 LANG_TELUGU, "TELUGU\0"
358 LANG_TURKISH, "TURKISH\0"
361 LANG_UKRAINIAN, "UKRAINIAN\0"
364 LANG_URDU, "URDU\0" "PAKISTAN\0"
368 LANG_UZBEK, "UZBEK\0" "LATIN\0"
372 LANG_VIETNAMESE, "VIETNAMESE\0"
377 * @brief Get a language ID for given language + sublanguage.
378 * @param [in] lang Language name.
379 * @param [in] sublang Sub language name.
380 * @return Language ID.
382 LANGID LangFileInfo::LangId(const char *lang, const char *sublang)
384 // binary search the array for passed in lang
386 size_t upper = countof(rg);
387 while (lower < upper)
389 size_t match = (upper + lower) >> 1;
390 int cmp = strcmp(rg[match].lang, lang);
398 LANGID baseid = rg[upper].id;
399 // figure out sublang
400 if ((baseid & ~0x3ff) && *sublang == '\0')
402 LANGID id = PRIMARYLANGID(baseid);
403 if (0 == strcmp(sublang, "DEFAULT"))
404 return MAKELANGID(id, SUBLANG_DEFAULT);
405 const char *sub = rg[upper].lang;
410 id += MAKELANGID(0, 1);
411 } while (id == baseid);
412 sub += strlen(sub) + 1;
413 if (0 == strcmp(sublang, sub))
420 * @brief A constructor taking a path to language file as parameter.
421 * @param [in] path Full path to the language file.
423 LangFileInfo::LangFileInfo(LPCTSTR path)
426 if (FILE *f = _tfopen(path, _T("r")))
429 while (fgets(buf, sizeof buf - 1, f))
433 sscanf(buf, "msgid \" LANG_ENGLISH , SUBLANG_ENGLISH_US \" %d", &i);
436 if (fgets(buf, sizeof buf, f))
438 char *lang = strstr(buf, "LANG_");
439 char *sublang = strstr(buf, "SUBLANG_");
442 strtok(lang, ",\" \t\r\n");
443 strtok(sublang, ",\" \t\r\n");
444 lang += sizeof "LANG";
445 sublang += sizeof "SUBLANG";
446 if (0 != strcmp(sublang, "DEFAULT"))
448 sublang = EatPrefix(sublang, lang);
449 if (sublang && *sublang)
450 sublang = EatPrefix(sublang, "_");
453 id = LangId(lang, sublang);
463 String LangFileInfo::GetString(LCTYPE type) const
466 if (int cch = GetLocaleInfo(id, type, 0, 0))
469 GetLocaleInfo(id, type, &*s.begin(), cch);
474 static HANDLE NTAPI FindFile(HANDLE h, LPCTSTR path, WIN32_FIND_DATA *fd)
476 if (h == INVALID_HANDLE_VALUE)
478 h = FindFirstFile(path, fd);
480 else if (fd->dwFileAttributes == INVALID_FILE_ATTRIBUTES || !FindNextFile(h, fd))
483 h = INVALID_HANDLE_VALUE;
488 /////////////////////////////////////////////////////////////////////////////
489 // CLanguageSelect dialog
491 /** @brief Default English language. */
492 const WORD wSourceLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
494 CLanguageSelect::CLanguageSelect(UINT idMainMenu, UINT idDocMenu, BOOL bReloadMenu /*=TRUE*/, BOOL bUpdateTitle /*=TRUE*/, CWnd* pParent /*=NULL*/)
495 : CDialog(CLanguageSelect::IDD, pParent)
497 , m_wCurLanguage(wSourceLangId)
498 , m_idMainMenu(idMainMenu)
499 , m_idDocMenu(idDocMenu)
501 , m_bReloadMenu(bReloadMenu)
502 , m_bUpdateTitle(bUpdateTitle)
504 SetThreadLocale(MAKELCID(m_wCurLanguage, SORT_DEFAULT));
507 void CLanguageSelect::DoDataExchange(CDataExchange* pDX)
509 CDialog::DoDataExchange(pDX);
510 //{{AFX_DATA_MAP(CLanguageSelect)
511 DDX_Control(pDX, IDC_LANGUAGE_LIST, m_ctlLangList);
515 BEGIN_MESSAGE_MAP(CLanguageSelect, CDialog)
516 //{{AFX_MSG_MAP(CLanguageSelect)
517 ON_LBN_DBLCLK(IDC_LANGUAGE_LIST, OnOK)
522 * @brief Remove prefix from the string.
523 * @param [in] text String from which to jump over prefix.
524 * @param [in] prefix Prefix string to jump over.
525 * @return String without the prefix.
526 * @note Function returns pointer to original string,
527 * it does not allocate a new string.
529 static char *EatPrefix(char *text, const char *prefix)
531 if (size_t len = strlen(prefix))
532 if (_memicmp(text, prefix, len) == 0)
538 * @brief Convert C style \\nnn, \\r, \\n, \\t etc into their indicated characters.
539 * @param [in] codepage Codepage to use in conversion.
540 * @param [in,out] s String to convert.
542 static void unslash(unsigned codepage, std::string &s)
544 char *p = &*s.begin();
577 *p = (char)strtol(r, &q, 16);
580 *p = (char)strtol(r - 1, &q, 8);
588 if ((*p & 0x80) && IsDBCSLeadByteEx(codepage, *p))
594 s.resize(p - 1 - &*s.begin());
598 * @brief Load language.file
599 * @param [in] wLangId
600 * @return TRUE on success, FALSE otherwise.
602 BOOL CLanguageSelect::LoadLanguageFile(LANGID wLangId)
604 String strPath = GetFileName(wLangId);
608 m_hCurrentDll = LoadLibrary(_T("MergeLang.dll"));
609 // There is no point in translating error messages about inoperational
610 // translation system, so go without string resources here.
611 if (m_hCurrentDll == 0)
614 AfxMessageBox(_T("Failed to load MergeLang.dll"), MB_ICONSTOP);
617 CVersionInfo viInstance = AfxGetInstanceHandle();
618 unsigned instanceVerMS = 0;
619 unsigned instanceVerLS = 0;
620 viInstance.GetFixedFileVersion(instanceVerMS, instanceVerLS);
621 CVersionInfo viResource = m_hCurrentDll;
622 unsigned resourceVerMS = 0;
623 unsigned resourceVerLS = 0;
624 viResource.GetFixedFileVersion(resourceVerMS, resourceVerLS);
625 if (instanceVerMS != resourceVerMS || instanceVerLS != resourceVerLS)
627 FreeLibrary(m_hCurrentDll);
630 AfxMessageBox(_T("MergeLang.dll version mismatch"), MB_ICONSTOP);
633 HRSRC mergepot = FindResource(m_hCurrentDll, _T("MERGEPOT"), RT_RCDATA);
637 AfxMessageBox(_T("MergeLang.dll is invalid"), MB_ICONSTOP);
640 size_t size = SizeofResource(m_hCurrentDll, mergepot);
641 const char *data = (const char *)LoadResource(m_hCurrentDll, mergepot);
645 std::vector<unsigned> lines;
648 while (const char *eol = (const char *)memchr(data, '\n', size))
650 size_t len = eol - data;
651 if (len >= sizeof buf)
656 memcpy(buf, data, len);
660 if (char *p = EatPrefix(buf, "#:"))
662 if (char *q = strchr(p, ':'))
664 int line = strtol(q + 1, &q, 10);
665 lines.push_back(line);
669 else if (EatPrefix(buf, "msgid "))
675 char *p = strchr(buf, '"');
676 char *q = strrchr(buf, '"');
677 if (std::string::size_type n = q - p)
679 ps->append(p + 1, n - 1);
684 // avoid dereference of empty vector or last vector
685 if (lines.size() > 0)
688 m_map_lineno.insert(std::make_pair(msgid, lines[0]));
689 for (unsigned *pline = &*lines.begin() ; pline <= &*(lines.end() - 1) ; ++pline)
691 unsigned line = *pline;
692 if (m_strarray.size() <= line)
693 m_strarray.resize(line + 1);
694 m_strarray[line] = msgid;
702 FILE *f = _tfopen(strPath.c_str(), _T("r"));
705 FreeLibrary(m_hCurrentDll);
709 std_tchar(ostringstream) stm;
710 stm << _T("Failed to load ") << strPath.c_str();
711 AfxMessageBox(stm.str().c_str(), MB_ICONSTOP);
720 std::string directive;
721 while (fgets(buf, sizeof buf, f))
723 if (char *p = EatPrefix(buf, "#:"))
725 if (char *q = strchr(p, ':'))
727 int line = strtol(q + 1, &q, 10);
728 lines.push_back(line);
732 else if (char *p = EatPrefix(buf, "#,"))
735 format.erase(0, format.find_first_not_of(" \t\r\n"));
736 format.erase(format.find_last_not_of(" \t\r\n") + 1);
738 else if (char *p = EatPrefix(buf, "#."))
741 directive.erase(0, directive.find_first_not_of(" \t\r\n"));
742 directive.erase(directive.find_last_not_of(" \t\r\n") + 1);
744 else if (EatPrefix(buf, "msgid "))
748 else if (EatPrefix(buf, "msgstr "))
754 char *p = strchr(buf, '"');
755 char *q = strrchr(buf, '"');
756 if (std::string::size_type n = q - p)
758 ps->append(p + 1, n - 1);
767 unslash(m_codepage, msgstr);
768 // avoid dereference of empty vector or last vector
771 for (unsigned *pline = &*lines.begin() ; pline <= &*(lines.end() - 1) ; ++pline)
773 unsigned line = *pline;
774 if (m_strarray.size() <= line)
775 m_strarray.resize(line + 1);
776 if (m_strarray[line] == msgid)
777 m_strarray[line] = msgstr;
783 if (directive == "Codepage")
785 m_codepage = strtol(msgstr.c_str(), &p, 10);
794 if (unresolved != 0 || mismatched != 0)
796 FreeLibrary(m_hCurrentDll);
799 m_map_lineno.clear();
803 std_tchar(ostringstream) stm;
804 stm << _T("Unresolved or mismatched references detected when ")
805 _T("attempting to read translations from\n") << strPath.c_str();
806 AfxMessageBox(stm.str().c_str(), MB_ICONSTOP);
814 * @brief Set UI language.
815 * @param [in] wLangId
816 * @return TRUE on success, FALSE otherwise.
818 BOOL CLanguageSelect::SetLanguage(LANGID wLangId)
822 if (m_wCurLanguage == wLangId)
824 // reset the resource handle
825 AfxSetResourceHandle(AfxGetInstanceHandle());
826 // free the existing DLL
829 FreeLibrary(m_hCurrentDll);
830 m_hCurrentDll = NULL;
833 m_map_lineno.clear();
835 if (wLangId != wSourceLangId)
837 if (LoadLanguageFile(wLangId))
838 AfxSetResourceHandle(m_hCurrentDll);
840 wLangId = wSourceLangId;
842 m_wCurLanguage = wLangId;
843 SetThreadLocale(MAKELCID(m_wCurLanguage, SORT_DEFAULT));
848 * @brief Get a language file for the specified language ID.
849 * This function gets a language file name for the given language ID. Language
850 * files are currently named as [languagename].po.
851 * @param [in] wLangId Language ID.
852 * @return Language filename, or empty string if no file for language found.
854 String CLanguageSelect::GetFileName(LANGID wLangId)
857 String path = paths_ConcatPath(env_GetProgPath(), szRelativePath);
858 String pattern = paths_ConcatPath(path, _T("*.po"));
860 HANDLE h = INVALID_HANDLE_VALUE;
861 while ((h = FindFile(h, pattern.c_str(), &ff)) != INVALID_HANDLE_VALUE)
863 filename = paths_ConcatPath(path, ff.cFileName);
864 LangFileInfo lfi = filename.c_str();
865 if (lfi.id == wLangId)
866 ff.dwFileAttributes = INVALID_FILE_ATTRIBUTES; // terminate loop
874 * @brief Check if there are language files installed.
876 * This function does as fast as possible check for installed language
877 * files. It needs to be fast since it is used in enabling/disabling
878 * GUI item(s). So the simple check we do is just find one .po file.
879 * If there is a .po file we assume we have at least one language
881 * @return TRUE if at least one lang file is found. FALSE if no lang
884 BOOL CLanguageSelect::AreLangsInstalled() const
887 String path = paths_ConcatPath(env_GetProgPath(), szRelativePath);
888 String pattern = paths_ConcatPath(path, _T("*.po"));
890 HANDLE h = INVALID_HANDLE_VALUE;
891 while ((h = FindFile(h, pattern.c_str(), &ff)) != INVALID_HANDLE_VALUE)
893 ff.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
899 /////////////////////////////////////////////////////////////////////////////
900 // CLanguageSelect commands
902 bool CLanguageSelect::TranslateString(size_t line, std::string &s) const
904 if (line > 0 && line < m_strarray.size())
906 s = m_strarray[line];
907 unsigned codepage = GetACP();
908 if (m_codepage != codepage)
910 // Attempt to convert to UI codepage
911 if (size_t len = s.length())
915 len = MultiByteToWideChar(m_codepage, 0, s.c_str(), -1, &*ws.begin(), static_cast<int>(len) + 1);
919 len = WideCharToMultiByte(codepage, 0, ws.c_str(), -1, 0, 0, 0, 0);
923 WideCharToMultiByte(codepage, 0, ws.c_str(), -1, &*s.begin(), static_cast<int>(len), 0, 0);
933 bool CLanguageSelect::TranslateString(size_t line, std::wstring &ws) const
935 if (line > 0 && line < m_strarray.size())
937 if (size_t len = m_strarray[line].length())
940 const char *msgstr = m_strarray[line].c_str();
941 len = MultiByteToWideChar(m_codepage, 0, msgstr, -1, &*ws.begin(), static_cast<int>(len) + 1);
949 bool CLanguageSelect::TranslateString(const std::string& str, String &translated_str) const
951 EngLinenoMap::const_iterator it = m_map_lineno.find(str);
952 if (it != m_map_lineno.end())
954 return TranslateString(it->second, translated_str);
956 translated_str = ucr::toTString(str);
960 void CLanguageSelect::SetIndicators(CStatusBar &sb, const UINT *rgid, int n) const
962 HGDIOBJ hf = (HGDIOBJ)sb.SendMessage(WM_GETFONT);
965 hf = dc.SelectObject(hf);
967 sb.SetIndicators(0, n);
970 int cx = ::GetSystemMetrics(SM_CXSCREEN) / 4; // default to 1/4 the screen width
971 UINT style = SBPS_STRETCH | SBPS_NOBORDERS; // first pane is stretchy
972 for (int i = 0 ; i < n ; ++i)
974 UINT id = rgid ? rgid[i] : sb.GetItemID(i);
975 if (id >= ID_INDICATOR_EXT)
977 String text = LoadString(id);
978 size_t cx = dc.GetTextExtent(text.c_str(), static_cast<int>(text.length())).cx;
979 sb.SetPaneInfo(i, id, style | SBPS_DISABLED, static_cast<int>(cx));
980 sb.SetPaneText(i, text.c_str(), FALSE);
984 sb.SetPaneInfo(i, 0, style, cx);
989 hf = dc.SelectObject(hf);
990 // Send WM_SIZE to get pane rectangles right
992 sb.GetClientRect(&rect);
993 sb.SendMessage(WM_SIZE, 0, MAKELPARAM(rect.right, rect.bottom));
996 void CLanguageSelect::TranslateMenu(HMENU h) const
998 int i = ::GetMenuItemCount(h);
1002 MENUITEMINFO mii = {0};
1003 #if(WINVER >= 0x0500)
1004 mii.cbSize = sizeof mii - sizeof HBITMAP;
1006 mii.cbSize = sizeof mii;
1008 mii.fMask = MIIM_STATE|MIIM_ID|MIIM_SUBMENU|MIIM_DATA;
1009 ::GetMenuItemInfo(h, i, TRUE, &mii);
1012 TranslateMenu(mii.hSubMenu);
1013 mii.wID = reinterpret_cast<UINT>(mii.hSubMenu);
1015 if (BCMenuData *pItemData = reinterpret_cast<BCMenuData *>(mii.dwItemData))
1017 if (LPCWSTR text = pItemData->GetWideString())
1020 swscanf(text, L"Merge.rc:%u", &line);
1022 if (TranslateString(line, s))
1023 pItemData->SetWideString(s.c_str());
1027 if (::GetMenuString(h, i, text, countof(text), MF_BYPOSITION))
1030 _stscanf(text, _T("Merge.rc:%u"), &line);
1032 if (TranslateString(line, s))
1033 ::ModifyMenu(h, i, mii.fState | MF_BYPOSITION, mii.wID, s.c_str());
1038 void CLanguageSelect::TranslateDialog(HWND h) const
1044 ::GetWindowText(h, text, countof(text));
1046 _stscanf(text, _T("Merge.rc:%u"), &line);
1048 if (TranslateString(line, s))
1049 ::SetWindowText(h, s.c_str());
1050 h = ::GetWindow(h, gw);
1055 String CLanguageSelect::LoadString(UINT id) const
1061 AfxLoadString(id, text, countof(text));
1063 _stscanf(text, _T("Merge.rc:%u"), &line);
1064 if (!TranslateString(line, s))
1070 std::wstring CLanguageSelect::LoadDialogCaption(LPCTSTR lpDialogTemplateID) const
1073 if (HINSTANCE hInst = AfxFindResourceHandle(lpDialogTemplateID, RT_DIALOG))
1075 if (HRSRC hRsrc = FindResource(hInst, lpDialogTemplateID, RT_DIALOG))
1077 if (LPCWSTR text = (LPCWSTR)LoadResource(hInst, hRsrc))
1079 // Skip DLGTEMPLATE or DLGTEMPLATEEX
1080 text += text[1] == 0xFFFF ? 13 : 9;
1081 // Skip menu name string or ordinal
1082 if (*text == (const WCHAR)-1)
1083 text += 2; // WCHARs
1086 // Skip class name string or ordinal
1087 if (*text == (const WCHAR)-1)
1088 text += 2; // WCHARs
1091 // Caption string is ahead
1093 swscanf(text, L"Merge.rc:%u", &line);
1094 if (!TranslateString(line, s))
1102 void CLanguageSelect::ReloadMenu()
1106 // set the menu of the main frame window
1107 UINT idMenu = GetDocResId();
1108 CMergeApp *pApp = dynamic_cast<CMergeApp *> (AfxGetApp());
1109 CMainFrame * pMainFrame = dynamic_cast<CMainFrame *> ((CFrameWnd*)pApp->m_pMainWnd);
1110 HMENU hNewDefaultMenu = pMainFrame->NewDefaultMenu(idMenu);
1111 HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
1112 HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
1113 if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
1115 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
1116 CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
1117 CMenu * pNewMergeMenu = CMenu::FromHandle(hNewMergeMenu);
1118 CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
1120 CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
1123 if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
1124 ((CChildFrame *)pFrame)->SetSharedMenu(hNewMergeMenu);
1125 else if (pFrame->IsKindOf(RUNTIME_CLASS(COpenFrame)))
1126 ((COpenFrame *)pFrame)->SetSharedMenu(hNewDefaultMenu);
1127 else if (pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
1128 ((CDirFrame *)pFrame)->SetSharedMenu(hNewDirMenu);
1129 pFrame = pFrame->GetNextWindow();
1132 CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
1135 if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
1136 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
1137 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
1138 pMainFrame->MDISetMenu(pNewDirMenu, NULL);
1140 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
1143 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
1145 // Don't delete the old menu
1146 // There is a bug in BCMenu or in Windows98 : the new menu does not
1147 // appear correctly if we destroy the old one
1148 // if (pOldDefaultMenu)
1149 // pOldDefaultMenu->DestroyMenu();
1150 // if (pOldMergeMenu)
1151 // pOldMergeMenu->DestroyMenu();
1153 // pOldDirMenu->DestroyMenu();
1155 // m_hMenuDefault is used to redraw the main menu when we close a child frame
1156 // if this child frame had a different menu
1157 pMainFrame->m_hMenuDefault = hNewDefaultMenu;
1158 pApp->m_pOpenTemplate->m_hMenuShared = hNewDefaultMenu;
1159 pApp->m_pDiffTemplate->m_hMenuShared = hNewMergeMenu;
1160 pApp->m_pDirTemplate->m_hMenuShared = hNewDirMenu;
1162 // force redrawing the menu bar
1163 pMainFrame->DrawMenuBar();
1170 UINT CLanguageSelect::GetDocResId()
1172 if (((CMDIFrameWnd*)AfxGetApp()->m_pMainWnd)->MDIGetActive())
1175 return m_idMainMenu;
1179 void CLanguageSelect::UpdateDocTitle()
1181 CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
1182 POSITION posTemplate = pDocManager->GetFirstDocTemplatePosition();
1183 ASSERT(posTemplate != NULL);
1185 while (posTemplate != NULL)
1187 CDocTemplate* pTemplate = pDocManager->GetNextDocTemplate(posTemplate);
1189 ASSERT(pTemplate != NULL);
1191 POSITION pos = pTemplate->GetFirstDocPosition();
1194 while ( pos != NULL )
1196 pDoc = pTemplate->GetNextDoc(pos);
1197 pDoc->SetTitle(NULL);
1198 ((CFrameWnd*)AfxGetApp()->m_pMainWnd)->OnUpdateFrameTitle(TRUE);
1205 void CLanguageSelect::OnOK()
1208 int index = m_ctlLangList.GetCurSel();
1209 if (index<0) return;
1210 //int i = m_ctlLangList.GetItemData(index);
1211 WORD lang = (WORD)m_ctlLangList.GetItemData(index); //m_wLangIds[i];
1212 if (lang != m_wCurLanguage)
1214 if (SetLanguage(lang))
1215 GetOptionsMgr()->SaveOption(OPT_SELECTED_LANGUAGE, (int)lang);
1217 theApp.UpdateCodepageModule();
1219 // Update status bar inicator texts
1220 SetIndicators(GetMainFrame()->m_wndStatusBar, 0, 0);
1222 // Update the current menu
1226 // update the title text of the document
1234 BOOL CLanguageSelect::OnInitDialog()
1236 TranslateDialog(m_hWnd);
1237 CDialog::OnInitDialog();
1239 // setup handler for resizing this dialog
1240 m_constraint.InitializeCurrentSize(this);
1241 // configure how individual controls adjust when dialog resizes
1242 m_constraint.ConstrainItem(IDC_LANGUAGE_LIST, 0, 1, 0, 1); // grows right & down
1243 m_constraint.ConstrainItem(IDCANCEL, .6, 0, 1, 0); // slides down, floats right
1244 m_constraint.ConstrainItem(IDOK, .3, 0, 1, 0); // slides down, floats right
1245 m_constraint.SubclassWnd(); // install subclassing
1246 m_constraint.LoadPosition(_T("ResizeableDialogs"), _T("LanguageSelectDlg"), false); // persist size via registry
1248 AfxGetMainWnd()->CenterWindow(this);
1250 LoadAndDisplayLanguages();
1256 * @brief Load languages available on disk, and display in list, and select current
1258 void CLanguageSelect::LoadAndDisplayLanguages()
1260 String path = paths_ConcatPath(env_GetProgPath(), szRelativePath);
1261 String pattern = paths_ConcatPath(path, _T("*.po"));
1263 HANDLE h = INVALID_HANDLE_VALUE;
1267 h == INVALID_HANDLE_VALUE
1268 ? LangFileInfo(wSourceLangId)
1269 : LangFileInfo(paths_ConcatPath(path, ff.cFileName).c_str());
1270 std_tchar(ostringstream) stm;
1271 stm << lfi.GetString(LOCALE_SLANGUAGE).c_str();
1273 stm << lfi.GetString(LOCALE_SNATIVELANGNAME|LOCALE_USE_CP_ACP).c_str();
1275 stm << lfi.GetString(LOCALE_SNATIVECTRYNAME|LOCALE_USE_CP_ACP).c_str();
1278 stm << lfi.GetString(LOCALE_SABBREVLANGNAME|LOCALE_USE_CP_ACP).c_str();
1280 stm << lfi.GetString(LOCALE_SABBREVCTRYNAME|LOCALE_USE_CP_ACP).c_str();
1283 stm << lfi.GetString(LOCALE_SENGLANGUAGE).c_str();
1285 stm << lfi.GetString(LOCALE_SENGCOUNTRY).c_str();
1287 int i = m_ctlLangList.AddString(stm.str().c_str());
1288 m_ctlLangList.SetItemData(i, lfi.id);
1289 if (lfi.id == m_wCurLanguage)
1290 m_ctlLangList.SetCurSel(i);
1291 } while ((h = FindFile(h, pattern.c_str(), &ff)) != INVALID_HANDLE_VALUE);
1295 * @brief Find DLL entry in lang_map for language for specified locale
1297 static WORD GetLangFromLocale(LCID lcid)
1299 TCHAR buff[8] = {0};
1301 if (GetLocaleInfo(lcid, LOCALE_IDEFAULTLANGUAGE, buff, countof(buff)))
1302 _stscanf(buff, _T("%4hx"), &langID);
1306 void CLanguageSelect::InitializeLanguage()
1308 ASSERT(LangFileInfo::LangId("GERMAN", "") == MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN));
1309 ASSERT(LangFileInfo::LangId("GERMAN", "DEFAULT") == MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT));
1310 ASSERT(LangFileInfo::LangId("GERMAN", "SWISS") == MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS));
1311 ASSERT(LangFileInfo::LangId("PORTUGUESE", "") == MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE));
1312 ASSERT(LangFileInfo::LangId("NORWEGIAN", "BOKMAL") == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL));
1313 ASSERT(LangFileInfo::LangId("NORWEGIAN", "NYNORSK") == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK));
1315 //TRACE(_T("%hs\n"), LangFileInfo::FileName(MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL)).c_str());
1316 //TRACE(_T("%hs\n"), LangFileInfo::FileName(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE)).c_str());
1317 //TRACE(_T("%hs\n"), LangFileInfo::FileName(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT)).c_str());
1319 WORD langID = (WORD)GetOptionsMgr()->GetInt(OPT_SELECTED_LANGUAGE);
1322 // User has set a language override
1323 SetLanguage(langID);
1326 // User has not specified a language
1327 // so look in thread locale, user locale, and then system locale for
1328 // a language that WinMerge supports
1329 WORD Lang1 = GetLangFromLocale(GetThreadLocale());
1330 if (SetLanguage(Lang1))
1332 WORD Lang2 = GetLangFromLocale(LOCALE_USER_DEFAULT);
1333 if (Lang2 != Lang1 && SetLanguage(Lang2))
1335 WORD Lang3 = GetLangFromLocale(LOCALE_SYSTEM_DEFAULT);
1336 if (Lang3 != Lang2 && Lang3 != Lang1 && SetLanguage(Lang3))