Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Support.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cctype>
7#include <cstdarg>
8
14#include "Stroika/Foundation/Execution/Throw.h"
16
17#include "Stroika/Frameworks/Led/Config.h" // For qStroika_Foundation_Common_Platform_Windows etc defines...
18
19#if qStroika_Foundation_Common_Platform_Windows
20#include <fcntl.h>
21#include <io.h>
22#elif qStroika_FeatureSupported_XWindows
23#include <fcntl.h>
24#include <stdio.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <sys/types.h>
28#include <time.h>
29#include <unistd.h>
30#endif
31
32#include "Stroika/Frameworks/Led/Support.h"
33
34#if qUseActiveXToOpenURLs
35#include <URLMon.h>
36#endif
37
38using namespace Stroika::Foundation;
40using namespace Stroika::Frameworks;
41using namespace Stroika::Frameworks::Led;
42
43#if !qTargetPlatformSDKUseswchar_t
44SDKString Led::Led_tString2SDKString (const Led_tString& s)
45{
46 return CodeCvt<wchar_t>{locale{}}.String2Bytes<SDKString> (span<const wchar_t>{s});
47}
48#endif
49
50/*
51@METHOD: Led_ANSIString2tString
52@DESCRIPTION: <p></p>
53*/
54Led_tString Led::Led_ANSIString2tString (const string& s)
55{
56 // Up until Stroika v3.0d2 (and maybe for a while after) - this converted from CP_ACP, despite being called 'ANSI' - I htink because I was
57 // once confused about the difference between these two --LGP 2023-07-27
58 return CodeCvt<Led_tChar>{locale{}}.Bytes2String<Led_tString> (as_bytes (span{s}));
59}
60
61/*
62@METHOD: Led_tString2ANSIString
63@DESCRIPTION: <p></p>
64*/
65string Led::Led_tString2ANSIString (const Led_tString& s)
66{
67 // Up until Stroika v3.0d2 (and maybe for a while after) - this converted from CP_ACP, despite being called 'ANSI' - I htink because I was
68 // once confused about the difference between these two --LGP 2023-07-27
69 return CodeCvt<Led_tChar>{locale{}}.String2Bytes<string> (span{s});
70}
71
72/*
73@METHOD: Led_BeepNotify
74@DESCRIPTION: <p>Make an audible beep on the users terminal. Used as a simple warning
75 mechanism (like for typing bad characters).</p>
76 <p>NB: For X-Windows only, this function invokes the private gBeepNotifyCallBackProc
77 callback procedure to handle the beeping.
78 This is because with X-Windows, the beep callback requires data (XDisplay) we don't have at this level.</p>
79*/
80void Led::Led_BeepNotify ()
81{
82#if qStroika_Foundation_Common_Platform_Windows
83 ::MessageBeep (MB_OK);
84#elif qStroika_FeatureSupported_XWindows
85 if (gBeepNotifyCallBackProc != nullptr) {
86 (gBeepNotifyCallBackProc) ();
87 }
88#endif
89}
90
91#if qStroika_FeatureSupported_XWindows
92static unsigned long sLastXWindowsEventTime;
93static double sLastEventReferenceTime;
94static double Led::GetThisMachineCurTime ()
95{
96 struct timeval tv;
97 memset (&tv, 0, sizeof (tv));
98 Verify (::gettimeofday (&tv, nullptr) == 0);
99 Assert (tv.tv_usec < 1000000);
100 double t = static_cast<double> (tv.tv_sec) + (tv.tv_usec / 1000000.0);
101 return t;
102}
103#endif
104
105#if qStroika_FeatureSupported_XWindows
106/*
107@METHOD: gBeepNotifyCallBackProc
108@DESCRIPTION: <p>X-Windows specific magic. See @'Led_BeepNotify'.</p>
109*/
110void (*Led::gBeepNotifyCallBackProc) () = nullptr;
111
112/*
113@METHOD: SyncronizeLedXTickCount
114@DESCRIPTION: <p>X-Windows specific magic. See @'Time::GetTickCount'.</p>
115*/
116void Led::SyncronizeLedXTickCount (unsigned long xTickCount)
117{
118 sLastEventReferenceTime = GetThisMachineCurTime ();
119 sLastXWindowsEventTime = xTickCount;
120}
121
122/*
123@METHOD: LedTickCount2XTime
124@DESCRIPTION: <p>X-Windows specific. See also @'SyncronizeLedXTickCount' and @'Time::GetTickCount'. Maps Time::GetTickCount ()
125 result to the sort of time value you can stick into an XEvent record.</p>
126*/
127unsigned long Led::LedTickCount2XTime (float ledTickCount)
128{
129 return static_cast<unsigned long> (ledTickCount * 1000.0f);
130}
131#endif
132
133/*
134@METHOD: Led_GetDoubleClickTime
135@DESCRIPTION: <p>Returns the amount of time (in seconds) between clicks which the OS deems should be interpretted
136 as a double click.</p>
137*/
138Time::DurationSeconds Led::Led_GetDoubleClickTime ()
139{
140#if qStroika_Foundation_Common_Platform_Windows
141 return Time::DurationSeconds{float (::GetDoubleClickTime ()) / 1000.0f};
142#else
143 return 0.25s; // SAME AS DOUBLE_CLICK_TIME FROM gdkevents.c
144#endif
145}
146
147int Led::Led_tStrniCmp (const Led_tChar* l, const Led_tChar* r, size_t n)
148{
149 using Characters::String;
150 RequireNotNull (l);
151 RequireNotNull (r);
152 auto result =
153 String::ThreeWayComparer{eCaseInsensitive}(Characters::String{l}.SafeSubString (0, n), Characters::String{r}.SafeSubString (0, n));
154 if (result == strong_ordering::equal)
155 return 0;
156 if (result == strong_ordering::less)
157 return -1;
158 if (result == strong_ordering::greater)
159 return 1;
161 return 0;
162}
163
164int Led::Led_tStriCmp (const Led_tChar* l, const Led_tChar* r)
165{
166 RequireNotNull (l);
167 RequireNotNull (r);
168 auto result = String::ThreeWayComparer{eCaseInsensitive}(Characters::String{l}, Characters::String{r});
169 if (result == strong_ordering::equal)
170 return 0;
171 if (result == strong_ordering::less)
172 return -1;
173 if (result == strong_ordering::greater)
174 return 1;
176 return 0;
177}
178
179/*
180@METHOD: Led_SkrunchOutSpecialChars
181@DESCRIPTION: <p>This function is useful to remove embedded NUL-characters from a string, or
182 any other such undesirable characters. Returns NEW length of string (after removals).
183 Removes in place.</p>
184*/
185size_t Led::Led_SkrunchOutSpecialChars (Led_tChar* text, size_t textLen, Led_tChar charToRemove)
186{
187 size_t charsSkipped = 0;
188 Led_tChar* end = text + textLen;
189 for (Led_tChar* p = text; p + charsSkipped < end;) {
190 if (*(p + charsSkipped) == charToRemove) {
191 ++charsSkipped;
192 continue;
193 }
194 *p = *(p + charsSkipped);
195 ++p;
196 }
197 return textLen - charsSkipped;
198}
199
200#if qStroika_Frameworks_Led_SupportClipboard
201/*
202 ********************************************************************************
203 ************************** Led_ClipboardObjectAcquire **************************
204 ********************************************************************************
205 */
206
207Led_ClipboardObjectAcquire::Led_ClipboardObjectAcquire (Led_ClipFormat clipType)
208 :
209#if qStroika_Foundation_Common_Platform_Windows
210 fOSClipHandle (nullptr)
211 ,
212#endif
213 fLockedData (nullptr)
214{
215#if qStroika_Foundation_Common_Platform_Windows
216 // perhaps rewrite to use exceptions, but for now - when no cliptype avail - set flag so GoodClip() method can check -
217 // just cuz thats what surounding code seems to expect - LGP 980617
218 fOSClipHandle = ::GetClipboardData (clipType);
219#if 0
220 DWORD errResult = ::GetLastError (); // could be helpful for debugging if above fails... - Maybe I should do ThrowIfErrorHRESULT().... - LGP 2000/04/26
221#endif
222 if (fOSClipHandle != nullptr) {
223 fLockedData = ::GlobalLock (fOSClipHandle);
224 }
225#endif
226}
227#endif
228
229#if qStroika_Foundation_Common_Platform_Windows
230/*
231 ********************************************************************************
232 **************************** VariantArrayPacker ********************************
233 ********************************************************************************
234 */
235VariantArrayPacker::VariantArrayPacker (VARIANT* v, VARTYPE vt, size_t nElts)
236 : fSafeArrayVariant{v}
237{
238 RequireNotNull (v);
239 // assumes passed in variant is CONSTRUCTED (initied) - but not necesarily having the right type
240 ::VariantClear (fSafeArrayVariant);
241 fSafeArrayVariant->parray = ::SafeArrayCreateVector (vt, 0, static_cast<ULONG> (nElts));
242 Execution::ThrowIfNull (fSafeArrayVariant->parray);
243 fSafeArrayVariant->vt = VT_ARRAY | vt;
244 ThrowIfErrorHRESULT (::SafeArrayAccessData (fSafeArrayVariant->parray, &fPtr));
245}
246
247VariantArrayPacker::~VariantArrayPacker ()
248{
249 AssertNotNull (fSafeArrayVariant);
250 AssertNotNull (fSafeArrayVariant->parray);
251 ::SafeArrayUnaccessData (fSafeArrayVariant->parray);
252}
253
254void* VariantArrayPacker::PokeAtData () const
255{
256 return fPtr;
257}
258
259/*
260 ********************************************************************************
261 ***************************** VariantArrayUnpacker *****************************
262 ********************************************************************************
263 */
264VariantArrayUnpacker::VariantArrayUnpacker (const VARIANT& v)
265 : fSafeArray{v.parray}
266{
267 if (not(v.vt & VT_ARRAY) or fSafeArray == nullptr) {
268 throw E_INVALIDARG;
269 }
270 if ((v.vt & ~VT_ARRAY) != GetArrayElementType ()) {
271 // I THINK this is right??? - LGP 2003-06-12 - but I'm not sure this is much reason to check/??
272 throw E_INVALIDARG;
273 }
274 void* useP = nullptr;
275 ThrowIfErrorHRESULT (::SafeArrayAccessData (fSafeArray, &useP));
276 fPtr = useP;
277}
278
279VariantArrayUnpacker::~VariantArrayUnpacker ()
280{
281 AssertNotNull (fSafeArray);
282 ::SafeArrayUnaccessData (fSafeArray);
283}
284
285const void* VariantArrayUnpacker::PeekAtData () const
286{
287 return fPtr;
288}
289
290VARTYPE VariantArrayUnpacker::GetArrayElementType () const
291{
292 AssertNotNull (fSafeArray);
293 VARTYPE vt = VT_EMPTY;
294 ThrowIfErrorHRESULT (::SafeArrayGetVartype (fSafeArray, &vt));
295 return vt;
296}
297
298size_t VariantArrayUnpacker::GetLength () const
299{
300 AssertNotNull (fSafeArray);
301 long lb = 0;
302 long ub = 0;
303 ThrowIfErrorHRESULT (::SafeArrayGetLBound (fSafeArray, 1, &lb));
304 ThrowIfErrorHRESULT (::SafeArrayGetUBound (fSafeArray, 1, &ub));
305 return ub - lb + 1;
306}
307#endif
308
309#if qStroika_Foundation_Common_Platform_Windows
310/*
311 ********************************************************************************
312 **************************** CreateSafeArrayOfBSTR *****************************
313 ********************************************************************************
314 */
315
316/*
317@METHOD: CreateSafeArrayOfBSTR
318@DESCRIPTION: <p>Overloaded method to create a VARIANT record containing a safe-array of BSTRs.</p>
319*/
320VARIANT Led::CreateSafeArrayOfBSTR (const wchar_t* const* strsStart, const wchar_t* const* strsEnd)
321{
322 size_t nElts = strsEnd - strsStart;
323 VARIANT result;
324 ::VariantInit (&result);
325 try {
326 const bool kEncodeAsVariant = false; // neither seems to work with javascript test code in MSIE, but both work
327 // with my C++-based unpacking code...
328 if (kEncodeAsVariant) {
329 VariantArrayPacker packer (&result, VT_VARIANT, nElts);
330 VARIANT* vptr = reinterpret_cast<VARIANT*> (packer.PokeAtData ());
331 for (size_t i = 0; i < nElts; ++i) {
332 ::VariantInit (&vptr[i]);
333 Execution::ThrowIfNull (vptr[i].bstrVal = ::SysAllocString (strsStart[i]));
334 vptr[i].vt = VT_VARIANT;
335 }
336 }
337 else {
338 VariantArrayPacker packer (&result, VT_BSTR, nElts);
339 BSTR* bptr = reinterpret_cast<BSTR*> (packer.PokeAtData ());
340 for (size_t i = 0; i < nElts; ++i) {
341 Execution::ThrowIfNull (bptr[i] = ::SysAllocString (strsStart[i]));
342 }
343 }
344 }
345 catch (...) {
346 ::VariantClear (&result);
347 throw;
348 }
349 return result;
350}
351
352VARIANT Led::CreateSafeArrayOfBSTR (const vector<const wchar_t*>& v)
353{
354 return CreateSafeArrayOfBSTR (Traversal::Iterator2Pointer (v.begin ()), Traversal::Iterator2Pointer (v.end ()));
355}
356
357VARIANT Led::CreateSafeArrayOfBSTR (const vector<wstring>& v)
358{
359 vector<const wchar_t*> tmp;
360 for (auto i = v.begin (); i != v.end (); ++i) {
361 // I hope (assure?) this doesn't create temporaries and take .c_str () of temporaries
362 tmp.push_back ((*i).c_str ());
363 }
364 return CreateSafeArrayOfBSTR (tmp);
365}
366
367/*
368 ********************************************************************************
369 ***************** UnpackVectorOfStringsFromVariantArray ************************
370 ********************************************************************************
371 */
372vector<wstring> Led::UnpackVectorOfStringsFromVariantArray (const VARIANT& v)
373{
374 vector<wstring> result;
375 VariantArrayUnpacker up (v);
376 size_t nElts = up.GetLength ();
377 switch (up.GetArrayElementType ()) {
378 case VT_BSTR: {
379 const BSTR* bArray = reinterpret_cast<const BSTR*> (up.PeekAtData ());
380 for (size_t i = 0; i < nElts; ++i) {
381 result.push_back (bArray[i]);
382 }
383 } break;
384 case VT_VARIANT: {
385 const VARIANT* vArray = reinterpret_cast<const VARIANT*> (up.PeekAtData ());
386 for (size_t i = 0; i < nElts; ++i) {
387 result.push_back (vArray[i].bstrVal);
388 }
389 } break;
390 default:
391 throw (DISP_E_BADVARTYPE);
392 }
393 return result;
394}
395#endif
396
397#if qStroika_Foundation_Common_Platform_Windows
398/*
399@METHOD: DumpSupportedInterfaces
400@DESCRIPTION: <p>@'qStroika_Foundation_Common_Platform_Windows' only</p>
401 <p>Helpful COM debugging utility which dumps to the debugger window all the interfaces
402 supported by a given COM object. The arguments 'objectName' and 'levelPrefix' can be nullptr (optional).
403 </p>
404*/
405void Led::DumpSupportedInterfaces (IUnknown* obj, const char* objectName, const char* levelPrefix)
406{
407 {
408 string label = objectName == nullptr ? string{} : Characters::CString::Format (" (named '%s')", objectName);
409 string msg = Characters::CString::Format ("Dumping interfaces for object%s at %p:\n", label.c_str (), obj);
410 ::OutputDebugStringA (msg.c_str ());
411
412 if (levelPrefix == nullptr) {
413 levelPrefix = "\t";
414 }
415 }
416
417 /*
418 * All interfaces are stored in the registry. Lookup each one, and do a query-interface
419 * with each on the given object to see what interfaces it contains.
420 */
421 HKEY interfaceKey = nullptr;
422 HKEY iKey = nullptr;
423 BSTR tmpStr = nullptr;
424 IUnknown* test = nullptr;
425 try {
426 if (::RegOpenKeyEx (HKEY_CLASSES_ROOT, _T ("Interface"), 0, KEY_READ, &interfaceKey) != ERROR_SUCCESS) {
427 throw GetLastError ();
428 }
429 for (DWORD i = 0;; ++i) {
430 char subKey[MAX_PATH];
431 DWORD subKeySize = sizeof (subKey);
432 if (::RegEnumKeyExA (interfaceKey, i, subKey, &subKeySize, 0, 0, 0, 0) == ERROR_SUCCESS) {
433 try {
434 CLSID iid;
435 (void)::memset (&iid, 0, sizeof (iid));
436 tmpStr = ::SysAllocString (String::FromNarrowSDKString (subKey).As<wstring> ().c_str ());
437 ThrowIfErrorHRESULT (::CLSIDFromString (tmpStr, &iid));
438 if (tmpStr != nullptr) {
439 ::SysFreeString (tmpStr);
440 tmpStr = nullptr;
441 }
442 if (SUCCEEDED (obj->QueryInterface (iid, reinterpret_cast<void**> (&test)))) {
443 // dump that obj interface
444 Assert (iKey == nullptr);
445 if (::RegOpenKeyExA (interfaceKey, subKey, 0, KEY_READ, &iKey) != ERROR_SUCCESS) {
446 throw GetLastError ();
447 }
448 DWORD dwType = 0;
449 TCHAR interfaceNameBuf[1024];
450 DWORD interfaceNameBufSize = sizeof (interfaceNameBuf);
451 if (::RegQueryValueEx (iKey, _T(""), nullptr, &dwType, reinterpret_cast<LPBYTE> (interfaceNameBuf),
452 &interfaceNameBufSize) == ERROR_SUCCESS) {
453 OutputDebugStringA (levelPrefix);
454 OutputDebugString (interfaceNameBuf);
455 OutputDebugStringA ("\n");
456 }
457 if (iKey != nullptr) {
458 ::CloseHandle (iKey);
459 iKey = nullptr;
460 }
461 }
462 if (test != nullptr) {
463 test->Release ();
464 test = nullptr;
465 }
466 }
467 catch (...) {
468 OutputDebugStringA (levelPrefix);
469 OutputDebugStringA ("ERROR LOOKING UP INTERFACE - IGNORING");
470 OutputDebugStringA ("\n");
471 }
472 }
473 else {
474 break; // end of list of interfaces?
475 }
476 }
477 if (test != nullptr) {
478 test->Release ();
479 test = nullptr;
480 }
481 if (tmpStr != nullptr) {
482 ::SysFreeString (tmpStr);
483 tmpStr = nullptr;
484 }
485 if (interfaceKey != nullptr) {
486 ::CloseHandle (interfaceKey);
487 interfaceKey = nullptr;
488 }
489 }
490 catch (...) {
491 if (iKey != nullptr) {
492 ::CloseHandle (iKey);
493 iKey = nullptr;
494 }
495 if (interfaceKey != nullptr) {
496 ::CloseHandle (interfaceKey);
497 interfaceKey = nullptr;
498 }
499 if (tmpStr != nullptr) {
500 ::SysFreeString (tmpStr);
501 tmpStr = nullptr;
502 }
503 if (test != nullptr) {
504 test->Release ();
505 test = nullptr;
506 }
507 }
508}
509
510/*
511@METHOD: DumpObjectsInIterator
512@DESCRIPTION: <p>@'qStroika_Foundation_Common_Platform_Windows' only</p>
513 <p>Helpful COM debugging utility which dumps to the debugger window all the subobjects of a given COM object,
514 along with the interfaces they supoport (see also @'DumpSupportedInterfaces'). The arguments
515 'iteratorName' and 'levelPrefix' can be nullptr (optional).
516 </p>
517*/
518void Led::DumpObjectsInIterator (IEnumUnknown* iter, const char* iteratorName, const char* levelPrefix)
519{
520 {
521 string label = iteratorName == nullptr ? string{} : Characters::CString::Format (" (named '%s')", iteratorName);
522 string msg = Characters::CString::Format ("Dumping objects (and their interface names) for iterator%s at %p\n", label.c_str (), iter);
523 ::OutputDebugStringA (msg.c_str ());
524 if (levelPrefix == nullptr) {
525 levelPrefix = "\t";
526 }
527 }
528 if (iter == nullptr) {
529 ::OutputDebugStringA (levelPrefix);
530 ::OutputDebugStringA ("<<nullptr ENUM POINTER>>\n"); // try DumpSupportedInterfaces to see what interfaces your enum interface supports!
531 return;
532 }
533 IUnknown* nextObj = nullptr;
534 for (size_t i = 0; SUCCEEDED (iter->Next (1, &nextObj, nullptr)); ++i) {
535 char nameBuf[1024];
536 (void)snprintf (nameBuf, Memory::NEltsOf (nameBuf), "obj#%d", static_cast<int> (i));
537 char levelPrefixBuf[1024];
538 Assert (::strlen (levelPrefix) < sizeof (levelPrefixBuf) / 2); // assert MUCH less
539 Characters::CString::Copy (levelPrefixBuf, Memory::NEltsOf (levelPrefixBuf), levelPrefix);
540 CString::Cat (levelPrefixBuf, Memory::NEltsOf (levelPrefixBuf), "\t");
541 DumpSupportedInterfaces (nextObj, nameBuf, levelPrefixBuf);
542 AssertNotNull (nextObj);
543 nextObj->Release ();
544 nextObj = nullptr;
545 }
546 Assert (nextObj == nullptr);
547}
548#endif
549
550/*
551 ********************************************************************************
552 *************************************** Led_URLD *******************************
553 ********************************************************************************
554 */
555Led_URLD::Led_URLD (const char* url, const char* title)
556 : fData ()
557{
558 RequireNotNull (url);
559 RequireNotNull (title);
560 size_t urlLen = ::strlen (url);
561 size_t titleLen = ::strlen (title);
562
563 size_t resultURLDLen = (urlLen + 1 + titleLen + 1);
564 fData.resize (resultURLDLen, '\0');
565
566 char* data = &fData.front ();
567 Characters::CString::Copy (data, resultURLDLen, url);
568 CString::Cat (data, fData.size () - 1, "\r");
569 CString::Cat (data, fData.size () - 1, title);
570}
571
572Led_URLD::Led_URLD (const void* urlData, size_t nBytes)
573 : fData ()
574{
575 // assure a validly formatted Led_URLD - regardless of input
576 for (size_t i = 0; i < nBytes; i++) {
577 char c = ((char*)urlData)[i];
578 fData.push_back (c);
579 if (c == '\0') {
580 break;
581 }
582 }
583 // make sure NUL-terminated...
584 if (fData.size () == 0 or fData.back () != '\0') {
585 fData.push_back ('\0');
586 }
587}
588
589size_t Led_URLD::GetURLDLength () const
590{
591 // Note we DONT count the terminating NUL - cuz Netscape 2.0 doesn't appear to. And thats our reference.
592 return fData.size () - 1;
593}
594
595size_t Led_URLD::GetURLLength () const
596{
597 size_t len = fData.size ();
598 for (size_t i = 0; i < len; i++) {
599 if (fData[i] == '\r') {
600 return i;
601 }
602 }
603 return len - 1;
604}
605
606size_t Led_URLD::GetTitleLength () const
607{
608 size_t len = fData.size ();
609 for (size_t i = 0; i < len; i++) {
610 if (fData[i] == '\r') {
611 Assert (len >= (i + 1 + 1)); // cuz must be NUL-term & skip \r
612 return len - (i + 1 + 1); // ditto
613 }
614 }
615 return 0;
616}
617
618char* Led_URLD::PeekAtURLD () const
619{
620 return (&const_cast<Led_URLD*> (this)->fData.front ());
621}
622
623char* Led_URLD::PeekAtURL () const
624{
625 return (&const_cast<Led_URLD*> (this)->fData.front ());
626}
627
628char* Led_URLD::PeekAtTitle () const
629{
630 return PeekAtURL () + GetURLLength () + 1; // skip URL and '\r'
631}
632
633string Led_URLD::GetTitle () const
634{
635 return string{PeekAtTitle (), GetTitleLength ()};
636}
637
638string Led_URLD::GetURL () const
639{
640 return string{PeekAtURL (), GetURLLength ()};
641}
642
643/*
644 ********************************************************************************
645 ********************************* Led_URLManager *******************************
646 ********************************************************************************
647 */
648namespace {
649 Led_URLManager* sTheURLMgr_;
650}
651Led_URLManager& Led_URLManager::Get ()
652{
653 if (sTheURLMgr_ == nullptr) {
654 sTheURLMgr_ = new Led_URLManager ();
655 }
656 return *sTheURLMgr_;
657}
658
659void Led_URLManager::Set (Led_URLManager* newURLMgr)
660{
661 if (sTheURLMgr_ != newURLMgr) {
662 delete sTheURLMgr_;
663 sTheURLMgr_ = newURLMgr;
664 }
665}
666
667void Led_URLManager::Open (const string& url)
668{
669#if qStroika_Foundation_Common_Platform_Windows
670#if qUseActiveXToOpenURLs
671 Open_ActiveX (url);
672#endif
673#endif
674}
675
676string Led_URLManager::FileSpecToURL ([[maybe_unused]] const filesystem::path& p)
677{
678 AssertNotImplemented (); // nyi (not needed anywhere right now)
679 return "";
680}
681
682#if qUseActiveXToOpenURLs
683void Led_URLManager::Open_ActiveX (const string& url)
684{
685 Memory::StackBuffer<wchar_t> wideURLBuf{Memory::eUninitialized, url.length () + 1};
686 {
687 int nWideChars = ::MultiByteToWideChar (CP_ACP, 0, url.c_str (), static_cast<int> (url.length ()), wideURLBuf.data (),
688 static_cast<int> (url.length ()));
689 wideURLBuf[nWideChars] = '\0';
690 }
691 IUnknown* pUnk = nullptr; // implies we are an OLE-ignorant app. But I'm not sure what else I'm supposed to pass here!
692
693/*
694 * Use LoadLibrary/GetProcAddress instead of direct call to avoid having to link with
695 * urlmon.lib. This avoidance allows us to run on systems that don't have MSIE installed.
696 *
697 * Personally, I think its pretty weak MS doesn't offer a simpler way to deal with this problem.
698 * (or if they do, their docs are weak).
699 * --LGP 961015
700 */
701#if 1
702 static HINSTANCE urlmonLibrary = ::LoadLibrary (_T ("urlmon"));
703 if (urlmonLibrary == nullptr) {
704 Execution::Throw (bad_alloc{});
705 }
706 static HRESULT (WINAPI * hlinkNavigateString) (IUnknown*, LPCWSTR) =
707 (HRESULT (WINAPI*) (IUnknown*, LPCWSTR))::GetProcAddress (urlmonLibrary, "HlinkNavigateString");
708 if (hlinkNavigateString == nullptr) {
709 Execution::Throw (bad_alloc{});
710 }
711 HRESULT result = (*hlinkNavigateString) (pUnk, wideURLBuf.data ());
712#else
713 HRESULT result = ::HlinkNavigateString (pUnk, wideURLBuf.data ());
714#endif
715 if (not SUCCEEDED (result)) {
716 throw result;
717 }
718}
719#endif
720
721/*
722 ********************************************************************************
723 ********************** Led::MakeSophistsAppNameVersionURL **********************
724 ********************************************************************************
725 */
726string Led::MakeSophistsAppNameVersionURL (const string& relURL, const string& appName, const string& extraArgs)
727{
728 Require (relURL.length () > 0 and relURL[0] == '/');
729 char fullVersionBuf[1024];
730 (void)snprintf (fullVersionBuf, Memory::NEltsOf (fullVersionBuf), "%d", qLed_FullVersion);
731 string fullURL = "http://www.sophists.com" + relURL + "?AppName=" + appName +
732#if qStroika_Foundation_Common_Platform_Windows
733 string{"&Platform=Windows"} +
734#elif qStroika_FeatureSupported_XWindows
735 string{"&Platform=XWindows"} +
736#endif
737 "&MajorMinorVersion=" + qLed_MajorMinorVersionString + "&LedFullVersion=" + fullVersionBuf +
738 "&ShortVersionString=" + qLed_ShortVersionString + extraArgs;
739 return fullURL;
740}
741
742/*
743 ********************************************************************************
744 ******************************* Led_CasedCharsEqual ****************************
745 ********************************************************************************
746 */
747bool Led::Led_CasedCharsEqual (char lhs, char rhs, bool ignoreCase)
748{
749 if (lhs == rhs) {
750 return true;
751 }
752 if (ignoreCase and isascii (lhs) and isalpha (lhs) and isascii (rhs) and isalpha (rhs) and toupper (lhs) == toupper (rhs)) {
753 return true;
754 }
755 return false;
756}
757
758CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led, qStroika_Frameworks_Led_ProvideIMESupport, qStroika_Frameworks_Led_ProvideIMESupport);
#define AssertNotNull(p)
Definition Assertions.h:333
#define AssertNotImplemented()
Definition Assertions.h:401
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertNotReached()
Definition Assertions.h:355
#define Verify(c)
Definition Assertions.h:419
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String SafeSubString(SZ from) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...