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