OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/clipboard/clipboard.h" | 5 #include "ui/base/clipboard/clipboard_android.h" |
6 | 6 |
7 #include "base/android/jni_string.h" | 7 #include "base/android/jni_string.h" |
8 #include "base/lazy_instance.h" | 8 #include "base/lazy_instance.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
11 #include "base/synchronization/lock.h" | 11 #include "base/synchronization/lock.h" |
12 #include "jni/Clipboard_jni.h" | 12 #include "jni/Clipboard_jni.h" |
13 #include "third_party/skia/include/core/SkBitmap.h" | 13 #include "third_party/skia/include/core/SkBitmap.h" |
14 #include "ui/base/clipboard/clipboard_android_initialization.h" | |
15 #include "ui/gfx/size.h" | 14 #include "ui/gfx/size.h" |
16 | 15 |
17 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, | 16 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, |
18 // HTML+text now that Android's clipboard system supports them, then nuke the | 17 // HTML+text now that Android's clipboard system supports them, then nuke the |
19 // legacy implementation note below. | 18 // legacy implementation note below. |
20 | 19 |
21 // Legacy implementation note: | 20 // Legacy implementation note: |
22 // The Android clipboard system used to only support text format. So we used the | 21 // The Android clipboard system used to only support text format. So we used the |
23 // Android system when some text was added or retrieved from the system. For | 22 // Android system when some text was added or retrieved from the system. For |
24 // anything else, we STILL store the value in some process wide static | 23 // anything else, we STILL store the value in some process wide static |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 } | 170 } |
172 } | 171 } |
173 it = map_.find(kHTMLFormat); | 172 it = map_.find(kHTMLFormat); |
174 if (it != map_.end()) { | 173 if (it != map_.end()) { |
175 map_.erase(kHTMLFormat); | 174 map_.erase(kHTMLFormat); |
176 } | 175 } |
177 } | 176 } |
178 | 177 |
179 } // namespace | 178 } // namespace |
180 | 179 |
| 180 // Clipboard::FormatType implementation. |
181 Clipboard::FormatType::FormatType() { | 181 Clipboard::FormatType::FormatType() { |
182 } | 182 } |
183 | 183 |
184 Clipboard::FormatType::FormatType(const std::string& native_format) | 184 Clipboard::FormatType::FormatType(const std::string& native_format) |
185 : data_(native_format) { | 185 : data_(native_format) { |
186 } | 186 } |
187 | 187 |
188 Clipboard::FormatType::~FormatType() { | 188 Clipboard::FormatType::~FormatType() { |
189 } | 189 } |
190 | 190 |
191 std::string Clipboard::FormatType::Serialize() const { | 191 std::string Clipboard::FormatType::Serialize() const { |
192 return data_; | 192 return data_; |
193 } | 193 } |
194 | 194 |
195 // static | 195 // static |
196 Clipboard::FormatType Clipboard::FormatType::Deserialize( | 196 Clipboard::FormatType Clipboard::FormatType::Deserialize( |
197 const std::string& serialization) { | 197 const std::string& serialization) { |
198 return FormatType(serialization); | 198 return FormatType(serialization); |
199 } | 199 } |
200 | 200 |
201 bool Clipboard::FormatType::Equals(const FormatType& other) const { | 201 bool Clipboard::FormatType::Equals(const FormatType& other) const { |
202 return data_ == other.data_; | 202 return data_ == other.data_; |
203 } | 203 } |
204 | 204 |
205 Clipboard::Clipboard() { | 205 // Miscellaneous Clipboard definitions. |
| 206 // static |
| 207 Clipboard::FormatType Clipboard::GetFormatType( |
| 208 const std::string& format_string) { |
| 209 return FormatType::Deserialize(format_string); |
| 210 } |
| 211 |
| 212 // static |
| 213 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |
| 214 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); |
| 215 return type; |
| 216 } |
| 217 |
| 218 // static |
| 219 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { |
| 220 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); |
| 221 return type; |
| 222 } |
| 223 |
| 224 // static |
| 225 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { |
| 226 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat)); |
| 227 return type; |
| 228 } |
| 229 |
| 230 // static |
| 231 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { |
| 232 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat)); |
| 233 return type; |
| 234 } |
| 235 |
| 236 // static |
| 237 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { |
| 238 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kRTFFormat)); |
| 239 return type; |
| 240 } |
| 241 |
| 242 // static |
| 243 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { |
| 244 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat)); |
| 245 return type; |
| 246 } |
| 247 |
| 248 // static |
| 249 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
| 250 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
| 251 return type; |
| 252 } |
| 253 |
| 254 // static |
| 255 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { |
| 256 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); |
| 257 return type; |
| 258 } |
| 259 |
| 260 // static |
| 261 Clipboard* Clipboard::Create() { |
| 262 return new ClipboardAndroid; |
| 263 } |
| 264 |
| 265 // ClipboardAndroid implementation. |
| 266 ClipboardAndroid::ClipboardAndroid() { |
206 DCHECK(CalledOnValidThread()); | 267 DCHECK(CalledOnValidThread()); |
207 } | 268 } |
208 | 269 |
209 Clipboard::~Clipboard() { | 270 ClipboardAndroid::~ClipboardAndroid() { |
210 DCHECK(CalledOnValidThread()); | 271 DCHECK(CalledOnValidThread()); |
211 } | 272 } |
212 | 273 |
213 // Main entry point used to write several values in the clipboard. | 274 uint64 ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) { |
214 void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) { | |
215 DCHECK(CalledOnValidThread()); | |
216 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
217 g_map.Get().Clear(); | |
218 for (ObjectMap::const_iterator iter = objects.begin(); | |
219 iter != objects.end(); ++iter) { | |
220 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | |
221 } | |
222 } | |
223 | |
224 uint64 Clipboard::GetSequenceNumber(ClipboardType /* type */) { | |
225 DCHECK(CalledOnValidThread()); | 275 DCHECK(CalledOnValidThread()); |
226 // TODO: implement this. For now this interface will advertise | 276 // TODO: implement this. For now this interface will advertise |
227 // that the clipboard never changes. That's fine as long as we | 277 // that the clipboard never changes. That's fine as long as we |
228 // don't rely on this signal. | 278 // don't rely on this signal. |
229 return 0; | 279 return 0; |
230 } | 280 } |
231 | 281 |
232 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, | 282 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format, |
233 ClipboardType type) const { | 283 ClipboardType type) const { |
234 DCHECK(CalledOnValidThread()); | 284 DCHECK(CalledOnValidThread()); |
235 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 285 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
236 return g_map.Get().HasFormat(format.data()); | 286 return g_map.Get().HasFormat(format.ToString()); |
237 } | 287 } |
238 | 288 |
239 void Clipboard::Clear(ClipboardType type) { | 289 void ClipboardAndroid::Clear(ClipboardType type) { |
240 DCHECK(CalledOnValidThread()); | 290 DCHECK(CalledOnValidThread()); |
241 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 291 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
242 g_map.Get().Clear(); | 292 g_map.Get().Clear(); |
243 } | 293 } |
244 | 294 |
245 void Clipboard::ReadAvailableTypes(ClipboardType type, | 295 void ClipboardAndroid::ReadAvailableTypes(ClipboardType type, |
246 std::vector<base::string16>* types, | 296 std::vector<base::string16>* types, |
247 bool* contains_filenames) const { | 297 bool* contains_filenames) const { |
248 DCHECK(CalledOnValidThread()); | 298 DCHECK(CalledOnValidThread()); |
249 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 299 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
250 | 300 |
251 if (!types || !contains_filenames) { | 301 if (!types || !contains_filenames) { |
252 NOTREACHED(); | 302 NOTREACHED(); |
253 return; | 303 return; |
254 } | 304 } |
255 | 305 |
256 NOTIMPLEMENTED(); | 306 NOTIMPLEMENTED(); |
257 | 307 |
258 types->clear(); | 308 types->clear(); |
259 *contains_filenames = false; | 309 *contains_filenames = false; |
260 } | 310 } |
261 | 311 |
262 void Clipboard::ReadText(ClipboardType type, base::string16* result) const { | 312 void ClipboardAndroid::ReadText(ClipboardType type, |
| 313 base::string16* result) const { |
263 DCHECK(CalledOnValidThread()); | 314 DCHECK(CalledOnValidThread()); |
264 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 315 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
265 std::string utf8; | 316 std::string utf8; |
266 ReadAsciiText(type, &utf8); | 317 ReadAsciiText(type, &utf8); |
267 *result = base::UTF8ToUTF16(utf8); | 318 *result = base::UTF8ToUTF16(utf8); |
268 } | 319 } |
269 | 320 |
270 void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const { | 321 void ClipboardAndroid::ReadAsciiText(ClipboardType type, |
| 322 std::string* result) const { |
271 DCHECK(CalledOnValidThread()); | 323 DCHECK(CalledOnValidThread()); |
272 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 324 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
273 *result = g_map.Get().Get(kPlainTextFormat); | 325 *result = g_map.Get().Get(kPlainTextFormat); |
274 } | 326 } |
275 | 327 |
276 // Note: |src_url| isn't really used. It is only implemented in Windows | 328 // Note: |src_url| isn't really used. It is only implemented in Windows |
277 void Clipboard::ReadHTML(ClipboardType type, | 329 void ClipboardAndroid::ReadHTML(ClipboardType type, |
278 base::string16* markup, | 330 base::string16* markup, |
279 std::string* src_url, | 331 std::string* src_url, |
280 uint32* fragment_start, | 332 uint32* fragment_start, |
281 uint32* fragment_end) const { | 333 uint32* fragment_end) const { |
282 DCHECK(CalledOnValidThread()); | 334 DCHECK(CalledOnValidThread()); |
283 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 335 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
284 if (src_url) | 336 if (src_url) |
285 src_url->clear(); | 337 src_url->clear(); |
286 | 338 |
287 std::string input = g_map.Get().Get(kHTMLFormat); | 339 std::string input = g_map.Get().Get(kHTMLFormat); |
288 *markup = base::UTF8ToUTF16(input); | 340 *markup = base::UTF8ToUTF16(input); |
289 | 341 |
290 *fragment_start = 0; | 342 *fragment_start = 0; |
291 *fragment_end = static_cast<uint32>(markup->length()); | 343 *fragment_end = static_cast<uint32>(markup->length()); |
292 } | 344 } |
293 | 345 |
294 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const { | 346 void ClipboardAndroid::ReadRTF(ClipboardType type, std::string* result) const { |
295 DCHECK(CalledOnValidThread()); | 347 DCHECK(CalledOnValidThread()); |
296 NOTIMPLEMENTED(); | 348 NOTIMPLEMENTED(); |
297 } | 349 } |
298 | 350 |
299 SkBitmap Clipboard::ReadImage(ClipboardType type) const { | 351 SkBitmap ClipboardAndroid::ReadImage(ClipboardType type) const { |
300 DCHECK(CalledOnValidThread()); | 352 DCHECK(CalledOnValidThread()); |
301 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 353 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
302 std::string input = g_map.Get().Get(kBitmapFormat); | 354 std::string input = g_map.Get().Get(kBitmapFormat); |
303 | 355 |
304 SkBitmap bmp; | 356 SkBitmap bmp; |
305 if (!input.empty()) { | 357 if (!input.empty()) { |
306 DCHECK_LE(sizeof(gfx::Size), input.size()); | 358 DCHECK_LE(sizeof(gfx::Size), input.size()); |
307 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.data()); | 359 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.data()); |
308 | 360 |
309 bmp.allocN32Pixels(size->width(), size->height()); | 361 bmp.allocN32Pixels(size->width(), size->height()); |
310 | 362 |
311 DCHECK_EQ(sizeof(gfx::Size) + bmp.getSize(), input.size()); | 363 DCHECK_EQ(sizeof(gfx::Size) + bmp.getSize(), input.size()); |
312 | 364 |
313 memcpy(bmp.getPixels(), input.data() + sizeof(gfx::Size), bmp.getSize()); | 365 memcpy(bmp.getPixels(), input.data() + sizeof(gfx::Size), bmp.getSize()); |
314 } | 366 } |
315 return bmp; | 367 return bmp; |
316 } | 368 } |
317 | 369 |
318 void Clipboard::ReadCustomData(ClipboardType clipboard_type, | 370 void ClipboardAndroid::ReadCustomData(ClipboardType clipboard_type, |
319 const base::string16& type, | 371 const base::string16& type, |
320 base::string16* result) const { | 372 base::string16* result) const { |
321 DCHECK(CalledOnValidThread()); | 373 DCHECK(CalledOnValidThread()); |
322 NOTIMPLEMENTED(); | 374 NOTIMPLEMENTED(); |
323 } | 375 } |
324 | 376 |
325 void Clipboard::ReadBookmark(base::string16* title, std::string* url) const { | 377 void ClipboardAndroid::ReadBookmark(base::string16* title, |
| 378 std::string* url) const { |
326 DCHECK(CalledOnValidThread()); | 379 DCHECK(CalledOnValidThread()); |
327 NOTIMPLEMENTED(); | 380 NOTIMPLEMENTED(); |
328 } | 381 } |
329 | 382 |
330 void Clipboard::ReadData(const Clipboard::FormatType& format, | 383 void ClipboardAndroid::ReadData(const Clipboard::FormatType& format, |
331 std::string* result) const { | 384 std::string* result) const { |
332 DCHECK(CalledOnValidThread()); | 385 DCHECK(CalledOnValidThread()); |
333 *result = g_map.Get().Get(format.data()); | 386 *result = g_map.Get().Get(format.ToString()); |
334 } | 387 } |
335 | 388 |
336 // static | 389 // Main entry point used to write several values in the clipboard. |
337 Clipboard::FormatType Clipboard::GetFormatType( | 390 void ClipboardAndroid::WriteObjects(ClipboardType type, |
338 const std::string& format_string) { | 391 const ObjectMap& objects) { |
339 return FormatType::Deserialize(format_string); | 392 DCHECK(CalledOnValidThread()); |
| 393 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
| 394 g_map.Get().Clear(); |
| 395 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); |
| 396 ++iter) { |
| 397 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
| 398 } |
340 } | 399 } |
341 | 400 |
342 // static | 401 void ClipboardAndroid::WriteText(const char* text_data, size_t text_len) { |
343 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | |
344 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); | |
345 return type; | |
346 } | |
347 | |
348 // static | |
349 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { | |
350 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); | |
351 return type; | |
352 } | |
353 | |
354 // static | |
355 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { | |
356 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat)); | |
357 return type; | |
358 } | |
359 | |
360 // static | |
361 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { | |
362 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat)); | |
363 return type; | |
364 } | |
365 | |
366 // static | |
367 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { | |
368 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kRTFFormat)); | |
369 return type; | |
370 } | |
371 | |
372 // static | |
373 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { | |
374 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat)); | |
375 return type; | |
376 } | |
377 | |
378 // static | |
379 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | |
380 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | |
381 return type; | |
382 } | |
383 | |
384 // static | |
385 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { | |
386 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); | |
387 return type; | |
388 } | |
389 | |
390 void Clipboard::WriteText(const char* text_data, size_t text_len) { | |
391 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); | 402 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); |
392 } | 403 } |
393 | 404 |
394 void Clipboard::WriteHTML(const char* markup_data, | 405 void ClipboardAndroid::WriteHTML(const char* markup_data, |
395 size_t markup_len, | 406 size_t markup_len, |
396 const char* url_data, | 407 const char* url_data, |
397 size_t url_len) { | 408 size_t url_len) { |
398 g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); | 409 g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); |
399 } | 410 } |
400 | 411 |
401 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { | 412 void ClipboardAndroid::WriteRTF(const char* rtf_data, size_t data_len) { |
402 NOTIMPLEMENTED(); | 413 NOTIMPLEMENTED(); |
403 } | 414 } |
404 | 415 |
405 // Note: according to other platforms implementations, this really writes the | 416 // Note: according to other platforms implementations, this really writes the |
406 // URL spec. | 417 // URL spec. |
407 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, | 418 void ClipboardAndroid::WriteBookmark(const char* title_data, |
408 const char* url_data, size_t url_len) { | 419 size_t title_len, |
| 420 const char* url_data, |
| 421 size_t url_len) { |
409 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); | 422 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); |
410 } | 423 } |
411 | 424 |
412 // Write an extra flavor that signifies WebKit was the last to modify the | 425 // Write an extra flavor that signifies WebKit was the last to modify the |
413 // pasteboard. This flavor has no data. | 426 // pasteboard. This flavor has no data. |
414 void Clipboard::WriteWebSmartPaste() { | 427 void ClipboardAndroid::WriteWebSmartPaste() { |
415 g_map.Get().Set(kWebKitSmartPasteFormat, std::string()); | 428 g_map.Get().Set(kWebKitSmartPasteFormat, std::string()); |
416 } | 429 } |
417 | 430 |
418 // Note: we implement this to pass all unit tests but it is currently unclear | 431 // Note: we implement this to pass all unit tests but it is currently unclear |
419 // how some code would consume this. | 432 // how some code would consume this. |
420 void Clipboard::WriteBitmap(const SkBitmap& bitmap) { | 433 void ClipboardAndroid::WriteBitmap(const SkBitmap& bitmap) { |
421 gfx::Size size(bitmap.width(), bitmap.height()); | 434 gfx::Size size(bitmap.width(), bitmap.height()); |
422 | 435 |
423 std::string packed(reinterpret_cast<const char*>(&size), sizeof(size)); | 436 std::string packed(reinterpret_cast<const char*>(&size), sizeof(size)); |
424 { | 437 { |
425 SkAutoLockPixels bitmap_lock(bitmap); | 438 SkAutoLockPixels bitmap_lock(bitmap); |
426 packed += std::string(static_cast<const char*>(bitmap.getPixels()), | 439 packed += std::string(static_cast<const char*>(bitmap.getPixels()), |
427 bitmap.getSize()); | 440 bitmap.getSize()); |
428 } | 441 } |
429 g_map.Get().Set(kBitmapFormat, packed); | 442 g_map.Get().Set(kBitmapFormat, packed); |
430 } | 443 } |
431 | 444 |
432 void Clipboard::WriteData(const Clipboard::FormatType& format, | 445 void ClipboardAndroid::WriteData(const Clipboard::FormatType& format, |
433 const char* data_data, size_t data_len) { | 446 const char* data_data, |
434 g_map.Get().Set(format.data(), std::string(data_data, data_len)); | 447 size_t data_len) { |
| 448 g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); |
435 } | 449 } |
436 | 450 |
437 // See clipboard_android_initialization.h for more information. | |
438 bool RegisterClipboardAndroid(JNIEnv* env) { | 451 bool RegisterClipboardAndroid(JNIEnv* env) { |
439 return RegisterNativesImpl(env); | 452 return RegisterNativesImpl(env); |
440 } | 453 } |
441 | 454 |
442 } // namespace ui | 455 } // namespace ui |
OLD | NEW |