1 #include "gui/Utils.h"
2 #include "common/Assert.h"
3 #include "io/FileSystem.h"
4 #include "thread/CheckFunction.h"
5 #include <iomanip>
6 #include <wx/dcmemory.h>
7 #include <wx/filedlg.h>
8 #include <wx/msgdlg.h>
9 
10 NAMESPACE_SPH_BEGIN
11 
getDesc(ArrayView<const FileFormat> formats,const bool doAll)12 static String getDesc(ArrayView<const FileFormat> formats, const bool doAll) {
13     String desc;
14     bool isFirst = true;
15     if (doAll && formats.size() > 1) {
16         desc += L"All supported formats|";
17         for (const FileFormat& format : formats) {
18             desc += L"*." + format.extension + L";";
19         }
20         isFirst = false;
21     }
22     for (const FileFormat& format : formats) {
23         if (!isFirst) {
24             desc += "|";
25         }
26         isFirst = false;
27         desc += format.description + L" (*." + format.extension + L")|*." + format.extension;
28     }
29     return desc;
30 }
31 
doFileDialog(const String & title,const String & fileMask,String & defaultDir,const int flags)32 static Optional<std::pair<Path, int>> doFileDialog(const String& title,
33     const String& fileMask,
34     String& defaultDir,
35     const int flags) {
36     wxFileDialog dialog(nullptr, title.toUnicode(), "", defaultDir.toUnicode(), fileMask.toUnicode(), flags);
37     if (dialog.ShowModal() == wxID_CANCEL) {
38         return NOTHING;
39     }
40     Path path(String(dialog.GetPath().wc_str()));
41     defaultDir = path.parentPath().string();
42     return std::make_pair(path, !fileMask.empty() ? dialog.GetFilterIndex() : -1);
43 }
44 
doOpenFileDialog(const String & title,Array<FileFormat> && formats)45 Optional<Path> doOpenFileDialog(const String& title, Array<FileFormat>&& formats) {
46     static String defaultDir = "";
47     Optional<std::pair<Path, int>> pathAndIndex =
48         doFileDialog(title, getDesc(formats, true), defaultDir, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
49     if (pathAndIndex) {
50         return pathAndIndex->first;
51     } else {
52         return NOTHING;
53     }
54 }
55 
doSaveFileDialog(const String & title,Array<FileFormat> && formats)56 Optional<Path> doSaveFileDialog(const String& title, Array<FileFormat>&& formats) {
57     static String defaultDir = "";
58     Optional<std::pair<Path, int>> pathAndIndex =
59         doFileDialog(title, getDesc(formats, false), defaultDir, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
60     if (pathAndIndex) {
61         const int index = pathAndIndex->second;
62         if (index >= 0) {
63             const String& ext = formats[index].extension;
64             pathAndIndex->first.replaceExtension(ext);
65         }
66         return pathAndIndex->first;
67     } else {
68         return NOTHING;
69     }
70 }
71 
messageBox(const String & message,const String & title,const int buttons)72 int messageBox(const String& message, const String& title, const int buttons) {
73     return wxMessageBox(message.toUnicode(), title.toUnicode(), buttons);
74 }
75 
getSubscriptSize(const String & text)76 static Size getSubscriptSize(const String& text) {
77     Size size = 0;
78     if (!text.empty() && text[0] == L'-') {
79         size++;
80     }
81     for (; size < text.size(); ++size) {
82         const wchar_t c = text[size];
83         if ((c < L'0' || c > L'9') && (c < L'a' || c > L'z') && (c < L'A' || c > L'Z')) {
84             return size;
85         }
86     }
87     return text.size();
88 }
89 
drawTextWithSubscripts(wxDC & dc,const String & text,const wxPoint point)90 void drawTextWithSubscripts(wxDC& dc, const String& text, const wxPoint point) {
91     std::size_t n;
92     std::size_t m = 0;
93     wxPoint actPoint = point;
94     const wxFont font = dc.GetFont();
95     const wxFont subcriptFont = font.Smaller();
96 
97     StaticArray<wchar_t, 2> specialChars = { L'_', L'^' };
98     while ((n = text.findAny(specialChars, m)) != String::npos) {
99         const bool isSubscript = text[n] == '_';
100         String part = text.substr(m, n - m);
101         wxSize extent = dc.GetTextExtent(part.toUnicode());
102         // draw part up to subscript using current font
103         dc.DrawText(part.toUnicode(), actPoint);
104 
105         actPoint.x += extent.x;
106         const Size subscriptSize = getSubscriptSize(text.substr(n + 1));
107         const String subscript = text.substr(n + 1, subscriptSize);
108         dc.SetFont(subcriptFont);
109         wxPoint subscriptPoint = isSubscript ? wxPoint(actPoint.x + 2, actPoint.y + extent.y / 3)
110                                              : wxPoint(actPoint.x + 2, actPoint.y - extent.y / 4);
111 
112         dc.DrawText(subscript.toUnicode(), subscriptPoint);
113         actPoint.x = subscriptPoint.x + dc.GetTextExtent(subscript.toUnicode()).x;
114 
115         dc.SetFont(font);
116         m = n + 1 + subscriptSize; // skip _ and the subscript character
117     }
118     // draw last part of the text
119     dc.DrawText(text.substr(m).toUnicode(), actPoint);
120 }
121 
toPrintableString(const Float value,const Size precision,const Float decimalThreshold)122 String toPrintableString(const Float value, const Size precision, const Float decimalThreshold) {
123     const Float absValue = abs(value);
124     std::wstringstream ss;
125     if (absValue == 0._f || (absValue >= 1._f / decimalThreshold && absValue <= decimalThreshold)) {
126         ss << value;
127     } else {
128         ss << std::setprecision(precision) << std::scientific << value;
129     }
130     String s = String::fromWstring(ss.str());
131 
132     String printable;
133     if (value > 0) {
134         printable += ' ';
135     }
136     bool exponent = false;
137     for (Size i = 0; i < s.size(); ++i) {
138         // replace unary pluses with spaces (to keep alignment of numbers
139         if (s[i] == '+') {
140             continue;
141         }
142         // replace 'e' with 'x10^'
143         if (s[i] == 'e') {
144             exponent = true;
145             printable += L"\u00D710^";
146             continue;
147         }
148         // get rid of leading zeros in exponent
149         if (exponent) {
150             if (s[i] == '-') {
151                 printable += '-';
152                 continue;
153             }
154             if (s[i] == '0') {
155                 continue;
156             }
157         }
158         printable += s[i];
159         exponent = false;
160     }
161     return printable;
162 }
163 
getOriginOffset(wxDC & dc,Flags<TextAlign> align,const String & text)164 static Pixel getOriginOffset(wxDC& dc, Flags<TextAlign> align, const String& text) {
165     wxSize extent = dc.GetTextExtent(text.toUnicode());
166     if (text.find(L"^") != String::npos) {
167         // number with superscript is actually a bit shorter, shrink it
168         /// \todo this should be done more correctly
169         extent.x -= 6;
170     }
171     Pixel offset(0, 0);
172     if (align.has(TextAlign::LEFT)) {
173         offset.x -= extent.x;
174     }
175     if (align.has(TextAlign::HORIZONTAL_CENTER)) {
176         offset.x -= extent.x / 2;
177     }
178     if (align.has(TextAlign::TOP)) {
179         offset.y -= extent.y;
180     }
181     if (align.has(TextAlign::VERTICAL_CENTER)) {
182         offset.y -= extent.y / 2;
183     }
184     return offset;
185 }
186 
printLabels(wxDC & dc,ArrayView<const IRenderOutput::Label> labels)187 void printLabels(wxDC& dc, ArrayView<const IRenderOutput::Label> labels) {
188     wxFont font = dc.GetFont();
189     for (const IRenderOutput::Label& label : labels) {
190         dc.SetTextForeground(wxColour(label.color));
191         font.SetPointSize(label.fontSize);
192         dc.SetFont(font);
193         const wxPoint origin(label.position + getOriginOffset(dc, label.align, label.text));
194         drawTextWithSubscripts(dc, label.text, origin);
195     }
196 }
197 
TransparencyPattern(const Size side,const Rgba & dark,const Rgba & light)198 TransparencyPattern::TransparencyPattern(const Size side, const Rgba& dark, const Rgba& light) {
199     CHECK_FUNCTION(CheckFunction::MAIN_THREAD);
200     stipple.resize(Pixel(2 * side, 2 * side), dark);
201     for (Size y = 0; y < side; ++y) {
202         for (Size x = 0; x < side; ++x) {
203             stipple(x, y) = light;
204             stipple(x + side, y + side) = light;
205         }
206     }
207 }
208 
draw(wxDC & dc,const wxRect & rect) const209 void TransparencyPattern::draw(wxDC& dc, const wxRect& rect) const {
210     CHECK_FUNCTION(CheckFunction::MAIN_THREAD);
211     wxBrush brush = *wxBLACK_BRUSH;
212     wxBitmap wx;
213     toWxBitmap(stipple, wx);
214     brush.SetStipple(wx);
215     dc.SetBrush(brush);
216     dc.DrawRectangle(rect);
217 }
218 
219 NAMESPACE_SPH_END
220