Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
FormatToolbar.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5#include "Stroika/Foundation/StroikaPreComp.h"
6
7#if WIN32
8
9DISABLE_COMPILER_MSC_WARNING_START (5054)
10#include <afxole.h>
11DISABLE_COMPILER_MSC_WARNING_END (5054)
12
13#include <atlconv.h>
14
15#endif
16
19
20#include "LedItApplication.h"
21#include "LedItResources.h"
22
23#include "FormatToolbar.h"
24
25using namespace Stroika::Foundation;
26
27#if qStroika_Foundation_Common_Platform_Windows
28
29using LocalComboBox = FormatToolbar::LocalComboBox;
30
31/*
32 ********************************************************************************
33 ******************************** FormatToolbar *********************************
34 ********************************************************************************
35 */
36
37BEGIN_MESSAGE_MAP (FormatToolbar, CToolBar)
38ON_WM_CREATE ()
39ON_WM_DESTROY ()
40ON_CBN_DROPDOWN (IDC_FONTSIZE, OnFontSizeDropDown)
41ON_CBN_KILLFOCUS (IDC_FONTNAME, OnFontNameKillFocus)
42ON_CBN_KILLFOCUS (IDC_FONTSIZE, OnFontSizeKillFocus)
43ON_CBN_SETFOCUS (IDC_FONTNAME, OnComboSetFocus)
44ON_CBN_SETFOCUS (IDC_FONTSIZE, OnComboSetFocus)
45ON_CBN_CLOSEUP (IDC_FONTNAME, OnComboCloseUp)
46ON_CBN_CLOSEUP (IDC_FONTSIZE, OnComboCloseUp)
47END_MESSAGE_MAP ()
48
49static CSize GetBaseUnits (CFont* pFont)
50{
51 ASSERT (pFont != NULL);
52 ASSERT (pFont->GetSafeHandle () != NULL);
53 CWindowDC screenDC (NULL);
54 pFont = screenDC.SelectObject (pFont);
55 TEXTMETRIC tm;
56 VERIFY (screenDC.GetTextMetrics (&tm));
57 screenDC.SelectObject (pFont);
58 // return CSize(tm.tmAveCharWidth, tm.tmHeight+tm.tmDescent);
59 return CSize (tm.tmAveCharWidth, tm.tmHeight);
60}
61
62FormatToolbar::FormatToolbar (FormatToolbarOwner& owner)
63 : fOwner (owner)
64{
65 CFont fnt;
66 fnt.Attach (GetStockObject (DEFAULT_GUI_FONT));
67 m_szBaseUnits = GetBaseUnits (&fnt);
68 LocalComboBox::m_nFontHeight = m_szBaseUnits.cy;
69}
70
71void FormatToolbar::OnUpdateCmdUI (CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
72{
73 CToolBar::OnUpdateCmdUI (pTarget, bDisableIfNoHndler);
74 // don't update combo boxes if either one has the focus
75 if (!m_comboFontName.HasFocus () && !m_comboFontSize.HasFocus ())
76 SyncToView ();
77}
78
79void FormatToolbar::SyncToView ()
80{
81 IncrementalFontSpecification fsp = fOwner.GetCurFont ();
82 if (fsp.GetFontNameSpecifier_Valid ()) {
83 m_comboFontName.MatchFont (fsp.GetFontName ().c_str ());
84 }
85 else {
86 m_comboFontName.SetTheText (_T(""));
87 }
88
89 // SetTwipSize only updates if different
90 // -1 means selection is not a single point size
91 m_comboFontSize.SetTwipSize (fsp.GetPointSize_Valid () ? fsp.GetPointSize () * 20 : -1);
92}
93
94void FormatToolbar::OnFontSizeDropDown ()
95{
96 CString str;
97 m_comboFontName.GetTheText (str);
98 CString lpszName;
99 int nIndex = m_comboFontName.FindStringExact (-1, str);
100 if (nIndex != CB_ERR) {
101 m_comboFontName.GetLBText (nIndex, lpszName);
102 }
103
104 int nSize = m_comboFontSize.GetTwipSize ();
105 if (nSize == -2) { // error
106#if 0
107 AfxMessageBox(IDS_INVALID_NUMBER, MB_OK | MB_ICONINFORMATION);
108#endif
109 nSize = m_comboFontSize.m_nTwipsLast;
110 }
111 else if ((nSize >= 0 && nSize < 20) || nSize > 32760) {
112#if 0
113 AfxMessageBox(IDS_INVALID_FONTSIZE, MB_OK | MB_ICONINFORMATION);
114#endif
115 nSize = m_comboFontSize.m_nTwipsLast;
116 }
117
118 CWindowDC screenDC (NULL);
119 m_comboFontSize.EnumFontSizes (screenDC, (lpszName.GetLength () == 0) ? NULL : LPCTSTR (lpszName));
120 m_comboFontSize.SetTwipSize (nSize);
121}
122
123void FormatToolbar::OnComboCloseUp ()
124{
125 NotifyOwner (NM_RETURN);
126}
127
128void FormatToolbar::OnComboSetFocus ()
129{
130 NotifyOwner (NM_SETFOCUS);
131}
132
133void FormatToolbar::OnFontNameKillFocus ()
134{
135 USES_CONVERSION;
136 // get the current font from the view and update
137 NotifyOwner (NM_KILLFOCUS);
138 // this will retrieve the font entered in the edit control
139 // it tries to match the font to something already present in the combo box
140 // this effectively ignores case of a font the user enters
141 // if a user enters arial, this will cause it to become Arial
142 CString str;
143 m_comboFontName.GetTheText (str); // returns "arial"
144 m_comboFontName.SetTheText (str); // selects "Arial"
145 m_comboFontName.GetTheText (str); // returns "Arial"
146
147 // if font name box is not empty
148 if (not str.IsEmpty ()) {
149 IncrementalFontSpecification fsp;
150 int nIndex = m_comboFontName.FindStringExact (-1, str);
151 if (nIndex != CB_ERR) {
152 CString name;
153 m_comboFontName.GetLBText (nIndex, name);
154 fsp.SetFontName ((LPCTSTR)name);
155 }
156 else {
157 // unknown font
158 // fsp.SetFontName (T2A((LPTSTR) (LPCTSTR)str));
159 fsp.SetFontName ((LPCTSTR)str);
160 }
161 fOwner.SetCurFont (fsp);
162 }
163}
164
165void FormatToolbar::OnFontSizeKillFocus ()
166{
167 NotifyOwner (NM_KILLFOCUS);
168 int nSize = m_comboFontSize.GetTwipSize ();
169 if (nSize == -2) {
170#if 0
171 AfxMessageBox(IDS_INVALID_NUMBER, MB_OK | MB_ICONINFORMATION);
172#endif
173 nSize = m_comboFontSize.m_nTwipsLast;
174 }
175 else if ((nSize >= 0 && nSize < 20) || nSize > 32760) {
176#if 0
177 AfxMessageBox(IDS_INVALID_FONTSIZE, MB_OK | MB_ICONINFORMATION);
178#endif
179 nSize = m_comboFontSize.m_nTwipsLast;
180 }
181 else if (nSize > 0) {
182#if 0
183 CCharFormat cf;
184 cf.dwMask = CFM_SIZE;
185 cf.yHeight = nSize;
186 SetCharFormat(cf);
187#endif
188 IncrementalFontSpecification fsp;
189 fsp.SetPointSize (static_cast<IncrementalFontSpecification::FontSize> (nSize / 20));
190 fOwner.SetCurFont (fsp);
191 }
192}
193
194int FormatToolbar::OnCreate (LPCREATESTRUCT lpCreateStruct)
195{
196 if (CToolBar::OnCreate (lpCreateStruct) == -1)
197 return -1;
198
199 CRect rect (0, 0, (3 * LF_FACESIZE * m_szBaseUnits.cx) / 2, 200);
200 if (!m_comboFontName.Create (WS_TABSTOP | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_SORT | CBS_AUTOHSCROLL | CBS_HASSTRINGS | CBS_OWNERDRAWFIXED,
201 rect, this, IDC_FONTNAME)) {
202 TRACE0 ("Failed to create fontname combo-box\n");
203 return -1;
204 }
205 m_comboFontName.LimitText (LF_FACESIZE);
206
207 rect.SetRect (0, 0, 10 * m_szBaseUnits.cx, 200);
208 if (!m_comboFontSize.Create (WS_TABSTOP | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN, rect, this, IDC_FONTSIZE)) {
209 TRACE0 ("Failed to create fontsize combo-box\n");
210 return -1;
211 }
212
213 m_comboFontSize.LimitText (4);
214 m_comboFontName.EnumFontFamiliesEx ();
215
216 static UINT BASED_CODE format[] = {
217 // same order as in the bitmap 'format.bmp'
218 ID_SEPARATOR, // font name combo box
219 ID_SEPARATOR,
220 ID_SEPARATOR, // font size combo box
221 ID_SEPARATOR, kFontStyleBoldCmd, kFontStyleItalicCmd, kFontStyleUnderlineCmd, ID_CHAR_COLOR,
222 ID_SEPARATOR, kJustifyLeftCmd, kJustifyCenterCmd, kJustifyRightCmd,
223 };
224
225 if (!LoadBitmap (IDB_FORMATBAR) || !SetButtons (format, sizeof (format) / sizeof (UINT))) {
226 TRACE0 ("Failed to create FormatBar\n");
227 return -1; // fail to create
228 }
229 SetBarStyle (GetBarStyle () | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC | CBRS_HIDE_INPLACE);
230 SetSizes (CSize (23, 22), CSize (16, 16));
231 CString str;
232 (void)str.LoadString (IDS_TITLE_FORMATBAR);
233 SetWindowText (str);
234 PositionCombos ();
235
236 return 0;
237}
238
239void FormatToolbar::PositionCombos ()
240{
241 CRect rect;
242 // make font name box same size as font size box
243 // this is necessary since font name box is owner draw
244 m_comboFontName.SetItemHeight (-1, m_comboFontSize.GetItemHeight (-1));
245
246 m_comboFontName.GetWindowRect (&rect);
247 int nHeight = rect.Height ();
248
249 m_comboFontName.GetWindowRect (&rect);
250 SetButtonInfo (0, IDC_FONTNAME, TBBS_SEPARATOR, rect.Width ());
251 GetItemRect (0, &rect); // FontName ComboBox
252 m_comboFontName.SetWindowPos (NULL, rect.left, ((rect.Height () - nHeight) / 2) + rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
253
254 m_comboFontSize.GetWindowRect (&rect);
255 SetButtonInfo (2, IDC_FONTSIZE, TBBS_SEPARATOR, rect.Width ());
256 GetItemRect (2, &rect); // FontSize ComboBox
257 m_comboFontSize.SetWindowPos (NULL, rect.left, ((rect.Height () - nHeight) / 2) + rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
258}
259
260void FormatToolbar::NotifyOwner (UINT nCode)
261{
262 NMHDR nm;
263 nm.hwndFrom = m_hWnd;
264 nm.idFrom = GetDlgCtrlID ();
265 nm.code = nCode;
266 GetOwner ()->SendMessage (WM_NOTIFY, nm.idFrom, (LPARAM)&nm);
267}
268
269/*
270 ********************************************************************************
271 ******************************** FontComboBox **********************************
272 ********************************************************************************
273 */
274using FontComboBox = FormatToolbar::FontComboBox;
275
276const int BMW = 16;
277const int BMH = 15;
278
279BEGIN_MESSAGE_MAP (FontComboBox, LocalComboBox)
280END_MESSAGE_MAP ()
281
282FontComboBox::FontComboBox ()
283{
284 VERIFY (m_bmFontType.LoadBitmap (IDB_FONTTYPE));
285}
286
287void FontComboBox::EnumFontFamiliesEx ()
288{
289 CString str;
290 GetTheText (str);
291 ResetContent ();
292 vector<SDKString> fonts = LedItApplication::Get ().GetUsableFontNames ();
293 for (vector<SDKString>::const_iterator i = fonts.begin (); i != fonts.end (); ++i) {
294 AddString ((*i).c_str ());
295 }
296 SetTheText (str);
297}
298
299BOOL CALLBACK AFX_EXPORT FontComboBox::CheckIsTrueTypeEnumFamCallback (ENUMLOGFONTEX* /*pelf*/, NEWTEXTMETRICEX* /*lpntm*/, int FontType, LPVOID pThis)
300{
301 bool* bs = (bool*)pThis;
302 if (FontType & TRUETYPE_FONTTYPE) {
303 *bs = true;
304 }
305 return 1;
306}
307
308void FontComboBox::DrawItem (LPDRAWITEMSTRUCT lpDIS)
309{
310 ASSERT (lpDIS->CtlType == ODT_COMBOBOX);
311 int id = (int)(WORD)lpDIS->itemID;
312
313 CDC* pDC = CDC::FromHandle (lpDIS->hDC);
314 CRect rc (lpDIS->rcItem);
315 if (lpDIS->itemState & ODS_FOCUS)
316 pDC->DrawFocusRect (rc);
317 int nIndexDC = pDC->SaveDC ();
318
319 CBrush brushFill;
320 if (lpDIS->itemState & ODS_SELECTED) {
321 brushFill.CreateSolidBrush (::GetSysColor (COLOR_HIGHLIGHT));
322 pDC->SetTextColor (::GetSysColor (COLOR_HIGHLIGHTTEXT));
323 }
324 else
325 brushFill.CreateSolidBrush (pDC->GetBkColor ());
326 pDC->SetBkMode (TRANSPARENT);
327 pDC->FillRect (rc, &brushFill);
328
329 CString strText;
330 GetLBText (id, strText);
331
332 bool isTrueType = false;
333 {
334 LOGFONT lf{};
335 Characters::CString::Copy (lf.lfFaceName, Memory::NEltsOf (lf.lfFaceName), static_cast<const TCHAR*> (strText));
336 CWindowDC screenDC{nullptr};
337 HDC hDC = screenDC.m_hDC;
338 ASSERT (hDC != NULL);
339 ::EnumFontFamiliesEx (hDC, &lf, (FONTENUMPROC)CheckIsTrueTypeEnumFamCallback, (LPARAM)&isTrueType, NULL);
340 }
341 if (isTrueType) {
342 CDC dc;
343 dc.CreateCompatibleDC (pDC);
344 CBitmap* pBitmap = dc.SelectObject (&m_bmFontType);
345 pDC->BitBlt (rc.left, rc.top, BMW, BMH, &dc, BMW, 0, SRCAND);
346 dc.SelectObject (pBitmap);
347 }
348
349 rc.left += BMW + 6;
350 pDC->TextOut (rc.left, rc.top, strText, strText.GetLength ());
351
352 pDC->RestoreDC (nIndexDC);
353}
354
355void FontComboBox::MeasureItem (LPMEASUREITEMSTRUCT lpMIS)
356{
357 ASSERT (lpMIS->CtlType == ODT_COMBOBOX);
358 ASSERT (m_nFontHeight > 0);
359 CRect rc;
360
361 GetWindowRect (&rc);
362 lpMIS->itemWidth = rc.Width ();
363 lpMIS->itemHeight = max (BMH, m_nFontHeight);
364}
365
366int FontComboBox::CompareItem (LPCOMPAREITEMSTRUCT lpCIS)
367{
368 ASSERT (lpCIS->CtlType == ODT_COMBOBOX);
369 int id1 = (int)(WORD)lpCIS->itemID1;
370 int id2 = (int)(WORD)lpCIS->itemID2;
371 CString str1, str2;
372 if (id1 == -1)
373 return -1;
374 if (id2 == -1)
375 return 1;
376 GetLBText (id1, str1);
377 GetLBText (id2, str2);
378 return str1.Collate (str2);
379}
380
381// find a font with the face name and charset
382void FontComboBox::MatchFont (LPCTSTR lpszName)
383{
384 int nFirstIndex = FindString (-1, lpszName);
385 if (nFirstIndex != CB_ERR) {
386 int nIndex = nFirstIndex;
387 do {
388 CString str;
389 GetLBText (nIndex, str);
390 if (lstrcmp (lpszName, str) == 0) {
391 //got a match
392 if (GetCurSel () != nIndex)
393 SetCurSel (nIndex);
394 return;
395 }
396 nIndex = FindString (nIndex, lpszName);
397 } while (nIndex != nFirstIndex);
398 // loop until found or back to first item again
399 }
400 //enter font name
401 SetTheText (lpszName, TRUE);
402}
403
404/*
405 ********************************************************************************
406 ******************************** SizeComboBox **********************************
407 ********************************************************************************
408 */
409using SizeComboBox = FormatToolbar::SizeComboBox;
410
411SizeComboBox::SizeComboBox ()
412{
413 m_nTwipsLast = 0;
414}
415
416void SizeComboBox::EnumFontSizes (CDC& dc, LPCTSTR pFontName)
417{
418 ResetContent ();
419 if (pFontName == NULL)
420 return;
421 if (pFontName[0] == NULL)
422 return;
423
424 ASSERT (dc.m_hDC != NULL);
425 m_nLogVert = dc.GetDeviceCaps (LOGPIXELSY);
426 ::EnumFontFamilies (dc.m_hDC, pFontName, (FONTENUMPROC)EnumSizeCallBack, (LPARAM)this);
427}
428
429void SizeComboBox::TwipsToPointString (LPTSTR lpszBuf, int nTwips)
430{
431 ASSERT (lpszBuf != NULL);
432 lpszBuf[0] = NULL;
433 if (nTwips >= 0) {
434 // round to nearest half point
435 nTwips = (nTwips + 5) / 10;
436 DISABLE_COMPILER_MSC_WARNING_START (4996)
437 if ((nTwips % 2) == 0)
438 _stprintf (lpszBuf, _T("%ld"), nTwips / 2);
439 else
440 _stprintf (lpszBuf, _T("%.1f"), (float)nTwips / 2.F);
441 DISABLE_COMPILER_MSC_WARNING_END (4996)
442 }
443}
444
445void SizeComboBox::SetTwipSize (int nTwips)
446{
447 if (nTwips != GetTwipSize ()) {
448 TCHAR buf[10];
449 TwipsToPointString (buf, nTwips);
450 SetTheText (buf, TRUE);
451 }
452 m_nTwipsLast = nTwips;
453}
454
455int SizeComboBox::GetTwipSize ()
456{
457 // return values
458 // -2 -- error
459 // -1 -- edit box empty
460 // >=0 -- font size in twips
461 CString str;
462 GetTheText (str);
463 LPCTSTR lpszText = str;
464
465 while (*lpszText == ' ' || *lpszText == '\t')
466 ++lpszText;
467
468 if (lpszText[0] == NULL)
469 return -1; // no text in control
470
471 double d = _tcstod (lpszText, (LPTSTR*)&lpszText);
472 while (*lpszText == ' ' || *lpszText == '\t')
473 ++lpszText;
474
475 if (*lpszText != NULL)
476 return -2; // not terminated properly
477
478 return (d < 0.) ? 0 : (int)(d * 20.);
479}
480
481BOOL CALLBACK AFX_EXPORT SizeComboBox::EnumSizeCallBack (LOGFONT FAR* /*lplf*/, LPNEWTEXTMETRIC lpntm, int FontType, LPVOID lpv)
482{
483 static int nFontSizes[] = {8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72};
484
485 SizeComboBox* pThis = (SizeComboBox*)lpv;
486 ASSERT (pThis != NULL);
487 TCHAR buf[10];
488 if ((FontType & TRUETYPE_FONTTYPE) || !((FontType & TRUETYPE_FONTTYPE) || (FontType & RASTER_FONTTYPE))) { // if truetype or vector font
489 // this occurs when there is a truetype and nontruetype version of a font
490 if (pThis->GetCount () != 0)
491 pThis->ResetContent ();
492
493 for (int i = 0; i < 16; ++i) {
494 wsprintf (buf, _T("%d"), nFontSizes[i]);
495 pThis->AddString (buf);
496 }
497 return FALSE; // don't call me again
498 }
499 // calc character height in pixels
500 pThis->InsertSize (MulDiv (lpntm->tmHeight - lpntm->tmInternalLeading, 1440, pThis->m_nLogVert));
501 return TRUE; // call me again
502}
503
504void SizeComboBox::InsertSize (int nSize)
505{
506 ASSERT (nSize > 0);
507 DWORD dwSize = (DWORD)nSize;
508 TCHAR buf[10];
509 TwipsToPointString (buf, nSize);
510 if (FindStringExact (-1, buf) == CB_ERR) {
511 int nIndex = -1;
512 int nPos = 0;
513 DWORD dw;
514 while ((dw = static_cast<DWORD> (GetItemData (nPos))) != CB_ERR) {
515 if (dw > dwSize) {
516 nIndex = nPos;
517 break;
518 }
519 ++nPos;
520 }
521 nIndex = InsertString (nIndex, buf);
522 ASSERT (nIndex != CB_ERR);
523 if (nIndex != CB_ERR)
524 SetItemData (nIndex, dwSize);
525 }
526}
527
528/*
529 ********************************************************************************
530 ******************************** LocalComboBox *********************************
531 ********************************************************************************
532 */
533using LocalComboBox = FormatToolbar::LocalComboBox;
534
535int LocalComboBox::m_nFontHeight = 0;
536
537BEGIN_MESSAGE_MAP (LocalComboBox, CComboBox)
538ON_WM_CREATE ()
539END_MESSAGE_MAP ()
540
541void LocalComboBox::GetTheText (CString& str)
542{
543 int nIndex = GetCurSel ();
544 if (nIndex == CB_ERR)
545 GetWindowText (str);
546 else
547 GetLBText (nIndex, str);
548}
549
550void LocalComboBox::SetTheText (LPCTSTR lpszText, BOOL bMatchExact)
551{
552 int idx = (bMatchExact) ? FindStringExact (-1, lpszText) : FindString (-1, lpszText);
553 SetCurSel ((idx == CB_ERR) ? -1 : idx);
554 if (idx == CB_ERR)
555 SetWindowText (lpszText);
556}
557
558BOOL LocalComboBox::LimitText (int nMaxChars)
559{
560 BOOL b = CComboBox::LimitText (nMaxChars);
561 if (b)
562 m_nLimitText = nMaxChars;
563 return b;
564}
565
566int LocalComboBox::OnCreate (LPCREATESTRUCT lpCreateStruct)
567{
568 if (CComboBox::OnCreate (lpCreateStruct) == -1)
569 return -1;
570 SendMessage (WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT));
571 return 0;
572}
573
574BOOL LocalComboBox::PreTranslateMessage (MSG* pMsg)
575{
576 if (pMsg->message == WM_KEYDOWN) {
577 FormatToolbar* pBar = (FormatToolbar*)GetParent ();
578 switch (pMsg->wParam) {
579 case VK_ESCAPE:
580 pBar->SyncToView ();
581 pBar->NotifyOwner (NM_RETURN);
582 return TRUE;
583 case VK_RETURN:
584 pBar->NotifyOwner (NM_RETURN);
585 return TRUE;
586 case VK_TAB:
587 pBar->GetNextDlgTabItem (this)->SetFocus ();
588 return TRUE;
589 case VK_UP:
590 case VK_DOWN:
591 if ((GetKeyState (VK_MENU) >= 0) && (GetKeyState (VK_CONTROL) >= 0) && !GetDroppedState ()) {
592 ShowDropDown ();
593 return TRUE;
594 }
595 }
596 }
597 return CComboBox::PreTranslateMessage (pMsg);
598}
599#endif