1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.IO;
  8 
  9 namespace System.Windows
 10 {
 11     public enum SystemMetricsId : int
 12     {
 13         SM_ARRANGE = 56
 14         SM_CLEANBOOT = 67
 15         SM_CMONITORS = 80
 16         SM_CMOUSEBUTTONS = 43
 17         SM_CONVERTIBLESLATEMODE = 8195
 18         SM_CXBORDER = 5
 19         SM_CXCURSOR = 13
 20         SM_CXDLGFRAME = 7
 21         SM_CXDOUBLECLK = 36
 22         SM_CXDRAG = 68
 23         SM_CXEDGE = 45
 24         SM_CXFIXEDFRAME = 7
 25         SM_CXFOCUSBORDER = 83
 26         SM_CXFRAME = 32
 27         SM_CXFULLSCREEN = 16
 28         SM_CXHSCROLL = 21
 29         SM_CXHTHUMB = 10
 30         SM_CXICON = 11
 31         SM_CXICONSPACING = 38
 32         SM_CXMAXIMIZED = 61
 33         SM_CXMAXTRACK = 59
 34         SM_CXMENUCHECK = 71
 35         SM_CXMENUSIZE = 54
 36         SM_CXMIN = 28
 37         SM_CXMINIMIZED = 57
 38         SM_CXMINSPACING = 47
 39         SM_CXMINTRACK = 34
 40         SM_CXPADDEDBORDER = 92
 41         SM_CXSCREEN = 0
 42         SM_CXSIZE = 30
 43         SM_CXSIZEFRAME = 32
 44         SM_CXSMICON = 49
 45         SM_CXSMSIZE = 52
 46         SM_CXVIRTUALSCREEN = 78
 47         SM_CXVSCROLL = 2
 48         SM_CYBORDER = 6
 49         SM_CYCAPTION = 4
 50         SM_CYCURSOR = 14
 51         SM_CYDLGFRAME = 8
 52         SM_CYDOUBLECLK = 37
 53         SM_CYDRAG = 69
 54         SM_CYEDGE = 46
 55         SM_CYFIXEDFRAME = 8
 56         SM_CYFOCUSBORDER = 84
 57         SM_CYFRAME = 33
 58         SM_CYFULLSCREEN = 17
 59         SM_CYHSCROLL = 3
 60         SM_CYICON = 12
 61         SM_CYICONSPACING = 39
 62         SM_CYKANJIWINDOW = 18
 63         SM_CYMAXIMIZED = 62
 64         SM_CYMAXTRACK = 60
 65         SM_CYMENU = 15
 66         SM_CYMENUCHECK = 72
 67         SM_CYMENUSIZE = 55
 68         SM_CYMIN = 29
 69         SM_CYMINIMIZED = 58
 70         SM_CYMINSPACING = 48
 71         SM_CYMINTRACK = 35
 72         SM_CYSCREEN = 1
 73         SM_CYSIZE = 31
 74         SM_CYSIZEFRAME = 33
 75         SM_CYSMCAPTION = 51
 76         SM_CYSMICON = 50
 77         SM_CYSMSIZE = 53
 78         SM_CYVIRTUALSCREEN = 79
 79         SM_CYVSCROLL = 20
 80         SM_CYVTHUMB = 9
 81         SM_DBCSENABLED = 42
 82         SM_DEBUG = 22
 83         SM_DIGITIZER = 94
 84         SM_IMMENABLED = 82
 85         SM_MAXIMUMTOUCHES = 95
 86         SM_MEDIACENTER = 87
 87         SM_MENUDROPALIGNMENT = 40
 88         SM_MIDEASTENABLED = 74
 89         SM_MOUSEPRESENT = 19
 90         SM_MOUSEHORIZONTALWHEELPRESENT = 91
 91         SM_MOUSEWHEELPRESENT = 75
 92         SM_NETWORK = 63
 93         SM_PENWINDOWS = 41
 94         SM_REMOTECONTROL = 8193
 95         SM_REMOTESESSION = 4096
 96         SM_SAMEDISPLAYFORMAT = 81
 97         SM_SERVERR2 = 89
 98         SM_SHOWSOUNDS = 70
 99         SM_SHUTTINGDOWN = 8192
100         SM_SLOWMACHINE = 73
101         SM_STARTER = 88
102         SM_SWAPBUTTON = 23
103         SM_SYSTEMDOCKED = 8196
104         SM_TABLETPC = 86
105         SM_XVIRTUALSCREEN = 76
106         SM_YVIRTUALSCREEN = 77
107     }
108 
109     int GetSystemMetrics(SystemMetricsId id)
110     {
111         return WinGetSystemMetrics(cast<int>(id));
112     }
113 
114     public const float inchMM = 25.400000f;
115     public const float pointMM = 0.351450f;
116 
117     public inline float InchToMM(float inches)
118     {
119         return inches * inchMM;
120     }
121 
122     public inline float MMToInch(float mm)
123     {
124         return mm / inchMM;
125     }
126 
127     public inline float PointToMM(float points)
128     {
129         return points * pointMM;
130     }
131 
132     public inline float MMToPoint(float mm)
133     {
134         return mm / pointMM;
135     }
136 
137     public inline int MMToPixels(float mmfloat dpi)
138     {
139         return cast<int>(mm * dpi / inchMM);
140     }
141 
142     public inline float PixelsToMM(int pixelsfloat dpi)
143     {
144         return pixels * inchMM / dpi;
145     }
146 
147     public inline double GoldenRatio()
148     {
149         return 1 + Sqrt(5) / 2;
150     }
151 
152     public string GetDefaultMetricsFilePath()
153     {
154         string metricsFilePath;
155         string cmajorRootDir;
156         int stringHandle = RtmGetEnvironmentVariable("CMAJOR_ROOT");
157         if (stringHandle != -1)
158         {
159             cmajorRootDir = RtmGetString(stringHandle);
160             RtmFreeString(stringHandle);
161         }
162         if (!cmajorRootDir.IsEmpty())
163         {
164             metricsFilePath = Path.Combine(Path.Combine(cmajorRootDir"config")"metrics.xml");
165         }
166         return metricsFilePath;
167     }
168 
169     public class SizeElement
170     {
171         public SizeElement(const string& name_) : name(name_)
172         {
173         }
174         public inline const SizeF& Get() const
175         {
176             return size;
177         }
178         public void Set(const SizeF& size_)
179         {
180             size = size_;
181         }
182         [nodiscard]
183         public Result<bool> Read(System.Xml.Element* parentElement)
184         {
185             Result<UniquePtr<System.XPath.NodeSet>> nodeSetResult = System.XPath.EvaluateToNodeSet(nameparentElement);
186             if (nodeSetResult.Error())
187             {
188                 return Result<bool>(ErrorId(nodeSetResult.GetErrorId()));
189             }
190             System.XPath.NodeSet* nodeSet = nodeSetResult.Value().Get();
191             int n = nodeSet->Count();
192             if (n == 1)
193             {
194                 System.Xml.Node* node = nodeSet->GetNode(0);
195                 if (node->IsElementNode())
196                 {
197                     System.Xml.Element* sizeElement = cast<System.Xml.Element*>(node);
198                     auto wresult = ParseFloat(sizeElement->GetAttribute("width"));
199                     if (wresult.Error())
200                     {
201                         return Result<bool>(ErrorId(wresult.GetErrorId()));
202                     }
203                     size.w = wresult.Value();
204                     auto hresult = ParseFloat(sizeElement->GetAttribute("height"));
205                     if (hresult.Error())
206                     {
207                         return Result<bool>(ErrorId(hresult.GetErrorId()));
208                     }
209                     size.h = hresult.Value();
210                 }
211             }
212             else
213             {
214                 return Result<bool>(false);
215             }
216             return Result<bool>(true);
217         }
218         public void Write(System.Xml.Element* parentElement)
219         {
220             System.Xml.Element* sizeElement(System.Xml.MakeElement(name));
221             sizeElement->SetAttribute("width"ToString(size.w));
222             sizeElement->SetAttribute("height"ToString(size.h));
223             parentElement->AppendChild(sizeElement);
224         }
225         private string name;
226         private SizeF size;
227     }
228 
229     public class Metrics
230     {
231         public Metrics() : 
232             useDefaults(false)dpiX(96)dpiY(96)
233             defaultButtonSize(8625)defaultButtonSizeElement("button")
234             defaultLabelSize(7119)defaultLabelSizeElement("label")
235             defaultTextBoxSize(9414)defaultTextBoxSizeElement("textBox")
236             defaultListBoxSize(12095)defaultListBoxSizeElement("listBox")
237             defaultCheckBoxSize(8017)defaultCheckBoxSizeElement("checkBox")
238             defaultControlSpacing(1010)defaultControlSpacingElement("controlSpacing")
239         {
240             SetElementValues();
241         }
242         public inline bool UseDefaults() const
243         {
244             return useDefaults;
245         }
246         public void SetUseDefaults(bool useDefaults_)
247         {
248             useDefaults = useDefaults_;
249         }
250         [nodiscard]
251         public Result<bool> LoadFromFile(const string& fileNameSystem.Lex.FileMap& fileMap)
252         {
253             auto existsResult = File.Exists(fileName);
254             if (existsResult.Error())
255             {
256                 return Result<bool>(ErrorId(existsResult.GetErrorId()));
257             }
258             bool exists = existsResult.Value();
259             if (exists)
260             {
261                 Result<UniquePtr<System.Xml.Document>> metricsDocResult =  System.Xml.ParseXmlDocument(fileNamefileMap);
262                 if (metricsDocResult.Error())
263                 {
264                     return Result<bool>(ErrorId(metricsDocResult.GetErrorId()));
265                 }
266                 System.Xml.Document* metricsDoc = metricsDocResult.Value().Get();
267                 auto result = Read(metricsDoc->DocumentElement());
268                 if (result.Error()) return result;
269                 LogView* logView = Application.GetLogView();
270                 if (logView != null)
271                 {
272                     auto result = logView->WriteLine("metrics loaded from \'" + fileName + "\'");
273                     if (result.Error()) return result;
274                 }
275             }
276             else
277             {
278                 return Result<bool>(false);
279             }
280             return Result<bool>(true);
281         }
282         public void SetElementValues()
283         {
284             defaultButtonSizeElement.Set(SizeF(HorizontalPixelsToMM(defaultButtonSize.w)VerticalPixelsToMM(defaultButtonSize.h)));
285             defaultLabelSizeElement.Set(SizeF(HorizontalPixelsToMM(defaultLabelSize.w)VerticalPixelsToMM(defaultLabelSize.h)));
286             defaultTextBoxSizeElement.Set(SizeF(HorizontalPixelsToMM(defaultTextBoxSize.w)VerticalPixelsToMM(defaultTextBoxSize.h)));
287             defaultListBoxSizeElement.Set(SizeF(HorizontalPixelsToMM(defaultListBoxSize.w)VerticalPixelsToMM(defaultListBoxSize.h)));
288             defaultCheckBoxSizeElement.Set(SizeF(HorizontalPixelsToMM(defaultCheckBoxSize.w)VerticalPixelsToMM(defaultCheckBoxSize.h)));
289             defaultControlSpacingElement.Set(SizeF(HorizontalPixelsToMM(defaultControlSpacing.w)VerticalPixelsToMM(defaultControlSpacing.h)));
290         }
291         [nodiscard]
292         public Result<bool> SaveTofile(const string& fileNamebool setElementValues)
293         {
294             if (setElementValues)
295             {
296                 SetElementValues();
297             }
298             System.Xml.Document metricsDoc;
299             metricsDoc.AppendChild(System.Xml.MakeElement("metrics"));
300             metricsDoc.DocumentElement()->AppendChild(System.Xml.MakeComment("metrics are in millimeters"));
301             metricsDoc.DocumentElement()->AppendChild(System.Xml.MakeText("\n "));
302             Write(metricsDoc.DocumentElement());
303             auto fileResult = File.CreateText(fileName);
304             if (fileResult.Error())
305             {
306                 return Result<bool>(ErrorId(fileResult.GetErrorId()));
307             }
308             StreamWriter& writer = fileResult.Value();
309             System.Text.CodeFormatter formatter(writer);
310             formatter.SetIndentSize(1);
311             auto writeResult = metricsDoc.Write(formatter);
312             if (writeResult.Error())
313             {
314                 return Result<bool>(ErrorId(writeResult.GetErrorId()));
315             }
316             LogView* logView = Application.GetLogView();
317             if (logView != null)
318             {
319                 auto result = logView->WriteLine("metrics saved to \'" + fileName + "\'");
320                 if (result.Error()) return result;
321             }
322             return Result<bool>(true);
323         }
324         [nodiscard]
325         public Result<bool> Read(System.Xml.Element* parentElement)
326         {
327             auto result = defaultButtonSizeElement.Read(parentElement);
328             if (result.Error()) return result;
329             result = defaultLabelSizeElement.Read(parentElement);
330             if (result.Error()) return result;
331             result = defaultTextBoxSizeElement.Read(parentElement);
332             if (result.Error()) return result;
333             result = defaultListBoxSizeElement.Read(parentElement);
334             if (result.Error()) return result;
335             result = defaultCheckBoxSizeElement.Read(parentElement);
336             if (result.Error()) return result;
337             result = defaultControlSpacingElement.Read(parentElement);
338             if (result.Error()) return result;
339             return Result<bool>(true);
340         }
341         public void Write(System.Xml.Element* parentElement)
342         {
343             defaultButtonSizeElement.Write(parentElement);
344             defaultLabelSizeElement.Write(parentElement);
345             defaultTextBoxSizeElement.Write(parentElement);
346             defaultListBoxSizeElement.Write(parentElement);
347             defaultCheckBoxSizeElement.Write(parentElement);
348             defaultControlSpacingElement.Write(parentElement);
349         }
350         public inline bool Calculated() const
351         {
352             return calculated;
353         }
354         public void Calculate(Graphics& graphics)
355         {
356             calculated = true;
357             dpiX = graphics.GetDpiX();
358             dpiY = graphics.GetDpiX();
359             if (useDefaults) return;
360             defaultButtonSize.w = MMToHorizontalPixels(defaultButtonSizeElement.Get().w);
361             defaultButtonSize.h = MMToVerticalPixels(defaultButtonSizeElement.Get().h);
362             defaultLabelSize.w = MMToHorizontalPixels(defaultLabelSizeElement.Get().w);
363             defaultLabelSize.h = MMToVerticalPixels(defaultLabelSizeElement.Get().h);
364             defaultTextBoxSize.w = MMToHorizontalPixels(defaultTextBoxSizeElement.Get().w);
365             defaultTextBoxSize.h = MMToVerticalPixels(defaultTextBoxSizeElement.Get().h);
366             defaultListBoxSize.w = MMToHorizontalPixels(defaultListBoxSizeElement.Get().w);
367             defaultListBoxSize.h = MMToVerticalPixels(defaultListBoxSizeElement.Get().h);
368             defaultCheckBoxSize.w = MMToHorizontalPixels(defaultCheckBoxSizeElement.Get().w);
369             defaultCheckBoxSize.h = MMToVerticalPixels(defaultCheckBoxSizeElement.Get().h);
370             defaultControlSpacing.w = MMToHorizontalPixels(defaultControlSpacingElement.Get().w);
371             defaultControlSpacing.h = MMToVerticalPixels(defaultControlSpacingElement.Get().h);
372         }
373         [nodiscard]
374         public Result<bool> PrintToLog()
375         {
376             LogView* logView = Application.GetLogView();
377             if (logView != null)
378             {
379                 auto result = logView->WriteLine("DPIX=" + ToString(dpiX) + ", DPIY=" + ToString(dpiY));
380                 if (result.Error()) return result;
381                 result = logView->WriteLine("Button: " + defaultButtonSize.ToString() + " - (" + ToString(HorizontalPixelsToMM(defaultButtonSize.w)) + "mm, " + 
382                     ToString(VerticalPixelsToMM(defaultButtonSize.h)) + "mm)");
383                 if (result.Error()) return result;
384                 result = logView->WriteLine("Label: " + defaultLabelSize.ToString() + " - (" + ToString(HorizontalPixelsToMM(defaultLabelSize.w)) + "mm, " + 
385                     ToString(VerticalPixelsToMM(defaultLabelSize.h)) + "mm)");
386                 if (result.Error()) return result;
387                 result = logView->WriteLine("TextBox: " + defaultTextBoxSize.ToString() + " - (" + ToString(HorizontalPixelsToMM(defaultTextBoxSize.w)) + "mm, " + 
388                     ToString(VerticalPixelsToMM(defaultTextBoxSize.h)) + "mm)");
389                 if (result.Error()) return result;
390                 result = logView->WriteLine("ListBox: " + defaultListBoxSize.ToString() + " - (" + ToString(HorizontalPixelsToMM(defaultListBoxSize.w)) + "mm, " + 
391                     ToString(VerticalPixelsToMM(defaultListBoxSize.h)) + "mm)");
392                 if (result.Error()) return result;
393                 result = logView->WriteLine("CheckBox: " + defaultCheckBoxSize.ToString() + " - (" + ToString(HorizontalPixelsToMM(defaultCheckBoxSize.w)) + "mm, " + 
394                     ToString(VerticalPixelsToMM(defaultCheckBoxSize.h)) + "mm)");
395                 if (result.Error()) return result;
396                 result = logView->WriteLine("Spacing: " + defaultControlSpacing.ToString() +  " - (" + ToString(HorizontalPixelsToMM(defaultControlSpacing.w)) + "mm, " + 
397                     ToString(VerticalPixelsToMM(defaultControlSpacing.h)) + "mm)");
398                 if (result.Error()) return result;
399             }
400             return Result<bool>(true);
401         }
402         public inline float DpiX() const
403         {
404             return dpiX;
405         }
406         public inline float DpiY() const
407         {
408             return dpiY;
409         }
410         public inline int MMToHorizontalPixels(float mm) const
411         {
412             return MMToPixels(mmdpiX);
413         }
414         public inline float HorizontalPixelsToMM(int pixels) const
415         {
416             return PixelsToMM(pixelsdpiX);
417         }
418         public inline int MMToVerticalPixels(float mm) const
419         {
420             return MMToPixels(mmdpiY);
421         }
422         public inline float VerticalPixelsToMM(int pixels) const
423         {
424             return PixelsToMM(pixelsdpiY);
425         }
426         public inline const Size& DefaultButtonSize() const
427         {
428             return defaultButtonSize;
429         }
430         public void SetDefaultButtonSize(const Size& defaultButtonSize_)
431         {
432             defaultButtonSize = defaultButtonSize_;
433         }
434         public inline const Size& DefaultLabelSize() const
435         {
436             return defaultLabelSize;
437         }
438         public void SetDefaultLabelSize(const Size& defaultLabelSize_)
439         {
440             defaultLabelSize = defaultLabelSize_;
441         }
442         public inline const Size& DefaultTextBoxSize() const
443         {
444             return defaultTextBoxSize;
445         }
446         public void SetDefaultTextBoxSize(const Size& defaultTextBoxSize_)
447         {
448             defaultTextBoxSize = defaultTextBoxSize_;
449         }
450         public inline const Size& DefaultListBoxSize() const
451         {
452             return defaultListBoxSize;
453         }
454         public void SetDefaultListBoxSize(const Size& defaultListBoxSize_)
455         {
456             defaultListBoxSize = defaultListBoxSize_;
457         }
458         public inline const Size& DefaultCheckBoxSize() const
459         {
460             return defaultCheckBoxSize;
461         }
462         public void SetDefaultCheckBoxSize(const Size& defaultCheckBoxSize_)
463         {
464             defaultCheckBoxSize = defaultCheckBoxSize_;
465         }
466         public inline const Size& DefaultControlSpacing() const
467         {
468             return defaultControlSpacing;
469         }
470         public void SetDefaultControlSpacing(const Size& defaultControlSpacing_)
471         {
472             defaultControlSpacing = defaultControlSpacing_;
473         }
474         public SizeElement& DefaultButtonSizeElement()
475         {
476             return defaultButtonSizeElement;
477         }
478         public SizeElement& DefaultLabelSizeElement()
479         {
480             return defaultLabelSizeElement;
481         }
482         public SizeElement& DefaultTextBoxSizeElement()
483         {
484             return defaultTextBoxSizeElement;
485         }
486         public SizeElement& DefaultControlSpacingElement()
487         {
488             return defaultControlSpacingElement;
489         }
490         private bool useDefaults;
491         private bool calculated;
492         private float dpiX;
493         private float dpiY;
494         private Size defaultButtonSize;
495         private SizeElement defaultButtonSizeElement;
496         private Size defaultLabelSize;
497         private SizeElement defaultLabelSizeElement;
498         private Size defaultTextBoxSize;
499         private SizeElement defaultTextBoxSizeElement;
500         private Size defaultListBoxSize;
501         private SizeElement defaultListBoxSizeElement;
502         private Size defaultCheckBoxSize;
503         private SizeElement defaultCheckBoxSizeElement;
504         private Size defaultControlSpacing;
505         private SizeElement defaultControlSpacingElement;
506     }
507 
508     public static class ScreenMetrics
509     {
510         public static ScreenMetrics() : metrics()
511         {
512         }
513         public static void SetUseDefaults(bool useDefaults)
514         {
515             metrics.SetUseDefaults(useDefaults);
516         }
517         public static Result<bool> Save(bool setElementValues)
518         {
519             string metricsFilePath = GetDefaultMetricsFilePath();
520             auto saveResult = metrics.SaveTofile(metricsFilePathsetElementValues);
521             if (saveResult.Error())
522             {
523                 return Result<bool>(ErrorId(saveResult.GetErrorId()));
524             }
525             return Result<bool>(true);
526         }
527         public static Result<bool> Load(System.Lex.FileMap& fileMap)
528         {
529             string metricsFilePath = GetDefaultMetricsFilePath();
530             auto existsResult = File.Exists(metricsFilePath);
531             if (existsResult.Error())
532             {
533                 return Result<bool>(ErrorId(existsResult.GetErrorId()));
534             }
535             bool exists = existsResult.Value();
536             if (exists)
537             {
538                 auto result = metrics.LoadFromFile(metricsFilePathfileMap);
539                 if (result.Error())
540                 {
541                     return Result<bool>(ErrorId(result.GetErrorId()));
542                 }
543             }
544             else
545             {
546                 return Result<bool>(false);
547             }
548             return Result<bool>(true);
549         }
550         public static bool Calculated()
551         {
552             return metrics.Calculated();
553         }
554         public static Result<bool> Calculate(Graphics& graphics)
555         {
556             if (!metrics.UseDefaults())
557             {
558                 auto result = Load(defaultFileMap);
559                 if (result.Error())
560                 {
561                     return Result<bool>(ErrorId(result.GetErrorId()));
562                 }
563             }
564             metrics.Calculate(graphics);
565             return Result<bool>(true);
566         }
567         public static const Metrics& Get()
568         {
569             return metrics;
570         }
571         public static void Set(const Metrics& metrics_)
572         {
573             metrics = metrics_;
574         }
575         public static Metrics metrics;
576         private static System.Lex.FileMap defaultFileMap;
577     }