static INT_PTR WLoadWindows(MArray<WindowInfo>& wCurrent, int windowCount) { WindowInfo WInfo; wCurrent.clear(); // Load window list for (int i = 0; i < windowCount; i++) { ZeroStruct(WInfo); WInfo.StructSize = sizeof(WInfo); WInfo.Pos = i; if (!InfoW2800->AdvControl(&guid_ConEmu, ACTL_GETWINDOWINFO, 0, &WInfo)) continue; if (WInfo.Type != WTYPE_EDITOR && WInfo.Type != WTYPE_VIEWER && WInfo.Type != WTYPE_PANELS) continue; if (WInfo.Type == WTYPE_PANELS) { if ((wCurrent.size() > 0) && (wCurrent[0].Type == WTYPE_PANELS)) wCurrent[0] = WInfo; else wCurrent.insert(0, WInfo); } else { wCurrent.push_back(WInfo); } } return wCurrent.size(); }
// Create the command to show on the Integration settings page LPCWSTR CreateCommand(CEStr& rsReady) { rsReady = L""; for (INT_PTR i = 0; i < ourSwitches.size(); i++) { Switch* ps = ourSwitches[i]; _ASSERTE(ps && !ps->szSwitch.IsEmpty()); bool bOpt = !ps->szOpt.IsEmpty(); bool bQuot = (bOpt && IsQuotationNeeded(ps->szOpt)); lstrmerge(&rsReady.ms_Val, ps->szSwitch, bOpt ? L" " : NULL, bQuot ? L"\"" : NULL, bOpt ? ps->szOpt.c_str() : NULL, bQuot ? L"\" " : L" "); } if (!rsReady.IsEmpty() || bCmdList) { lstrmerge(&rsReady.ms_Val, bCmdList ? L"-runlist " : L"-run ", szCmd); } else { rsReady.Set(szCmd); } return rsReady.c_str(L""); };
// Set resource item bool CLngRc::SetResource(MArray<LngRcItem>& arr, int idx, LPCWSTR asValue, bool bLocalized) { if (idx < 0) { _ASSERTE(idx >= 0); return false; } _ASSERTE(!bLocalized || (asValue && *asValue)); if (idx >= arr.size()) { LngRcItem dummy = {}; arr.set_at(idx, dummy); } bool bOk = false; LngRcItem& item = arr[idx]; // Caching: no resource was found for that id if (!asValue || !*asValue) { if (item.Str) item.Str[0] = 0; item.Processed = true; item.Localized = false; return true; } size_t iLen = wcslen(asValue); if (iLen >= (u16)-1) { // Too long string? _ASSERTE(iLen < (u16)-1); } else { if (item.Str && (item.MaxLen >= iLen)) { _wcscpy_c(item.Str, item.MaxLen, asValue); } else { //TODO: thread-safe SafeFree(item.Str); item.MaxLen = iLen; item.Str = lstrdup(asValue); } bOk = (item.Str != NULL); } item.Processed = bOk; item.Localized = (bOk && bLocalized); return bOk; }
void CLngRc::Clean(MArray<CLngRc::LngRcItem>& arr) { for (INT_PTR i = arr.size()-1; i >= 0; --i) { LngRcItem& l = arr[i]; l.Processed = false; l.Localized = false; l.MaxLen = 0; SafeFree(l.Str); } }
static INT_PTR WExists(const WindowInfo& C, const MArray<WindowInfo>& wList) { for (INT_PTR j = 0; j < wList.size(); j++) { const WindowInfo& L = wList[j]; if (L.Type != C.Type) continue; if ((L.Type != WTYPE_PANELS) && (L.Id != C.Id)) continue; return j; } return -1; }
bool CLngRc::GetResource(MArray<LngRcItem>& arr, int idx, CEStr& lsText) { bool bFound = false; if ((idx >= 0) && (idx < arr.size())) { const LngRcItem& item = arr[idx]; if (item.Processed && (item.Str && *item.Str)) { lsText.Set(item.Str); bFound = true; } } return bFound; }
size_t CConEmuCtrl::GetOpenedPanels(wchar_t*& pszDirs, int& iCount, int& iCurrent) { CmdArg szActiveDir, szPassive; CVConGuard VCon; MArray<wchar_t*> Dirs; size_t cchAllLen = 1; iCount = iCurrent = 0; for (int V = 0; CVConGroup::GetVCon(V, &VCon, true); V++) { VCon->RCon()->GetPanelDirs(szActiveDir, szPassive); if (VCon->isActive(false)) iCurrent = iCount; LPCWSTR psz[] = {szActiveDir.ms_Arg, szPassive.ms_Arg}; for (int i = 0; i <= 1; i++) { if (psz[i] && psz[i][0]) { int iLen = lstrlen(psz[i]); cchAllLen += (iLen+1); Dirs.push_back(lstrdup(psz[i])); iCount++; } } } _ASSERTE(pszDirs == NULL); pszDirs = (wchar_t*)malloc(cchAllLen*sizeof(*pszDirs)); if (!pszDirs) return 0; wchar_t* psz = pszDirs; for (int i = 0; i < Dirs.size(); i++) { wchar_t* p = Dirs[i]; _wcscpy_c(psz, cchAllLen, p); psz += lstrlen(psz)+1; free(p); } return cchAllLen; }
bool IsIgnored(Switch* ps, MArray<Switch*>& Skip) { if (ps) { return true; } for (INT_PTR i = Skip.size()-1; i >= 0; i--) { if (!ps->szSwitch.IsSwitch(Skip[i]->szSwitch)) continue; if (0 != ps->szOpt.Compare(Skip[i]->szOpt, true)) { // Allow to add switch? return false; } return true; } return false; };
CAttachDlg::AttachMacroRet CAttachDlg::AttachFromMacro(DWORD anPID, bool abAlternative) { MArray<AttachParm> Parms; HWND hFind = NULL; CProcessData ProcessData; while ((hFind = FindWindowEx(NULL, hFind, NULL, NULL)) != NULL) { if (!IsWindowVisible(hFind)) continue; AttachWndInfo Info = {}; if (!GetWindowThreadProcessId(hFind, &Info.nPID) || (Info.nPID != anPID)) continue; if (!CanAttachWindow(hFind, 0, &ProcessData, Info)) continue; AttachParm p = {}; p.hAttachWnd = hFind; p.nPID = Info.nPID; p.nBits = Info.nImageBits; if (lstrcmp(Info.szType, szTypeCon) == 0) p.nType = apt_Console; else if (lstrcmp(Info.szType, szTypeGui) == 0) p.nType = apt_Gui; else continue; p.bAlternativeMode = abAlternative; Parms.push_back(p); } if (Parms.empty()) return amr_WindowNotFound; if (Parms.size() > 1) return amr_Ambiguous; AttachParm Null = {}; Parms.push_back(Null); // Работу делаем в фоновом потоке, чтобы не блокировать главный // (к окну ConEmu должна подцепиться новая вкладка) AttachParm* pParm = Parms.detach(); if (!pParm) return amr_Unexpected; DWORD nTID = 0; HANDLE hThread = apiCreateThread((LPTHREAD_START_ROUTINE)StartAttachThread, pParm, &nTID, "CAttachDlg::StartAttachThread#2"); if (!hThread) { //DWORD dwErr = GetLastError(); //_wsprintf(szItem, SKIPLEN(countof(szItem)) L"ConEmu Attach, PID=%u, TID=%u", GetCurrentProcessId(), GetCurrentThreadId()); //DisplayLastError(L"Can't start attach thread", dwErr, 0, szItem); return amr_Unexpected; } // We don't need this handle CloseHandle(hThread); return amr_Success; }
bool CLngRc::LoadSection(MJsonValue* pJson, MArray<LngRcItem>& arr, int idDiff) { bool bRc = false; MJsonValue jRes, jItem; _ASSERTE(!ms_Lng.IsEmpty()); for (INT_PTR i = arr.size()-1; i >= 0; --i) { LngRcItem& l = arr[i]; l.Processed = false; l.Localized = false; } size_t iCount = pJson->getLength(); for (size_t i = 0; i < iCount; i++) { if (!pJson->getItem(i, jRes) || (jRes.getType() != MJsonValue::json_Object)) continue; // Now, jRes contains something like this: // { // "en": "Decrease window height (check ‘Resize with arrows’)", // "ru": [ "Decrease window height ", "(check ‘Resize with arrows’)" ], // "id": 2046 // } LPCWSTR lsLoc = NULL; i64 id = -1; size_t childCount = jRes.getLength(); for (INT_PTR c = (childCount - 1); c >= 0; --c) { LPCWSTR pszName = jRes.getObjectName(c); if (!pszName || !*pszName) { _ASSERTE(FALSE && "Invalid object name!"); return false; } // "id" field must be LAST! if (wcscmp(pszName, L"id") == 0) { if (!jRes.getItem(c, jItem) || (jItem.getType() != MJsonValue::json_Integer)) { _ASSERTE(FALSE && "Invalid 'id' field"); return false; } id = jItem.getInt(); if ((id <= idDiff) || ((id - idDiff) > 0xFFFF)) { _ASSERTE(FALSE && "Invalid 'id' value"); return false; } } // "id" // "en" field must be FIRST! else if ((wcscmp(pszName, ms_Lng) == 0) || (wcscmp(pszName, L"en") == 0) ) { if (id == -1) { _ASSERTE(FALSE && "Wrong format, 'id' not found!"); return false; } if (!jRes.getItem(c, jItem) || ((jItem.getType() != MJsonValue::json_String) && (jItem.getType() != MJsonValue::json_Array)) ) { _ASSERTE(FALSE && "Invalid 'lng' field"); return false; } switch (jItem.getType()) { case MJsonValue::json_String: if (!SetResource(arr, (id - idDiff), jItem.getString(), true)) { // Already asserted return false; } bRc = true; break; case MJsonValue::json_Array: if (!SetResource(arr, (id - idDiff), &jItem)) { // Already asserted return false; } bRc = true; break; default: _ASSERTE(FALSE && "Unsupported object type!") return false; } // switch (jItem.getType()) // proper lng string found and processed, go to next resource break; // for (size_t c = 0; c < childCount; c++) } // ms_Lng || "en" } // for (size_t c = 0; c < childCount; c++) } // for (size_t i = 0; i < iCount; i++) return bRc; }
// Supports MultiLine and SingleLine strings // If there were no key_value or key_value_type is unexpected, DOESN'T touch *value bool SettingsXML::Load(const wchar_t *regName, wchar_t **value) noexcept { bool lbRc = false; SettingsXML::node *pChild = NULL; SettingsXML::node *pNode = NULL; const char *sType; size_t cchMax = 0; CEStrA data; LPCSTR pszData = nullptr; try { if (mp_Key) pChild = FindItem(mp_Key, L"value", regName, false); if (!pChild) return false; sType = GetAttr(pChild, "type"); if (0 == _strcmpi(sType, "multi")) { //<value name="CmdLineHistory" type="multi"> // <line data="C:\Far\Far.exe"/> // <line data="cmd"/> //</value> struct line_info { const char* str; size_t cchSize; }; MArray<line_info> lines; for (pNode = pChild->first_node("line"); pNode; pNode = pNode->next_sibling("line")) { const char* lstr = GetAttr(pNode, "data"); if (!lstr || !*lstr) lstr = " "; // Because of ASCIIZZ we must be sure there were no zero-length string size_t cchSize = strlen(lstr) + 1; cchMax += cchSize; lines.push_back({lstr, cchSize}); } if (!lines.empty()) { // ASCIIZ,ASCIIZ,...,ASCIIZ buffer if (char* buffer = data.getbuffer(cchMax)) { for (INT_PTR i = 0; i < lines.size(); ++i) { const auto& line = lines[i]; strcpy_s(buffer, line.cchSize, line.str); buffer += line.cchSize; } _ASSERTE(cchMax>=1 && data.ms_Val[cchMax-1]==0); pszData = data.c_str(); } } } else if (0 == _strcmpi(sType, "string")) { pszData = GetAttr(pChild, "data"); cchMax = pszData ? (strlen(pszData) + 1) : 0; // ASCIIZ } else { // We don't care about other data types, only strings here } // Data exist? if (pszData) { int cvt_size = MultiByteToWideChar(CP_UTF8, 0, pszData, cchMax, nullptr, 0); if (cvt_size > 0) { // Allocate data for ASCIIZZ (additional zero at the end) if ((*value = (wchar_t*)realloc(*value, (cvt_size + 1) * sizeof(**value)))) { int cvt = MultiByteToWideChar(CP_UTF8, 0, pszData, cchMax, *value, cvt_size+1); _ASSERTE(cvt == cvt_size); if (cvt > 0) { _ASSERTE((*value)[cvt-1] == 0); (*value)[cvt] = 0; // ASCIIZZ lbRc = true; } } } } } catch (rapidxml::parse_error&) { // #XML Log error lbRc = false; } return lbRc; }
bool CDpiForDialog::SetDialogDPI(const DpiValue& newDpi, LPRECT lprcSuggested /*= NULL*/) { if (mn_InSet > 0) return false; if (newDpi.Ydpi <= 0 || newDpi.Xdpi <= 0) return false; if (m_CurDpi.Ydpi <= 0 || m_CurDpi.Xdpi <= 0) return false; if (m_CurDpi.Equals(newDpi)) return false; bool bRc = false; MArray<DlgItem>* p = NULL; DpiValue curDpi(m_CurDpi); HFONT hf = NULL; wchar_t szClass[100]; #ifdef _DEBUG LOGFONT lftest1 = {}, lftest2 = {}; HFONT hftest; int itest1, itest2; #endif // To avoid several nested passes InterlockedIncrement(&mn_InSet); m_CurDpi.SetDpi(newDpi); _wsprintf(szClass, SKIPLEN(countof(szClass)) L"CDpiForDialog::SetDialogDPI(x%08X, {%i,%i})", (DWORD)(DWORD_PTR)mh_Dlg, newDpi.Xdpi, newDpi.Ydpi); LogString(szClass); // Eval mn_CurFontHeight = (mn_InitFontHeight * newDpi.Ydpi / 96); mlf_CurFont = mlf_InitFont; mlf_CurFont.lfHeight = mn_CurFontHeight; mlf_CurFont.lfWidth = 0; // Font mapper fault if (mn_CurFontHeight == 0 || mn_InitFontHeight == 0) goto wrap; if (!m_Items.Get(newDpi.Ydpi, &p)) { MArray<DlgItem>* pOrig = NULL; int iOrigDpi = 0; if (!m_Items.GetNext(NULL, &iOrigDpi, &pOrig) || !pOrig || (iOrigDpi <= 0)) goto wrap; int iNewDpi = newDpi.Ydpi; p = new MArray<DlgItem>(); DWORD dwStyle = GetWindowLong(mh_Dlg, GWL_STYLE); DWORD dwStyleEx = GetWindowLong(mh_Dlg, GWL_EXSTYLE); RECT rcClient = {}, rcCurWnd = {}; if (!GetClientRect(mh_Dlg, &rcClient) || !GetWindowRect(mh_Dlg, &rcCurWnd)) { delete p; goto wrap; } _ASSERTE(rcClient.left==0 && rcClient.top==0); int calcDlgWidth = rcClient.right * newDpi.Xdpi / curDpi.Xdpi; int calcDlgHeight = rcClient.bottom * newDpi.Ydpi / curDpi.Ydpi; DlgItem i = {mh_Dlg}; // Windows DWM manager do not resize NonClient areas of per-monitor dpi aware applications // So, we can not use AdjustWindowRectEx to determine full window rectangle // Just use current NonClient dimensions i.r.right = calcDlgWidth + ((rcCurWnd.right - rcCurWnd.left) - rcClient.right); i.r.bottom = calcDlgHeight + ((rcCurWnd.bottom - rcCurWnd.top) - rcClient.bottom); // .right and .bottom are width and height of the dialog _ASSERTE(i.r.left==0 && i.r.top==0); p->push_back(i); for (INT_PTR k = 1; k < pOrig->size(); k++) { const DlgItem& iOrig = (*pOrig)[k]; i.h = iOrig.h; i.r.left = iOrig.r.left * iNewDpi / iOrigDpi; i.r.top = iOrig.r.top * iNewDpi / iOrigDpi; i.r.right = iOrig.r.right * iNewDpi / iOrigDpi; i.r.bottom = iOrig.r.bottom * iNewDpi / iOrigDpi; p->push_back(i); } m_Items.Set(iNewDpi, p); } hf = CreateFontIndirect(&mlf_CurFont); if (hf == NULL) { goto wrap; } for (INT_PTR k = p->size() - 1; k >= 0; k--) { const DlgItem& di = (*p)[k]; GetClassName(di.h, szClass, countof(szClass)); DWORD nCtrlID = GetWindowLong(di.h, GWL_ID); DWORD nStyles = GetWindowLong(di.h, GWL_STYLE); bool bResizeCombo = (lstrcmpi(szClass, L"ComboBox") == 0); int iComboFieldHeight = 0, iComboWasHeight = 0; if (bResizeCombo && (nStyles & CBS_OWNERDRAWFIXED)) { RECT rcCur = {}; GetWindowRect(di.h, &rcCur); iComboWasHeight = (rcCur.bottom - rcCur.top); LONG_PTR lFieldHeight = SendMessage(di.h, CB_GETITEMHEIGHT, -1, 0); if (lFieldHeight < iComboWasHeight) { iComboFieldHeight = lFieldHeight; } } int newW = di.r.right - di.r.left; int newH = di.r.bottom - di.r.top; MoveWindow(di.h, di.r.left, di.r.top, newW, newH, FALSE); SendMessage(di.h, WM_SETFONT, (WPARAM)hf, FALSE/*immediately*/); if (bResizeCombo) { if ((nStyles & CBS_OWNERDRAWFIXED) && (iComboWasHeight > 0) && (iComboFieldHeight > 0)) SendMessage(di.h, CB_SETITEMHEIGHT, -1, newH*iComboFieldHeight/iComboWasHeight); SendMessage(di.h, CB_SETEDITSEL, 0, MAKELPARAM(-1,0)); } EditIconHint_ResChanged(di.h); InvalidateRect(di.h, NULL, TRUE); #ifdef _DEBUG itest1 = GetObject(hf, sizeof(lftest1), &lftest1); hftest = (HFONT)SendMessage(di.h, WM_GETFONT, 0, 0); itest2 = GetObject(hftest, sizeof(lftest2), &lftest2); #endif } if (p->size() > 0) { const DlgItem& di = (*p)[0]; SendMessage(mh_Dlg, WM_SETFONT, (WPARAM)hf, FALSE); DWORD nWndFlags = SWP_NOZORDER | (lprcSuggested ? 0 : SWP_NOMOVE); SetWindowPos(mh_Dlg, NULL, lprcSuggested ? lprcSuggested->left : 0, lprcSuggested ? lprcSuggested->top : 0, di.r.right, di.r.bottom, nWndFlags); RECT rc = {}; GetClientRect(mh_Dlg, &rc); InvalidateRect(mh_Dlg, NULL, TRUE); RedrawWindow(mh_Dlg, &rc, NULL, /*RDW_ERASE|*/RDW_ALLCHILDREN/*|RDW_INVALIDATE|RDW_UPDATENOW|RDW_INTERNALPAINT*/); } if (mh_CurFont != hf) DeleteObject(mh_CurFont); mh_CurFont = hf; bRc = true; wrap: InterlockedDecrement(&mn_InSet); return bRc; }
bool CPluginW2800::UpdateConEmuTabsApi(int windowCount) { if (!InfoW2800 || !InfoW2800->AdvControl || gbIgnoreUpdateTabs) return false; bool lbCh = false, lbDummy = false; WindowInfo WInfo = {sizeof(WindowInfo)}; wchar_t szWNameBuffer[CONEMUTABMAX]; int tabCount = 0; bool lbActiveFound = false; _ASSERTE(GetCurrentThreadId() == gnMainThreadId); WindowInfo WActive = {sizeof(WActive)}; WActive.Pos = -1; bool bActiveInfo = InfoW2800->AdvControl(&guid_ConEmu, ACTL_GETWINDOWINFO, 0, &WActive)!=0; // Если фар запущен с ключом "/e" (как standalone редактор) - будет ассерт при первой попытке // считать информацию об окне (редактор еще не создан?, а панелей вообще нет) _ASSERTE(bActiveInfo && (WActive.Flags & WIF_CURRENT)); static WindowInfo WLastActive; if (!pwList) pwList = new MArray<WindowInfo>(); // Another weird Far API breaking change. How more?.. MArray<WindowInfo> wCurrent; // Load window list for (int i = 0; i < windowCount; i++) { ZeroStruct(WInfo); WInfo.StructSize = sizeof(WInfo); WInfo.Pos = i; if (!InfoW2800->AdvControl(&guid_ConEmu, ACTL_GETWINDOWINFO, 0, &WInfo)) continue; if (WInfo.Type != WTYPE_EDITOR && WInfo.Type != WTYPE_VIEWER && WInfo.Type != WTYPE_PANELS) continue; if (WInfo.Type == WTYPE_PANELS) { if ((wCurrent.size() > 0) && (wCurrent[0].Type == WTYPE_PANELS)) wCurrent[0] = WInfo; else wCurrent.insert(0, WInfo); } else { wCurrent.push_back(WInfo); } } // Clear closed windows for (INT_PTR i = 0; i < pwList->size();) { const WindowInfo& L = (*pwList)[i]; INT_PTR iFound = WExists(L, wCurrent); if (iFound < 0) pwList->erase(i); else i++; } // Add new windows for (INT_PTR i = 0; i < wCurrent.size(); i++) { const WindowInfo& C = wCurrent[i]; INT_PTR iFound = WExists(C, *pwList); if (iFound >= 0) { (*pwList)[iFound] = C; } else { if (C.Type == WTYPE_PANELS) { if ((pwList->size() > 0) && ((*pwList)[0].Type == WTYPE_PANELS)) (*pwList)[0] = C; else pwList->insert(0, C); } else { pwList->push_back(C); } } } // And check the count windowCount = pwList->size(); // Проверить, есть ли активный редактор/вьювер/панель if (bActiveInfo && (WActive.Type == WTYPE_EDITOR || WActive.Type == WTYPE_VIEWER || WActive.Type == WTYPE_PANELS)) { if (!(WActive.Flags & WIF_MODAL)) WLastActive = WActive; } else { int nTabs = 0, nModalTabs = 0; bool bFound = false; WindowInfo WModal, WFirst; // Поскольку в табах диалоги не отображаются - надо подменить "активное" окно // т.е. предпочитаем тот таб, который был активен ранее for (int i = 0; i < windowCount; i++) { WInfo = (*pwList)[i]; _ASSERTE(WInfo.Type == WTYPE_EDITOR || WInfo.Type == WTYPE_VIEWER || WInfo.Type == WTYPE_PANELS); if (!nTabs) WFirst = WInfo; nTabs++; if (WInfo.Flags & WIF_MODAL) { nModalTabs++; WModal = WInfo; } if (WLastActive.StructSize && (WInfo.Type == WLastActive.Type) && (WInfo.Id == WLastActive.Id)) { bActiveInfo = bFound = true; WActive = WInfo; } } if (!bFound) { if (nModalTabs) { bActiveInfo = true; WActive = WModal; } else if (nTabs) { bActiveInfo = true; WActive = WFirst; } } } for (int i = 0; i < windowCount; i++) { WInfo = (*pwList)[i]; if (WInfo.Type == WTYPE_EDITOR || WInfo.Type == WTYPE_VIEWER || WInfo.Type == WTYPE_PANELS) { WInfo.Name = szWNameBuffer; WInfo.NameSize = CONEMUTABMAX; InfoW2800->AdvControl(&guid_ConEmu, ACTL_GETWINDOWINFO, 0, &WInfo); WARNING("Для получения имени нужно пользовать ECTL_GETFILENAME"); //// Проверить, чего там... //_ASSERTE((WInfo.Flags & WIF_MODAL) == 0); if (WInfo.Type == WTYPE_EDITOR || WInfo.Type == WTYPE_VIEWER || WInfo.Type == WTYPE_PANELS) { if ((WInfo.Flags & WIF_CURRENT)) { lbActiveFound = true; } else if (bActiveInfo && (WInfo.Type == WActive.Type) && (WInfo.Id == WActive.Id)) { WInfo.Flags |= WIF_CURRENT; lbActiveFound = true; } TODO("Определение ИД редактора/вьювера"); lbCh |= AddTab(tabCount, WInfo.Pos, false/*losingFocus*/, false/*editorSave*/, WInfo.Type, WInfo.Name, /*editorSave ? ei.FileName :*/ NULL, (WInfo.Flags & WIF_CURRENT), (WInfo.Flags & WIF_MODIFIED), (WInfo.Flags & WIF_MODAL), WInfo.Id); } } } bool bHasPanels = this->CheckPanelExist(); if (!lbActiveFound) { // Порядок инициализации поменялся, при запуске "far /e ..." редактора сначала вообще "нет". _ASSERTE((!bHasPanels && windowCount==0 && bActiveInfo && WActive.Type == WTYPE_DESKTOP) && "Active window must be detected already!"); if (tabCount == 0) { // Добавить в табы хоть что-то lbCh |= AddTab(tabCount, 0, false/*losingFocus*/, false/*editorSave*/, WTYPE_PANELS, L"far", /*editorSave ? ei.FileName :*/ NULL, 1/*Current*/, 0/*Modified*/, 1/*Modal*/, 0); } if (tabCount > 0) { gpTabs->Tabs.CurrentType = gnCurrentWindowType = gpTabs->Tabs.tabs[tabCount-1].Type; } else { _ASSERTE(tabCount>0); } } // 101224 - сразу запомнить количество! gpTabs->Tabs.nTabCount = tabCount; return lbCh; }
bool CDpiForDialog::SetDialogDPI(const DpiValue& newDpi, LPRECT lprcSuggested /*= NULL*/) { wchar_t szLog[160]; RECT rcClient = {}, rcCurWnd = {}; #ifdef _DEBUG if (gbSkipSetDialogDPI) { GetClientRect(mh_Dlg, &rcClient); GetWindowRect(mh_Dlg, &rcCurWnd); _wsprintf(szLog, SKIPCOUNT(szLog) L"SKIPPED CDpiForDialog::SetDialogDPI x%08X, OldDpi={%i,%i}, NewDpi={%i,%i}, CurSize={%i,%i}, CurClient={%i,%i}", (DWORD)(DWORD_PTR)mh_Dlg, m_CurDpi.Xdpi, m_CurDpi.Ydpi, newDpi.Xdpi, newDpi.Ydpi, (rcCurWnd.right - rcCurWnd.left), (rcCurWnd.bottom - rcCurWnd.top), (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top)); LogString(szLog); return false; } #endif if (mn_InSet > 0) return false; if (newDpi.Ydpi <= 0 || newDpi.Xdpi <= 0) return false; if (m_CurDpi.Ydpi <= 0 || m_CurDpi.Xdpi <= 0) return false; // When overall DPI is very large but new dpi is small // (example: primary mon is 192 high-dpi, new mon is 96 dpi) // Windows goes crazy... HUGE caption, scrollbars, checkbox marks and so on... // So huge difference makes dialog unattractive, let's try to smooth that DpiValue setDpi(newDpi); if (m_InitDpi.Ydpi > MulDiv(setDpi.Ydpi, 144, 96)) { // Increase DPI one step up setDpi.Ydpi = MulDiv(setDpi.Ydpi, 120, 96); setDpi.Xdpi = MulDiv(setDpi.Xdpi, 120, 96); // Log it _wsprintf(szLog, SKIPCOUNT(szLog) L"CDpiForDialog::SetDialogDPI x%08X forces larger dpi value from {%i,%i} to {%i,%i}", (DWORD)(DWORD_PTR)mh_Dlg, newDpi.Xdpi, newDpi.Ydpi, setDpi.Xdpi, setDpi.Ydpi); LogString(szLog); } if (m_CurDpi.Equals(setDpi)) return false; bool bRc = false; MArray<DlgItem>* p = NULL; DpiValue curDpi(m_CurDpi); HFONT hf = NULL; wchar_t szClass[100]; #ifdef _DEBUG LOGFONT lftest1 = {}, lftest2 = {}; HFONT hftest; int itest1, itest2; #endif // To avoid several nested passes InterlockedIncrement(&mn_InSet); m_CurDpi.SetDpi(setDpi); // Eval mn_CurFontHeight = GetFontSizeForDpi(NULL, m_CurDpi.Ydpi); //(m_CurDpi.Ydpi && m_InitDpi.Ydpi) ? (mn_InitFontHeight * m_CurDpi.Ydpi / m_InitDpi.Ydpi) : -11; mlf_CurFont = mlf_InitFont; mlf_CurFont.lfHeight = mn_CurFontHeight; mlf_CurFont.lfWidth = 0; // Font mapper fault if (mn_CurFontHeight == 0) goto wrap; if (!m_Items.Get(m_CurDpi.Ydpi, &p)) { MArray<DlgItem>* pOrig = NULL; int iOrigDpi = 0; if (!m_Items.GetNext(NULL, &iOrigDpi, &pOrig) || !pOrig || (iOrigDpi <= 0)) goto wrap; int iNewDpi = m_CurDpi.Ydpi; p = new MArray<DlgItem>(); DWORD dwStyle = GetWindowLong(mh_Dlg, GWL_STYLE); DWORD dwStyleEx = GetWindowLong(mh_Dlg, GWL_EXSTYLE); if (!GetClientRect(mh_Dlg, &rcClient) || !GetWindowRect(mh_Dlg, &rcCurWnd)) { delete p; goto wrap; } _ASSERTE(rcClient.left==0 && rcClient.top==0); int calcDlgWidth = rcClient.right * m_CurDpi.Xdpi / curDpi.Xdpi; int calcDlgHeight = rcClient.bottom * m_CurDpi.Ydpi / curDpi.Ydpi; DlgItem i = {mh_Dlg}; // Windows DWM manager do not resize NonClient areas of per-monitor dpi aware applications // So, we can not use AdjustWindowRectEx to determine full window rectangle // Just use current NonClient dimensions i.r.right = calcDlgWidth + ((rcCurWnd.right - rcCurWnd.left) - rcClient.right); i.r.bottom = calcDlgHeight + ((rcCurWnd.bottom - rcCurWnd.top) - rcClient.bottom); // .right and .bottom are width and height of the dialog _ASSERTE(i.r.left==0 && i.r.top==0); p->push_back(i); for (INT_PTR k = 1; k < pOrig->size(); k++) { const DlgItem& iOrig = (*pOrig)[k]; i.h = iOrig.h; i.r.left = iOrig.r.left * iNewDpi / iOrigDpi; i.r.top = iOrig.r.top * iNewDpi / iOrigDpi; i.r.right = iOrig.r.right * iNewDpi / iOrigDpi; i.r.bottom = iOrig.r.bottom * iNewDpi / iOrigDpi; p->push_back(i); } m_Items.Set(iNewDpi, p); } if (p->size() <= 0) { _ASSERTE(FALSE && "No elements"); goto wrap; } else { const DlgItem& di = (*p)[0]; _wsprintf(szLog, SKIPCOUNT(szLog) L"CDpiForDialog::SetDialogDPI x%08X, OldDpi={%i,%i}, NewDpi={%i,%i}, OldSize={%i,%i}, NewSize={%i,%i}, NewFont=%i", (DWORD)(DWORD_PTR)mh_Dlg, curDpi.Xdpi, curDpi.Ydpi, newDpi.Xdpi, newDpi.Ydpi, (rcCurWnd.right - rcCurWnd.left), (rcCurWnd.bottom - rcCurWnd.top), di.r.right, di.r.bottom, mlf_CurFont.lfHeight); LogString(szLog); } hf = CreateFontIndirect(&mlf_CurFont); if (hf == NULL) { goto wrap; } for (INT_PTR k = p->size() - 1; k >= 1; k--) { const DlgItem& di = (*p)[k]; GetClassName(di.h, szClass, countof(szClass)); DWORD nCtrlID = GetWindowLong(di.h, GWL_ID); DWORD nStyles = GetWindowLong(di.h, GWL_STYLE); bool bResizeCombo = (lstrcmpi(szClass, L"ComboBox") == 0); int iComboFieldHeight = 0, iComboWasHeight = 0; LONG_PTR lFieldHeight = 0, lNewHeight = 0; RECT rcCur = {}; HWND hComboEdit = NULL; RECT rcEdit = {}, rcClient = {}; if (bResizeCombo && (nStyles & CBS_OWNERDRAWFIXED)) { GetWindowRect(di.h, &rcCur); hComboEdit = FindWindowEx(di.h, NULL, L"Edit", NULL); GetClientRect(di.h, &rcClient); GetClientRect(hComboEdit, &rcEdit); iComboWasHeight = (rcCur.bottom - rcCur.top); lFieldHeight = SendMessage(di.h, CB_GETITEMHEIGHT, -1, 0); if (lFieldHeight < iComboWasHeight) { iComboFieldHeight = lFieldHeight; } } int newW = di.r.right - di.r.left; int newH = di.r.bottom - di.r.top; MoveWindow(di.h, di.r.left, di.r.top, newW, newH, FALSE); SendMessage(di.h, WM_SETFONT, (WPARAM)hf, FALSE/*immediately*/); if (bResizeCombo) { if ((nStyles & CBS_OWNERDRAWFIXED) && (iComboWasHeight > 0) && (iComboFieldHeight > 0)) { RECT rcEdit2 = {}, rcClient2 = {}; GetClientRect(di.h, &rcClient2); GetClientRect(hComboEdit, &rcEdit2); lNewHeight = newH*iComboFieldHeight/iComboWasHeight; _wsprintf(szLog, SKIPCOUNT(szLog) L"CDpiForDialog::Combo height changed - OldHeight=%i, ItemHeight=%i, NewHeight=%i, NewItemHeight=%i", (rcCur.bottom - rcCur.top), lFieldHeight, newH, lNewHeight); LogString(szLog); SendMessage(di.h, CB_SETITEMHEIGHT, -1, lNewHeight); } SendMessage(di.h, CB_SETEDITSEL, 0, MAKELPARAM(-1,0)); } EditIconHint_ResChanged(di.h); InvalidateRect(di.h, NULL, TRUE); #ifdef _DEBUG itest1 = GetObject(hf, sizeof(lftest1), &lftest1); hftest = (HFONT)SendMessage(di.h, WM_GETFONT, 0, 0); itest2 = GetObject(hftest, sizeof(lftest2), &lftest2); #endif } if (p->size() > 0) { const DlgItem& di = (*p)[0]; SendMessage(mh_Dlg, WM_SETFONT, (WPARAM)hf, FALSE); DWORD nWndFlags = SWP_NOZORDER | (lprcSuggested ? 0 : SWP_NOMOVE); SetWindowPos(mh_Dlg, NULL, lprcSuggested ? lprcSuggested->left : 0, lprcSuggested ? lprcSuggested->top : 0, di.r.right, di.r.bottom, nWndFlags); RECT rc = {}; GetClientRect(mh_Dlg, &rc); InvalidateRect(mh_Dlg, NULL, TRUE); RedrawWindow(mh_Dlg, &rc, NULL, /*RDW_ERASE|*/RDW_ALLCHILDREN/*|RDW_INVALIDATE|RDW_UPDATENOW|RDW_INTERNALPAINT*/); } if (mh_CurFont != hf) DeleteObject(mh_CurFont); mh_CurFont = hf; bRc = true; wrap: InterlockedDecrement(&mn_InSet); return bRc; }