diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-03 04:25:37 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-03 04:25:37 +0000 |
commit | 90f399075f9082bf7031512d6579ec1196dc060a (patch) | |
tree | 8d47273615d98732a60d0e77a0787d1623204a4b /app/clipboard/clipboard_linux.cc | |
parent | f0f400cb2e0a3c545d723524e8bbaf136caacf81 (diff) | |
download | chromium_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.cc | 410 |
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; +} |