OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "base/clipboard.h" | 5 #include "base/clipboard.h" |
6 #include "base/string_util.h" | 6 #include "base/string_util.h" |
7 | 7 |
8 #include <gtk/gtk.h> | 8 #include <gtk/gtk.h> |
9 #include <map> | 9 #include <map> |
10 #include <set> | 10 #include <set> |
(...skipping 14 matching lines...) Expand all Loading... |
25 Clipboard::TargetMap* data_map = | 25 Clipboard::TargetMap* data_map = |
26 reinterpret_cast<Clipboard::TargetMap*>(user_data); | 26 reinterpret_cast<Clipboard::TargetMap*>(user_data); |
27 | 27 |
28 Clipboard::TargetMap::iterator iter = | 28 Clipboard::TargetMap::iterator iter = |
29 data_map->find(std::string(gdk_atom_name(selection_data->target))); | 29 data_map->find(std::string(gdk_atom_name(selection_data->target))); |
30 | 30 |
31 if (iter == data_map->end()) | 31 if (iter == data_map->end()) |
32 return; | 32 return; |
33 | 33 |
34 gtk_selection_data_set(selection_data, selection_data->target, 8, | 34 gtk_selection_data_set(selection_data, selection_data->target, 8, |
35 iter->second.first, | 35 reinterpret_cast<guchar*>(iter->second.first), |
36 iter->second.second); | 36 iter->second.second); |
37 } | 37 } |
38 | 38 |
39 // GtkClipboardClearFunc callback. | 39 // GtkClipboardClearFunc callback. |
40 // GTK will call this when new data is set on the clipboard (whether or not we | 40 // We are guaranteed this will be called exactly once for each call to |
41 // retain ownership) or when gtk_clipboard_clear() is called. We don't do | 41 // gtk_clipboard_set_with_data |
42 // anything because we don't want to clear the clipboard_data_ map if we call | |
43 // set data several times in a row. Instead we manually clear the | |
44 // clipboard_data_ map on Clear() and on Clipboard destruction. | |
45 void ClearData(GtkClipboard* clipboard, | 42 void ClearData(GtkClipboard* clipboard, |
46 gpointer user_data) { | 43 gpointer user_data) { |
| 44 Clipboard::TargetMap* map = |
| 45 reinterpret_cast<Clipboard::TargetMap*>(user_data); |
| 46 std::set<char*> ptrs; |
| 47 |
| 48 for (Clipboard::TargetMap::iterator iter = map->begin(); |
| 49 iter != map->end(); ++iter) |
| 50 ptrs.insert(iter->second.first); |
| 51 |
| 52 for (std::set<char*>::iterator iter = ptrs.begin(); |
| 53 iter != ptrs.end(); ++iter) |
| 54 delete[] *iter; |
| 55 |
| 56 delete map; |
| 57 } |
| 58 |
| 59 // Frees the pointers in the given map and clears the map. |
| 60 // Does not double-free any pointers. |
| 61 void FreeTargetMap(Clipboard::TargetMap map) { |
| 62 std::set<char*> ptrs; |
| 63 |
| 64 for (Clipboard::TargetMap::iterator iter = map.begin(); |
| 65 iter != map.end(); ++iter) |
| 66 ptrs.insert(iter->second.first); |
| 67 |
| 68 for (std::set<char*>::iterator iter = ptrs.begin(); |
| 69 iter != ptrs.end(); ++iter) |
| 70 delete[] *iter; |
| 71 |
| 72 map.clear(); |
47 } | 73 } |
48 | 74 |
49 } // namespace | 75 } // namespace |
50 | 76 |
51 Clipboard::Clipboard() { | 77 Clipboard::Clipboard() { |
52 clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); | 78 clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); |
53 } | 79 } |
54 | 80 |
55 Clipboard::~Clipboard() { | 81 Clipboard::~Clipboard() { |
56 // TODO(estade): do we want to save clipboard data after we exit? | 82 // TODO(estade): do we want to save clipboard data after we exit? |
57 // gtk_clipboard_set_can_store and gtk_clipboard_store work | 83 // gtk_clipboard_set_can_store and gtk_clipboard_store work |
58 // but have strangely awful performance. | 84 // but have strangely awful performance. |
59 Clear(); | |
60 } | 85 } |
61 | 86 |
62 void Clipboard::Clear() { | 87 void Clipboard::WriteObjects(const ObjectMap& objects) { |
63 gtk_clipboard_clear(clipboard_); | 88 clipboard_data_ = new TargetMap(); |
64 FreeTargetMap(); | |
65 } | |
66 | 89 |
67 void Clipboard::WriteText(const std::wstring& text) { | 90 for (ObjectMap::const_iterator iter = objects.begin(); |
68 std::string utf8_text = WideToUTF8(text); | 91 iter != objects.end(); ++iter) { |
69 size_t text_len = utf8_text.size() + 1; | 92 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
70 uint8* text_data = new uint8[text_len]; | 93 } |
71 memcpy(text_data, utf8_text.c_str(), text_len); | |
72 | |
73 uint8* old_data = InsertOrOverwrite(kMimeText, text_data, text_len); | |
74 InsertOrOverwrite("TEXT", text_data, text_len); | |
75 InsertOrOverwrite("STRING", text_data, text_len); | |
76 InsertOrOverwrite("UTF8_STRING", text_data, text_len); | |
77 InsertOrOverwrite("COMPOUND_TEXT", text_data, text_len); | |
78 | |
79 if (old_data) | |
80 delete[] old_data; | |
81 | |
82 SetGtkClipboard(); | |
83 } | |
84 | |
85 void Clipboard::WriteHTML(const std::wstring& markup, | |
86 const std::string& src_url) { | |
87 // TODO(estade): might not want to ignore src_url | |
88 std::string html = WideToUTF8(markup); | |
89 size_t data_len = html.size() + 1; | |
90 uint8* html_data = new uint8[data_len]; | |
91 memcpy(html_data, html.c_str(), data_len); | |
92 | |
93 uint8* old_data = InsertOrOverwrite(kMimeHtml, html_data, data_len); | |
94 | |
95 if (old_data) | |
96 delete[] old_data; | |
97 | 94 |
98 SetGtkClipboard(); | 95 SetGtkClipboard(); |
99 } | 96 } |
100 | 97 |
| 98 // Take ownership of the GTK clipboard and inform it of the targets we support. |
| 99 void Clipboard::SetGtkClipboard() { |
| 100 GtkTargetEntry targets[clipboard_data_->size()]; |
| 101 |
| 102 int i = 0; |
| 103 for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin(); |
| 104 iter != clipboard_data_->end(); ++iter, ++i) { |
| 105 char* target_string = new char[iter->first.size() + 1]; |
| 106 strcpy(target_string, iter->first.c_str()); |
| 107 targets[i].target = target_string; |
| 108 targets[i].flags = 0; |
| 109 targets[i].info = i; |
| 110 } |
| 111 |
| 112 gtk_clipboard_set_with_data(clipboard_, targets, |
| 113 clipboard_data_->size(), |
| 114 GetData, ClearData, |
| 115 clipboard_data_); |
| 116 |
| 117 for (size_t i = 0; i < clipboard_data_->size(); i++) |
| 118 delete[] targets[i].target; |
| 119 } |
| 120 |
| 121 void Clipboard::WriteText(const char* text_data, size_t text_len) { |
| 122 char* data = new char[text_len]; |
| 123 memcpy(data, text_data, text_len); |
| 124 |
| 125 InsertMapping(kMimeText, data, text_len); |
| 126 InsertMapping("TEXT", data, text_len); |
| 127 InsertMapping("STRING", data, text_len); |
| 128 InsertMapping("UTF8_STRING", data, text_len); |
| 129 InsertMapping("COMPOUND_TEXT", data, text_len); |
| 130 } |
| 131 |
| 132 void Clipboard::WriteHTML(const char* markup_data, |
| 133 size_t markup_len, |
| 134 const char* url_data, |
| 135 size_t url_len) { |
| 136 // TODO(estade): might not want to ignore |url_data| |
| 137 char* data = new char[markup_len]; |
| 138 memcpy(data, markup_data, markup_len); |
| 139 |
| 140 InsertMapping(kMimeHtml, data, markup_len); |
| 141 } |
| 142 |
101 // We do not use gtk_clipboard_wait_is_target_available because of | 143 // We do not use gtk_clipboard_wait_is_target_available because of |
102 // a bug with the gtk clipboard. It caches the available targets | 144 // a bug with the gtk clipboard. It caches the available targets |
103 // and does not always refresh the cache when it is appropriate. | 145 // and does not always refresh the cache when it is appropriate. |
104 // TODO(estade): When gnome bug 557315 is resolved, change this function | 146 // TODO(estade): When gnome bug 557315 is resolved, change this function |
105 // to use gtk_clipboard_wait_is_target_available. Also, catch requests | 147 // to use gtk_clipboard_wait_is_target_available. Also, catch requests |
106 // for plain text and change them to gtk_clipboard_wait_is_text_available | 148 // for plain text and change them to gtk_clipboard_wait_is_text_available |
107 // (which checks for several standard text targets). | 149 // (which checks for several standard text targets). |
108 bool Clipboard::IsFormatAvailable(Clipboard::FormatType format) const { | 150 bool Clipboard::IsFormatAvailable(Clipboard::FormatType format) const { |
109 bool retval = false; | 151 bool retval = false; |
110 GdkAtom* targets = NULL; | 152 GdkAtom* targets = NULL; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 // static | 220 // static |
179 Clipboard::FormatType Clipboard::GetPlainTextWFormatType() { | 221 Clipboard::FormatType Clipboard::GetPlainTextWFormatType() { |
180 return GetPlainTextFormatType(); | 222 return GetPlainTextFormatType(); |
181 } | 223 } |
182 | 224 |
183 // static | 225 // static |
184 Clipboard::FormatType Clipboard::GetHtmlFormatType() { | 226 Clipboard::FormatType Clipboard::GetHtmlFormatType() { |
185 return gdk_atom_intern(kMimeHtml, false); | 227 return gdk_atom_intern(kMimeHtml, false); |
186 } | 228 } |
187 | 229 |
188 // Take ownership of the GTK clipboard and inform it of the targets we support. | 230 // Insert the key/value pair in the clipboard_data structure. If |
189 void Clipboard::SetGtkClipboard() { | 231 // the mapping already exists, it frees the associated data. Don't worry |
190 GtkTargetEntry targets[clipboard_data_.size()]; | 232 // about double freeing because if the same key is inserted into the |
| 233 // map twice, it must have come from different Write* functions and the |
| 234 // data pointer cannot be the same. |
| 235 void Clipboard::InsertMapping(const char* key, |
| 236 char* data, |
| 237 size_t data_len) { |
| 238 TargetMap::iterator iter = clipboard_data_->find(key); |
191 | 239 |
192 int i = 0; | 240 if (iter != clipboard_data_->end()) |
193 for (Clipboard::TargetMap::iterator iter = clipboard_data_.begin(); | 241 delete[] iter->second.first; |
194 iter != clipboard_data_.end(); iter++, i++) { | |
195 char* target_string = new char[iter->first.size() + 1]; | |
196 strcpy(target_string, iter->first.c_str()); | |
197 targets[i].target = target_string; | |
198 targets[i].flags = 0; | |
199 targets[i].info = i; | |
200 } | |
201 | 242 |
202 gtk_clipboard_set_with_data(clipboard_, targets, | 243 (*clipboard_data_)[key] = std::make_pair(data, data_len); |
203 clipboard_data_.size(), | |
204 GetData, ClearData, | |
205 &clipboard_data_); | |
206 | |
207 for (size_t i = 0; i < clipboard_data_.size(); i++) | |
208 delete[] targets[i].target; | |
209 } | 244 } |
210 | |
211 // Free the pointers in the clipboard_data_ map and reset the map. | |
212 void Clipboard::FreeTargetMap() { | |
213 std::set<uint8*> ptrs; | |
214 | |
215 for (Clipboard::TargetMap::iterator iter = clipboard_data_.begin(); | |
216 iter != clipboard_data_.end(); iter++) | |
217 ptrs.insert(iter->second.first); | |
218 | |
219 for (std::set<uint8*>::iterator iter = ptrs.begin(); | |
220 iter != ptrs.end(); iter++) | |
221 delete[] *iter; | |
222 | |
223 clipboard_data_.clear(); | |
224 } | |
225 | |
226 // Insert the key/value pair in the clipboard_data structure. Overwrite | |
227 // and any old value that might have had that key. If we overwrote something, | |
228 // return the pointer to that something. Otherwise return null. | |
229 uint8* Clipboard::InsertOrOverwrite(std::string key, | |
230 uint8* data, size_t data_len) { | |
231 std::pair<std::string, std::pair<uint8*, size_t> > mapping = | |
232 std::make_pair(key, std::make_pair(data, data_len)); | |
233 | |
234 Clipboard::TargetMap::iterator iter = clipboard_data_.find(key); | |
235 uint8* retval = NULL; | |
236 | |
237 if (iter == clipboard_data_.end()) { | |
238 clipboard_data_.insert(mapping); | |
239 } else { | |
240 retval = iter->second.first; | |
241 iter->second = mapping.second; | |
242 } | |
243 | |
244 return retval; | |
245 } | |
246 | |
OLD | NEW |