1 // Copyright (c) 2005-2006, Rodrigo Braz Monteiro
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of the Aegisub Group nor the names of its contributors
13 // may be used to endorse or promote products derived from this software
14 // without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 // POSSIBILITY OF SUCH DAMAGE.
27 //
28 // Aegisub Project http://www.aegisub.org/
29
30 #include "utils.h"
31
32 #include "compat.h"
33 #include "format.h"
34 #include "options.h"
35 #include "retina_helper.h"
36
37 #include <libaegisub/dispatch.h>
38 #include <libaegisub/fs.h>
39 #include <libaegisub/log.h>
40
41 #ifdef __UNIX__
42 #include <unistd.h>
43 #endif
44 #include <boost/filesystem/path.hpp>
45 #include <map>
46 #include <unicode/locid.h>
47 #include <unicode/unistr.h>
48 #include <wx/clipbrd.h>
49 #include <wx/filedlg.h>
50 #include <wx/stdpaths.h>
51 #include <wx/window.h>
52
53 #ifdef __APPLE__
54 #include <libaegisub/util_osx.h>
55 #include <CoreText/CTFont.h>
56 #endif
57
58 /// @brief There shall be no kiB, MiB stuff here Pretty reading of size
PrettySize(int bytes)59 wxString PrettySize(int bytes) {
60 const char *suffix[] = { "", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
61
62 // Set size
63 size_t i = 0;
64 double size = bytes;
65 while (size > 1024 && i + 1 < sizeof(suffix) / sizeof(suffix[0])) {
66 size /= 1024.0;
67 i++;
68 }
69
70 // Set number of decimal places
71 const char *fmt = "%.0f";
72 if (size < 10)
73 fmt = "%.2f";
74 else if (size < 100)
75 fmt = "%1.f";
76 return agi::wxformat(fmt, size) + " " + suffix[i];
77 }
78
float_to_string(double val)79 std::string float_to_string(double val) {
80 std::string s = agi::format("%.3f", val);
81 size_t pos = s.find_last_not_of("0");
82 if (pos != s.find(".")) ++pos;
83 s.erase(begin(s) + pos, end(s));
84 return s;
85 }
86
SmallestPowerOf2(int x)87 int SmallestPowerOf2(int x) {
88 x--;
89 x |= (x >> 1);
90 x |= (x >> 2);
91 x |= (x >> 4);
92 x |= (x >> 8);
93 x |= (x >> 16);
94 x++;
95 return x;
96 }
97
98 #ifndef __WXMAC__
RestartAegisub()99 void RestartAegisub() {
100 config::opt->Flush();
101
102 #if defined(__WXMSW__)
103 wxExecute("\"" + wxStandardPaths::Get().GetExecutablePath() + "\"");
104 #else
105 wxExecute(wxStandardPaths::Get().GetExecutablePath());
106 #endif
107 }
108 #endif
109
ForwardMouseWheelEvent(wxWindow * source,wxMouseEvent & evt)110 bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt) {
111 wxWindow *target = wxFindWindowAtPoint(wxGetMousePosition());
112 if (!target || target == source) return true;
113
114 // If the mouse is over a parent of the source window just pretend it's
115 // over the source window, so that the mouse wheel works on borders and such
116 wxWindow *parent = source->GetParent();
117 while (parent && parent != target) parent = parent->GetParent();
118 if (parent == target) return true;
119
120 // Otherwise send it to the new target
121 target->GetEventHandler()->ProcessEvent(evt);
122 evt.Skip(false);
123 return false;
124 }
125
GetClipboard()126 std::string GetClipboard() {
127 wxString data;
128 wxClipboard *cb = wxClipboard::Get();
129 if (cb->Open()) {
130 if (cb->IsSupported(wxDF_TEXT)) {
131 wxTextDataObject raw_data;
132 cb->GetData(raw_data);
133 data = raw_data.GetText();
134 }
135 cb->Close();
136 }
137 return from_wx(data);
138 }
139
SetClipboard(std::string const & new_data)140 void SetClipboard(std::string const& new_data) {
141 wxClipboard *cb = wxClipboard::Get();
142 if (cb->Open()) {
143 cb->SetData(new wxTextDataObject(to_wx(new_data)));
144 cb->Flush();
145 cb->Close();
146 }
147 }
148
SetClipboard(wxBitmap const & new_data)149 void SetClipboard(wxBitmap const& new_data) {
150 wxClipboard *cb = wxClipboard::Get();
151 if (cb->Open()) {
152 cb->SetData(new wxBitmapDataObject(new_data));
153 cb->Flush();
154 cb->Close();
155 }
156 }
157
CleanCache(agi::fs::path const & directory,std::string const & file_type,uint64_t max_size,uint64_t max_files)158 void CleanCache(agi::fs::path const& directory, std::string const& file_type, uint64_t max_size, uint64_t max_files) {
159 static std::unique_ptr<agi::dispatch::Queue> queue;
160 if (!queue)
161 queue = agi::dispatch::Create();
162
163 max_size <<= 20;
164 if (max_files == 0)
165 max_files = std::numeric_limits<uint64_t>::max();
166 queue->Async([=]{
167 LOG_D("utils/clean_cache") << "cleaning " << directory/file_type;
168 uint64_t total_size = 0;
169 std::multimap<int64_t, agi::fs::path> cachefiles;
170 for (auto const& file : agi::fs::DirectoryIterator(directory, file_type)) {
171 agi::fs::path path = directory/file;
172 cachefiles.insert({agi::fs::ModifiedTime(path), path});
173 total_size += agi::fs::Size(path);
174 }
175
176 if (cachefiles.size() <= max_files && total_size <= max_size) {
177 LOG_D("utils/clean_cache") << agi::format("cache does not need cleaning (maxsize=%d, cursize=%d, maxfiles=%d, numfiles=%d), exiting"
178 , max_size, total_size, max_files, cachefiles.size());
179 return;
180 }
181
182 int deleted = 0;
183 for (auto const& i : cachefiles) {
184 // stop cleaning?
185 if ((total_size <= max_size && cachefiles.size() - deleted <= max_files) || cachefiles.size() - deleted < 2)
186 break;
187
188 uint64_t size = agi::fs::Size(i.second);
189 try {
190 agi::fs::Remove(i.second);
191 LOG_D("utils/clean_cache") << "deleted " << i.second;
192 }
193 catch (agi::Exception const& e) {
194 LOG_D("utils/clean_cache") << "failed to delete file " << i.second << ": " << e.GetMessage();
195 continue;
196 }
197
198 total_size -= size;
199 ++deleted;
200 }
201
202 LOG_D("utils/clean_cache") << "deleted " << deleted << " files, exiting";
203 });
204 }
205
206 #ifndef __WXOSX_COCOA__
207 // OS X implementation in osx_utils.mm
AddFullScreenButton(wxWindow *)208 void AddFullScreenButton(wxWindow *) { }
SetFloatOnParent(wxWindow *)209 void SetFloatOnParent(wxWindow *) { }
210
211 // OS X implementation in retina_helper.mm
RetinaHelper(wxWindow *)212 RetinaHelper::RetinaHelper(wxWindow *) { }
~RetinaHelper()213 RetinaHelper::~RetinaHelper() { }
GetScaleFactor() const214 int RetinaHelper::GetScaleFactor() const { return 1; }
215
216 // OS X implementation in scintilla_ime.mm
217 namespace osx { namespace ime {
inject(wxStyledTextCtrl *)218 void inject(wxStyledTextCtrl *) { }
invalidate(wxStyledTextCtrl *)219 void invalidate(wxStyledTextCtrl *) { }
process_key_event(wxStyledTextCtrl *,wxKeyEvent &)220 bool process_key_event(wxStyledTextCtrl *, wxKeyEvent&) { return false; }
221 } }
222 #endif
223
FontFace(std::string opt_prefix)224 wxString FontFace(std::string opt_prefix) {
225 opt_prefix += "/Font Face";
226 auto value = OPT_GET(opt_prefix)->GetString();
227 #ifdef __WXOSX_COCOA__
228 if (value.empty()) {
229 auto default_font = CTFontCreateUIFontForLanguage(kCTFontUserFontType, 0, nullptr);
230 auto default_font_name = CTFontCopyPostScriptName(default_font);
231 CFRelease(default_font);
232
233 auto utf8_str = CFStringGetCStringPtr(default_font_name, kCFStringEncodingUTF8);
234 if (utf8_str)
235 value = utf8_str;
236 else {
237 char buffer[1024];
238 CFStringGetCString(default_font_name, buffer, sizeof(buffer), kCFStringEncodingUTF8);
239 buffer[1023] = '\0';
240 value = buffer;
241 }
242
243 CFRelease(default_font_name);
244 }
245 #endif
246 return to_wx(value);
247 }
248
FileSelector(wxString const & message,std::string const & option_name,std::string const & default_filename,std::string const & default_extension,wxString const & wildcard,int flags,wxWindow * parent)249 agi::fs::path FileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, int flags, wxWindow *parent) {
250 wxString path;
251 if (!option_name.empty())
252 path = to_wx(OPT_GET(option_name)->GetString());
253 agi::fs::path filename = wxFileSelector(message, path, to_wx(default_filename), to_wx(default_extension), wildcard, flags, parent).wx_str();
254 if (!filename.empty() && !option_name.empty())
255 OPT_SET(option_name)->SetString(filename.parent_path().string());
256 return filename;
257 }
258
OpenFileSelector(wxString const & message,std::string const & option_name,std::string const & default_filename,std::string const & default_extension,wxString const & wildcard,wxWindow * parent)259 agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent) {
260 return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST, parent);
261 }
262
SaveFileSelector(wxString const & message,std::string const & option_name,std::string const & default_filename,std::string const & default_extension,wxString const & wildcard,wxWindow * parent)263 agi::fs::path SaveFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent) {
264 return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
265 }
266
LocalizedLanguageName(wxString const & lang)267 wxString LocalizedLanguageName(wxString const& lang) {
268 Locale iculoc(lang.c_str());
269 if (!iculoc.isBogus()) {
270 UnicodeString ustr;
271 iculoc.getDisplayName(iculoc, ustr);
272 #ifdef _MSC_VER
273 return wxString(ustr.getBuffer());
274 #else
275 std::string utf8;
276 ustr.toUTF8String(utf8);
277 return to_wx(utf8);
278 #endif
279 }
280
281 if (auto info = wxLocale::FindLanguageInfo(lang))
282 return info->Description;
283 return lang;
284 }
285