Chromium Code Reviews| 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/dragdrop/os_exchange_data_provider_aurax11.h" | 5 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/message_pump_aurax11.h" | 8 #include "base/message_pump_aurax11.h" |
| 9 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "net/base/net_util.h" | 11 #include "net/base/net_util.h" |
| 12 #include "ui/base/clipboard/clipboard.h" | 12 #include "ui/base/clipboard/clipboard.h" |
| 13 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 13 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
| 14 #include "ui/base/dragdrop/desktop_selection_provider_aurax11.h" | |
| 15 #include "ui/base/x/selection_utils.h" | 14 #include "ui/base/x/selection_utils.h" |
| 16 #include "ui/base/x/x11_util.h" | 15 #include "ui/base/x/x11_util.h" |
| 17 | 16 |
| 18 // Note: the GetBlah() methods are used immediately by the | 17 // Note: the GetBlah() methods are used immediately by the |
| 19 // web_contents_view_aura.cc:PrepareWebDropData(), while the omnibox is a | 18 // web_contents_view_aura.cc:PrepareWebDropData(), while the omnibox is a |
| 20 // little more discriminating and calls HasBlah() before trying to get the | 19 // little more discriminating and calls HasBlah() before trying to get the |
| 21 // information. | 20 // information. |
| 22 | 21 |
| 23 namespace ui { | 22 namespace ui { |
| 24 | 23 |
| 25 namespace { | 24 namespace { |
| 26 | 25 |
| 27 const char kDndSelection[] = "XdndSelection"; | 26 const char kDndSelection[] = "XdndSelection"; |
| 28 | 27 |
| 29 const char* kAtomsToCache[] = { | 28 const char* kAtomsToCache[] = { |
| 30 kString, | 29 kString, |
| 31 kText, | 30 kText, |
| 32 kUtf8String, | 31 kUtf8String, |
| 33 kDndSelection, | 32 kDndSelection, |
| 34 Clipboard::kMimeTypeURIList, | 33 Clipboard::kMimeTypeURIList, |
| 35 kMimeTypeMozillaURL, | 34 kMimeTypeMozillaURL, |
| 35 Clipboard::kMimeTypeText, | |
| 36 NULL | 36 NULL |
| 37 }; | 37 }; |
| 38 | 38 |
| 39 } // namespace | 39 } // namespace |
| 40 | 40 |
| 41 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11( | 41 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11( |
| 42 ui::DesktopSelectionProviderAuraX11* provider, | |
| 43 ::Window x_window, | 42 ::Window x_window, |
| 44 const std::vector< ::Atom> targets) | 43 scoped_ptr<SelectionFormatMap> selection) |
| 45 : x_display_(GetXDisplay()), | 44 : x_display_(GetXDisplay()), |
| 46 x_root_window_(DefaultRootWindow(x_display_)), | 45 x_root_window_(DefaultRootWindow(x_display_)), |
| 47 own_window_(false), | 46 own_window_(false), |
| 48 selection_event_provider_(provider), | |
| 49 x_window_(x_window), | 47 x_window_(x_window), |
| 50 atom_cache_(x_display_, kAtomsToCache), | 48 atom_cache_(x_display_, kAtomsToCache), |
| 51 selection_requestor_(x_display_, x_window_, | 49 format_map_(selection.Pass()), |
| 52 atom_cache_.GetAtom(kDndSelection)), | |
| 53 selection_owner_(x_display_, x_window_, | 50 selection_owner_(x_display_, x_window_, |
| 54 atom_cache_.GetAtom(kDndSelection)), | 51 atom_cache_.GetAtom(kDndSelection)) { |
| 55 targets_(targets) { | |
| 56 // We don't know all possible MIME types at compile time. | 52 // We don't know all possible MIME types at compile time. |
| 57 atom_cache_.allow_uncached_atoms(); | 53 atom_cache_.allow_uncached_atoms(); |
| 58 | |
| 59 selection_event_provider_->SetDropHandler(this); | |
| 60 } | 54 } |
| 61 | 55 |
| 62 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11() | 56 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11() |
| 63 : x_display_(GetXDisplay()), | 57 : x_display_(GetXDisplay()), |
| 64 x_root_window_(DefaultRootWindow(x_display_)), | 58 x_root_window_(DefaultRootWindow(x_display_)), |
| 65 own_window_(true), | 59 own_window_(true), |
| 66 selection_event_provider_(NULL), | |
| 67 x_window_(XCreateWindow( | 60 x_window_(XCreateWindow( |
| 68 x_display_, | 61 x_display_, |
| 69 x_root_window_, | 62 x_root_window_, |
| 70 -100, -100, 10, 10, // x, y, width, height | 63 -100, -100, 10, 10, // x, y, width, height |
| 71 0, // border width | 64 0, // border width |
| 72 CopyFromParent, // depth | 65 CopyFromParent, // depth |
| 73 InputOnly, | 66 InputOnly, |
| 74 CopyFromParent, // visual | 67 CopyFromParent, // visual |
| 75 0, | 68 0, |
| 76 NULL)), | 69 NULL)), |
| 77 atom_cache_(x_display_, kAtomsToCache), | 70 atom_cache_(x_display_, kAtomsToCache), |
| 78 selection_requestor_(x_display_, x_window_, | 71 format_map_(new SelectionFormatMap), |
| 79 atom_cache_.GetAtom(kDndSelection)), | |
| 80 selection_owner_(x_display_, x_window_, | 72 selection_owner_(x_display_, x_window_, |
| 81 atom_cache_.GetAtom(kDndSelection)) { | 73 atom_cache_.GetAtom(kDndSelection)) { |
| 82 // We don't know all possible MIME types at compile time. | 74 // We don't know all possible MIME types at compile time. |
| 83 atom_cache_.allow_uncached_atoms(); | 75 atom_cache_.allow_uncached_atoms(); |
| 84 | 76 |
| 85 XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window"); | 77 XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window"); |
| 86 | 78 |
| 87 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, x_window_); | 79 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, x_window_); |
| 88 } | 80 } |
| 89 | 81 |
| 90 OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() { | 82 OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() { |
| 91 if (own_window_) { | 83 if (own_window_) { |
| 92 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_); | 84 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_); |
| 93 XDestroyWindow(x_display_, x_window_); | 85 XDestroyWindow(x_display_, x_window_); |
| 94 } else { | |
| 95 selection_event_provider_->SetDropHandler(NULL); | |
| 96 } | 86 } |
| 97 } | 87 } |
| 98 | 88 |
| 99 void OSExchangeDataProviderAuraX11::OnSelectionNotify( | 89 void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const { |
| 100 const XSelectionEvent& event) { | 90 selection_owner_.TakeOwnershipOfSelection( |
| 101 selection_requestor_.OnSelectionNotify(event); | 91 scoped_ptr<SelectionFormatMap>(format_map_->Clone())); |
| 102 } | 92 } |
| 103 | 93 |
| 104 void OSExchangeDataProviderAuraX11::SetString(const string16& data) { | 94 void OSExchangeDataProviderAuraX11::RetrieveTargets( |
| 105 NOTIMPLEMENTED(); | 95 std::vector<Atom>* targets) const { |
| 96 selection_owner_.RetrieveTargets(targets); | |
| 97 } | |
| 98 | |
| 99 scoped_ptr<SelectionFormatMap> | |
| 100 OSExchangeDataProviderAuraX11::CloneFormatMap() const { | |
| 101 // We clone the |selection_owner_|'s format map instead of our own in case | |
| 102 // ours has been modified since TakeOwnershipOfSelection() was called. | |
| 103 return selection_owner_.selection_format_map()->Clone(); | |
| 104 } | |
| 105 | |
| 106 void OSExchangeDataProviderAuraX11::SetString(const string16& text_data) { | |
| 107 std::string utf8 = UTF16ToUTF8(text_data); | |
| 108 | |
| 109 size_t text_len = utf8.size(); | |
|
sky
2013/06/18 14:09:45
Document ownership.
| |
| 110 char* data = new char[text_len]; | |
| 111 memcpy(data, utf8.c_str(), text_len); | |
| 112 | |
| 113 format_map_->Insert( | |
| 114 atom_cache_.GetAtom(Clipboard::kMimeTypeText), data, text_len); | |
| 115 format_map_->Insert( | |
| 116 atom_cache_.GetAtom(kText), data, text_len); | |
| 117 format_map_->Insert( | |
| 118 atom_cache_.GetAtom(kString), data, text_len); | |
| 119 format_map_->Insert( | |
| 120 atom_cache_.GetAtom(kUtf8String), data, text_len); | |
| 106 } | 121 } |
| 107 | 122 |
| 108 void OSExchangeDataProviderAuraX11::SetURL(const GURL& url, | 123 void OSExchangeDataProviderAuraX11::SetURL(const GURL& url, |
| 109 const string16& title) { | 124 const string16& title) { |
| 110 NOTIMPLEMENTED(); | 125 NOTIMPLEMENTED(); |
| 111 } | 126 } |
| 112 | 127 |
| 113 void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) { | 128 void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) { |
| 114 NOTIMPLEMENTED(); | 129 NOTIMPLEMENTED(); |
| 115 } | 130 } |
| 116 | 131 |
| 117 void OSExchangeDataProviderAuraX11::SetFilenames( | 132 void OSExchangeDataProviderAuraX11::SetFilenames( |
| 118 const std::vector<OSExchangeData::FileInfo>& filenames) { | 133 const std::vector<OSExchangeData::FileInfo>& filenames) { |
| 119 NOTIMPLEMENTED(); | 134 NOTIMPLEMENTED(); |
| 120 } | 135 } |
| 121 | 136 |
| 122 void OSExchangeDataProviderAuraX11::SetPickledData( | 137 void OSExchangeDataProviderAuraX11::SetPickledData( |
| 123 const OSExchangeData::CustomFormat& format, | 138 const OSExchangeData::CustomFormat& format, |
| 124 const Pickle& data) { | 139 const Pickle& data) { |
| 125 NOTIMPLEMENTED(); | 140 NOTIMPLEMENTED(); |
| 126 } | 141 } |
| 127 | 142 |
| 128 bool OSExchangeDataProviderAuraX11::GetString(string16* result) const { | 143 bool OSExchangeDataProviderAuraX11::GetString(string16* result) const { |
| 129 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_); | 144 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_); |
| 130 std::vector< ::Atom> requested_types; | 145 std::vector< ::Atom> requested_types; |
| 131 ui::GetAtomIntersection(text_atoms, targets_, &requested_types); | 146 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types); |
| 132 | 147 |
| 133 scoped_ptr<ui::SelectionData> data( | 148 scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types)); |
| 134 selection_requestor_.RequestAndWaitForTypes(requested_types)); | |
| 135 if (data) { | 149 if (data) { |
| 136 std::string text = data->GetText(); | 150 std::string text = data->GetText(); |
| 137 *result = UTF8ToUTF16(text); | 151 *result = UTF8ToUTF16(text); |
| 138 return true; | 152 return true; |
| 139 } | 153 } |
| 140 | 154 |
| 141 return false; | 155 return false; |
| 142 } | 156 } |
| 143 | 157 |
| 144 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(GURL* url, | 158 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(GURL* url, |
| 145 string16* title) const { | 159 string16* title) const { |
| 146 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_); | 160 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_); |
| 147 std::vector< ::Atom> requested_types; | 161 std::vector< ::Atom> requested_types; |
| 148 ui::GetAtomIntersection(url_atoms, targets_, &requested_types); | 162 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); |
| 149 | 163 |
| 150 scoped_ptr<ui::SelectionData> data( | 164 scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types)); |
| 151 selection_requestor_.RequestAndWaitForTypes(requested_types)); | |
| 152 if (data) { | 165 if (data) { |
| 153 // TODO(erg): Technically, both of these forms can accept multiple URLs, | 166 // TODO(erg): Technically, both of these forms can accept multiple URLs, |
| 154 // but that doesn't match the assumptions of the rest of the system which | 167 // but that doesn't match the assumptions of the rest of the system which |
| 155 // expect single types. | 168 // expect single types. |
| 156 | 169 |
| 157 if (data->type() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { | 170 if (data->type() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { |
| 158 // Mozilla URLs are (UTF16: URL, newline, title). | 171 // Mozilla URLs are (UTF16: URL, newline, title). |
| 159 string16 unparsed; | 172 string16 unparsed; |
| 160 data->AssignTo(&unparsed); | 173 data->AssignTo(&unparsed); |
| 161 | 174 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 206 bool OSExchangeDataProviderAuraX11::GetPickledData( | 219 bool OSExchangeDataProviderAuraX11::GetPickledData( |
| 207 const OSExchangeData::CustomFormat& format, | 220 const OSExchangeData::CustomFormat& format, |
| 208 Pickle* data) const { | 221 Pickle* data) const { |
| 209 NOTIMPLEMENTED(); | 222 NOTIMPLEMENTED(); |
| 210 return false; | 223 return false; |
| 211 } | 224 } |
| 212 | 225 |
| 213 bool OSExchangeDataProviderAuraX11::HasString() const { | 226 bool OSExchangeDataProviderAuraX11::HasString() const { |
| 214 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_); | 227 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_); |
| 215 std::vector< ::Atom> requested_types; | 228 std::vector< ::Atom> requested_types; |
| 216 ui::GetAtomIntersection(text_atoms, targets_, &requested_types); | 229 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types); |
| 217 return !requested_types.empty(); | 230 return !requested_types.empty(); |
| 218 } | 231 } |
| 219 | 232 |
| 220 bool OSExchangeDataProviderAuraX11::HasURL() const { | 233 bool OSExchangeDataProviderAuraX11::HasURL() const { |
| 221 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_); | 234 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_); |
| 222 std::vector< ::Atom> requested_types; | 235 std::vector< ::Atom> requested_types; |
| 223 ui::GetAtomIntersection(url_atoms, targets_, &requested_types); | 236 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); |
| 224 return !requested_types.empty(); | 237 return !requested_types.empty(); |
| 225 } | 238 } |
| 226 | 239 |
| 227 bool OSExchangeDataProviderAuraX11::HasFile() const { | 240 bool OSExchangeDataProviderAuraX11::HasFile() const { |
| 228 // On X11, files are passed by URL and aren't separate. | 241 // On X11, files are passed by URL and aren't separate. |
| 229 return false; | 242 return false; |
| 230 } | 243 } |
| 231 | 244 |
| 232 bool OSExchangeDataProviderAuraX11::HasCustomFormat( | 245 bool OSExchangeDataProviderAuraX11::HasCustomFormat( |
| 233 const OSExchangeData::CustomFormat& format) const { | 246 const OSExchangeData::CustomFormat& format) const { |
| 234 std::vector< ::Atom> url_atoms; | 247 std::vector< ::Atom> url_atoms; |
| 235 url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); | 248 url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); |
| 236 std::vector< ::Atom> requested_types; | 249 std::vector< ::Atom> requested_types; |
| 237 ui::GetAtomIntersection(url_atoms, targets_, &requested_types); | 250 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); |
| 238 | 251 |
| 239 return !requested_types.empty(); | 252 return !requested_types.empty(); |
| 240 } | 253 } |
| 241 | 254 |
| 242 void OSExchangeDataProviderAuraX11::SetHtml(const string16& html, | 255 void OSExchangeDataProviderAuraX11::SetHtml(const string16& html, |
| 243 const GURL& base_url) { | 256 const GURL& base_url) { |
| 244 NOTIMPLEMENTED(); | 257 NOTIMPLEMENTED(); |
| 245 } | 258 } |
| 246 | 259 |
| 247 bool OSExchangeDataProviderAuraX11::GetHtml(string16* html, | 260 bool OSExchangeDataProviderAuraX11::GetHtml(string16* html, |
| 248 GURL* base_url) const { | 261 GURL* base_url) const { |
| 249 std::vector< ::Atom> url_atoms; | 262 std::vector< ::Atom> url_atoms; |
| 250 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); | 263 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); |
| 251 std::vector< ::Atom> requested_types; | 264 std::vector< ::Atom> requested_types; |
| 252 ui::GetAtomIntersection(url_atoms, targets_, &requested_types); | 265 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); |
| 253 | 266 |
| 254 scoped_ptr<ui::SelectionData> data( | 267 scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types)); |
| 255 selection_requestor_.RequestAndWaitForTypes(requested_types)); | |
| 256 if (data) { | 268 if (data) { |
| 257 *html = data->GetHtml(); | 269 *html = data->GetHtml(); |
| 258 *base_url = GURL(); | 270 *base_url = GURL(); |
| 259 return true; | 271 return true; |
| 260 } | 272 } |
| 261 | 273 |
| 262 return false; | 274 return false; |
| 263 } | 275 } |
| 264 | 276 |
| 265 bool OSExchangeDataProviderAuraX11::HasHtml() const { | 277 bool OSExchangeDataProviderAuraX11::HasHtml() const { |
| 266 std::vector< ::Atom> url_atoms; | 278 std::vector< ::Atom> url_atoms; |
| 267 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); | 279 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); |
| 268 std::vector< ::Atom> requested_types; | 280 std::vector< ::Atom> requested_types; |
| 269 ui::GetAtomIntersection(url_atoms, targets_, &requested_types); | 281 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); |
| 270 | 282 |
| 271 return !requested_types.empty(); | 283 return !requested_types.empty(); |
| 272 } | 284 } |
| 273 | 285 |
| 274 void OSExchangeDataProviderAuraX11::SetDragImage( | 286 void OSExchangeDataProviderAuraX11::SetDragImage( |
| 275 const gfx::ImageSkia& image, | 287 const gfx::ImageSkia& image, |
| 276 const gfx::Vector2d& cursor_offset) { | 288 const gfx::Vector2d& cursor_offset) { |
| 277 NOTIMPLEMENTED(); | 289 drag_image_ = image; |
| 290 drag_image_offset_ = cursor_offset; | |
| 278 } | 291 } |
| 279 | 292 |
| 280 const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const { | 293 const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const { |
| 281 NOTIMPLEMENTED(); | |
| 282 return drag_image_; | 294 return drag_image_; |
| 283 } | 295 } |
| 284 | 296 |
| 285 const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const { | 297 const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const { |
| 286 NOTIMPLEMENTED(); | |
| 287 return drag_image_offset_; | 298 return drag_image_offset_; |
| 288 } | 299 } |
| 289 | 300 |
| 290 bool OSExchangeDataProviderAuraX11::Dispatch(const base::NativeEvent& event) { | 301 bool OSExchangeDataProviderAuraX11::Dispatch(const base::NativeEvent& event) { |
| 291 // TODO(erg): Implement this side when we implement sending data. | 302 XEvent* xev = event; |
| 292 return false; | 303 switch (xev->type) { |
| 304 case SelectionRequest: | |
| 305 selection_owner_.OnSelectionRequest(xev->xselectionrequest); | |
| 306 break; | |
| 307 default: | |
| 308 NOTIMPLEMENTED(); | |
| 309 } | |
| 310 | |
| 311 return true; | |
| 293 } | 312 } |
| 294 | 313 |
| 295 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const { | 314 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const { |
| 296 NOTIMPLEMENTED(); | 315 NOTIMPLEMENTED(); |
| 297 return false; | 316 return false; |
| 298 } | 317 } |
| 299 | 318 |
| 319 std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const { | |
| 320 return format_map_->GetTypes(); | |
| 321 } | |
| 322 | |
| 300 /////////////////////////////////////////////////////////////////////////////// | 323 /////////////////////////////////////////////////////////////////////////////// |
| 301 // OSExchangeData, public: | 324 // OSExchangeData, public: |
| 302 | 325 |
| 303 // static | 326 // static |
| 304 OSExchangeData::Provider* OSExchangeData::CreateProvider() { | 327 OSExchangeData::Provider* OSExchangeData::CreateProvider() { |
| 305 return new OSExchangeDataProviderAuraX11(); | 328 return new OSExchangeDataProviderAuraX11(); |
| 306 } | 329 } |
| 307 | 330 |
| 308 } // namespace ui | 331 } // namespace ui |
| OLD | NEW |