summaryrefslogtreecommitdiffstats
path: root/app/clipboard/clipboard_linux.cc
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-03 04:25:37 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-03 04:25:37 +0000
commit90f399075f9082bf7031512d6579ec1196dc060a (patch)
tree8d47273615d98732a60d0e77a0787d1623204a4b /app/clipboard/clipboard_linux.cc
parentf0f400cb2e0a3c545d723524e8bbaf136caacf81 (diff)
downloadchromium_src-90f399075f9082bf7031512d6579ec1196dc060a.zip
chromium_src-90f399075f9082bf7031512d6579ec1196dc060a.tar.gz
chromium_src-90f399075f9082bf7031512d6579ec1196dc060a.tar.bz2
Move the clipboard stuff out of base and into app/clipboard. I renamed
clipboard_util to clipboard_util_win since it's Windows-only. This patch makes test_shell depend on app as well. There should be no logic change. TEST=none BUG=none Review URL: http://codereview.chromium.org/260003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27937 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app/clipboard/clipboard_linux.cc')
-rw-r--r--app/clipboard/clipboard_linux.cc410
1 files changed, 410 insertions, 0 deletions
diff --git a/app/clipboard/clipboard_linux.cc b/app/clipboard/clipboard_linux.cc
new file mode 100644
index 0000000..8b30ffd
--- /dev/null
+++ b/app/clipboard/clipboard_linux.cc
@@ -0,0 +1,410 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "app/clipboard/clipboard.h"
+
+#include <gtk/gtk.h>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/file_path.h"
+#include "base/gfx/size.h"
+#include "base/scoped_ptr.h"
+#include "base/linux_util.h"
+#include "base/string_util.h"
+
+namespace {
+
+const char kMimeBmp[] = "image/bmp";
+const char kMimeHtml[] = "text/html";
+const char kMimeText[] = "text/plain";
+const char kMimeURI[] = "text/uri-list";
+const char kMimeWebkitSmartPaste[] = "chromium/x-webkit-paste";
+
+std::string GdkAtomToString(const GdkAtom& atom) {
+ gchar* name = gdk_atom_name(atom);
+ std::string rv(name);
+ g_free(name);
+ return rv;
+}
+
+GdkAtom StringToGdkAtom(const std::string& str) {
+ return gdk_atom_intern(str.c_str(), false);
+}
+
+// GtkClipboardGetFunc callback.
+// GTK will call this when an application wants data we copied to the clipboard.
+void GetData(GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info,
+ gpointer user_data) {
+ Clipboard::TargetMap* data_map =
+ reinterpret_cast<Clipboard::TargetMap*>(user_data);
+
+ std::string target_string = GdkAtomToString(selection_data->target);
+ Clipboard::TargetMap::iterator iter = data_map->find(target_string);
+
+ if (iter == data_map->end())
+ return;
+
+ if (target_string == kMimeBmp) {
+ gtk_selection_data_set_pixbuf(selection_data,
+ reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ } else if (target_string == kMimeURI) {
+ gchar* uri_list[2];
+ uri_list[0] = reinterpret_cast<gchar*>(iter->second.first);
+ uri_list[1] = NULL;
+ gtk_selection_data_set_uris(selection_data, uri_list);
+ } else {
+ gtk_selection_data_set(selection_data, selection_data->target, 8,
+ reinterpret_cast<guchar*>(iter->second.first),
+ iter->second.second);
+ }
+}
+
+// GtkClipboardClearFunc callback.
+// We are guaranteed this will be called exactly once for each call to
+// gtk_clipboard_set_with_data
+void ClearData(GtkClipboard* clipboard,
+ gpointer user_data) {
+ Clipboard::TargetMap* map =
+ reinterpret_cast<Clipboard::TargetMap*>(user_data);
+ std::set<char*> ptrs;
+
+ for (Clipboard::TargetMap::iterator iter = map->begin();
+ iter != map->end(); ++iter) {
+ if (iter->first == kMimeBmp)
+ g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ else
+ ptrs.insert(iter->second.first);
+ }
+
+ for (std::set<char*>::iterator iter = ptrs.begin();
+ iter != ptrs.end(); ++iter) {
+ delete[] *iter;
+ }
+
+ delete map;
+}
+
+// Called on GdkPixbuf destruction; see WriteBitmap().
+void GdkPixbufFree(guchar* pixels, gpointer data) {
+ free(pixels);
+}
+
+} // namespace
+
+Clipboard::Clipboard() {
+ clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ primary_selection_ = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+}
+
+Clipboard::~Clipboard() {
+ // TODO(estade): do we want to save clipboard data after we exit?
+ // gtk_clipboard_set_can_store and gtk_clipboard_store work
+ // but have strangely awful performance.
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects) {
+ clipboard_data_ = new TargetMap();
+
+ for (ObjectMap::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ }
+
+ SetGtkClipboard();
+}
+
+// When a URL is copied from a render view context menu (via "copy link
+// location", for example), we additionally stick it in the X clipboard. This
+// matches other linux browsers.
+void Clipboard::DidWriteURL(const std::string& utf8_text) {
+ gtk_clipboard_set_text(primary_selection_, utf8_text.c_str(),
+ utf8_text.length());
+}
+
+// Take ownership of the GTK clipboard and inform it of the targets we support.
+void Clipboard::SetGtkClipboard() {
+ scoped_array<GtkTargetEntry> targets(
+ new GtkTargetEntry[clipboard_data_->size()]);
+
+ int i = 0;
+ for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin();
+ iter != clipboard_data_->end(); ++iter, ++i) {
+ targets[i].target = static_cast<gchar*>(malloc(iter->first.size() + 1));
+ memcpy(targets[i].target, iter->first.data(), iter->first.size());
+ targets[i].target[iter->first.size()] = '\0';
+
+ targets[i].flags = 0;
+ targets[i].info = i;
+ }
+
+ gtk_clipboard_set_with_data(clipboard_, targets.get(),
+ clipboard_data_->size(),
+ GetData, ClearData,
+ clipboard_data_);
+
+ for (size_t i = 0; i < clipboard_data_->size(); i++)
+ free(targets[i].target);
+}
+
+void Clipboard::WriteText(const char* text_data, size_t text_len) {
+ char* data = new char[text_len];
+ memcpy(data, text_data, text_len);
+
+ InsertMapping(kMimeText, data, text_len);
+ InsertMapping("TEXT", data, text_len);
+ InsertMapping("STRING", data, text_len);
+ InsertMapping("UTF8_STRING", data, text_len);
+ InsertMapping("COMPOUND_TEXT", data, text_len);
+}
+
+void Clipboard::WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len) {
+ // TODO(estade): might not want to ignore |url_data|
+ char* data = new char[markup_len];
+ memcpy(data, markup_data, markup_len);
+
+ InsertMapping(kMimeHtml, data, markup_len);
+}
+
+// Write an extra flavor that signifies WebKit was the last to modify the
+// pasteboard. This flavor has no data.
+void Clipboard::WriteWebSmartPaste() {
+ InsertMapping(kMimeWebkitSmartPaste, NULL, 0);
+}
+
+void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+
+ guchar* data = base::BGRAToRGBA(reinterpret_cast<const uint8_t*>(pixel_data),
+ size->width(), size->height(), 0);
+
+ GdkPixbuf* pixbuf =
+ gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE,
+ 8, size->width(), size->height(),
+ size->width() * 4, GdkPixbufFree, NULL);
+ // We store the GdkPixbuf*, and the size_t half of the pair is meaningless.
+ // Note that this contrasts with the vast majority of entries in our target
+ // map, which directly store the data and its length.
+ InsertMapping(kMimeBmp, reinterpret_cast<char*>(pixbuf), 0);
+}
+
+void Clipboard::WriteBookmark(const char* title_data, size_t title_len,
+ const char* url_data, size_t url_len) {
+ // Write as plain text.
+ WriteText(url_data, url_len);
+
+ // Write as a URI.
+ char* data = new char[url_len + 1];
+ memcpy(data, url_data, url_len);
+ data[url_len] = NULL;
+ InsertMapping(kMimeURI, data, url_len + 1);
+}
+
+void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
+ NOTIMPLEMENTED();
+}
+
+void Clipboard::WriteData(const char* format_name, size_t format_len,
+ const char* data_data, size_t data_len) {
+ char* data = new char[data_len];
+ memcpy(data, data_data, data_len);
+ std::string format(format_name, format_len);
+ InsertMapping(format.c_str(), data, data_len);
+}
+
+// We do not use gtk_clipboard_wait_is_target_available because of
+// a bug with the gtk clipboard. It caches the available targets
+// and does not always refresh the cache when it is appropriate.
+bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return false;
+
+ bool format_is_plain_text = GetPlainTextFormatType() == format;
+ if (format_is_plain_text) {
+ // This tries a number of common text targets.
+ if (gtk_clipboard_wait_is_text_available(clipboard))
+ return true;
+ }
+
+ bool retval = false;
+ GdkAtom* targets = NULL;
+ GtkSelectionData* data =
+ gtk_clipboard_wait_for_contents(clipboard,
+ gdk_atom_intern("TARGETS", false));
+
+ if (!data)
+ return false;
+
+ int num = 0;
+ gtk_selection_data_get_targets(data, &targets, &num);
+
+ // Some programs post data to the clipboard without any targets. If this is
+ // the case we attempt to make sense of the contents as text. This is pretty
+ // unfortunate since it means we have to actually copy the data to see if it
+ // is available, but at least this path shouldn't be hit for conforming
+ // programs.
+ if (num <= 0) {
+ if (format_is_plain_text) {
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+ if (text) {
+ g_free(text);
+ retval = true;
+ }
+ }
+ }
+
+ GdkAtom format_atom = StringToGdkAtom(format);
+
+ for (int i = 0; i < num; i++) {
+ if (targets[i] == format_atom) {
+ retval = true;
+ break;
+ }
+ }
+
+ g_free(targets);
+ gtk_selection_data_free(data);
+
+ return retval;
+}
+
+bool Clipboard::IsFormatAvailableByString(const std::string& format,
+ Clipboard::Buffer buffer) const {
+ return IsFormatAvailable(format, buffer);
+}
+
+void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+
+ result->clear();
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+
+ if (text == NULL)
+ return;
+
+ // TODO(estade): do we want to handle the possible error here?
+ UTF8ToUTF16(text, strlen(text), result);
+ g_free(text);
+}
+
+void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
+ std::string* result) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+
+ result->clear();
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+
+ if (text == NULL)
+ return;
+
+ result->assign(text);
+ g_free(text);
+}
+
+void Clipboard::ReadFile(FilePath* file) const {
+ *file = FilePath();
+}
+
+// TODO(estade): handle different charsets.
+// TODO(port): set *src_url.
+void Clipboard::ReadHTML(Clipboard::Buffer buffer, string16* markup,
+ std::string* src_url) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+ markup->clear();
+
+ GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard,
+ StringToGdkAtom(GetHtmlFormatType()));
+
+ if (!data)
+ return;
+
+ UTF8ToUTF16(reinterpret_cast<char*>(data->data), data->length, markup);
+ gtk_selection_data_free(data);
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ // TODO(estade): implement this.
+}
+
+void Clipboard::ReadData(const std::string& format, std::string* result) {
+ GtkSelectionData* data =
+ gtk_clipboard_wait_for_contents(clipboard_, StringToGdkAtom(format));
+ if (!data)
+ return;
+ result->assign(reinterpret_cast<char*>(data->data), data->length);
+ gtk_selection_data_free(data);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ return GdkAtomToString(GDK_TARGET_STRING);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ return GetPlainTextFormatType();
+}
+
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ return std::string(kMimeHtml);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return std::string(kMimeWebkitSmartPaste);
+}
+
+// Insert the key/value pair in the clipboard_data structure. If
+// the mapping already exists, it frees the associated data. Don't worry
+// about double freeing because if the same key is inserted into the
+// map twice, it must have come from different Write* functions and the
+// data pointer cannot be the same.
+void Clipboard::InsertMapping(const char* key,
+ char* data,
+ size_t data_len) {
+ TargetMap::iterator iter = clipboard_data_->find(key);
+
+ if (iter != clipboard_data_->end()) {
+ if (strcmp(kMimeBmp, key) == 0)
+ g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ else
+ delete[] iter->second.first;
+ }
+
+ (*clipboard_data_)[key] = std::make_pair(data, data_len);
+}
+
+GtkClipboard* Clipboard::LookupBackingClipboard(Buffer clipboard) const {
+ GtkClipboard* result;
+
+ switch (clipboard) {
+ case BUFFER_STANDARD:
+ result = clipboard_;
+ break;
+ case BUFFER_SELECTION:
+ result = primary_selection_;
+ break;
+ default:
+ NOTREACHED();
+ result = NULL;
+ break;
+ }
+ return result;
+}