This source file includes following definitions.
- IsSeparator
- Show
- BubbleClosing
- Observe
- factory_
- OnDestroy
- OnNameActivate
- OnFolderChanged
- OnEditClicked
- OnCloseClicked
- OnRemoveClicked
- OnSignInClicked
- OnSyncPromoRealize
- OnSyncPromoExpose
- UpdatePromoColors
- ApplyEdits
- GetTitle
- ShowEditor
- InitFolderComboModel
#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
#include <gtk/gtk.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/bookmarks/bookmark_editor.h"
#include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/sync/sync_promo_ui.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/user_metrics.h"
#include "grit/generated_resources.h"
#include "ui/base/gtk/gtk_hig_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas_paint_gtk.h"
using base::UserMetricsAction;
namespace {
enum {
COLUMN_NAME,
COLUMN_IS_SEPARATOR,
COLUMN_COUNT
};
const int kBubbleBorderThickness = 1;
const SkColor kBubbleBorderColor = SkColorSetRGB(0x63, 0x63, 0x63);
const GdkColor kPromoBackgroundColor = GDK_COLOR_RGB(0xf5, 0xf5, 0xf5);
const SkColor kPromoBorderColor = SkColorSetRGB(0xe5, 0xe5, 0xe5);
const GdkColor kPromoTextColor = GDK_COLOR_RGB(0x66, 0x66, 0x66);
const int kPromoVerticalPadding = 15;
const char kPromoLinkMarkup[] =
"<a href='signin'><span underline='none'>%s</span></a>";
const char kPromoLinkStyle[] =
"style \"sign-in-link\" {\n"
" GtkWidget::link-color=\"blue\"\n"
"}\n"
"widget \"*sign-in-link\" style \"sign-in-link\"\n";
gboolean IsSeparator(GtkTreeModel* model, GtkTreeIter* iter, gpointer data) {
gboolean is_separator;
gtk_tree_model_get(model, iter, COLUMN_IS_SEPARATOR, &is_separator, -1);
return is_separator;
}
}
BookmarkBubbleGtk* BookmarkBubbleGtk::bookmark_bubble_ = NULL;
void BookmarkBubbleGtk::Show(GtkWidget* anchor,
Profile* profile,
const GURL& url,
bool newly_bookmarked) {
if (bookmark_bubble_)
return;
bookmark_bubble_ = new BookmarkBubbleGtk(anchor,
profile,
url,
newly_bookmarked);
}
void BookmarkBubbleGtk::BubbleClosing(BubbleGtk* bubble,
bool closed_by_escape) {
if (closed_by_escape) {
remove_bookmark_ = newly_bookmarked_;
apply_edits_ = false;
}
}
void BookmarkBubbleGtk::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
if (theme_service_->UsingNativeTheme()) {
for (std::vector<GtkWidget*>::iterator it = labels_.begin();
it != labels_.end(); ++it) {
gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, NULL);
}
} else {
for (std::vector<GtkWidget*>::iterator it = labels_.begin();
it != labels_.end(); ++it) {
gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &ui::kGdkBlack);
}
}
UpdatePromoColors();
}
BookmarkBubbleGtk::BookmarkBubbleGtk(GtkWidget* anchor,
Profile* profile,
const GURL& url,
bool newly_bookmarked)
: url_(url),
profile_(profile),
model_(BookmarkModelFactory::GetForProfile(profile)),
theme_service_(GtkThemeService::GetFrom(profile_)),
anchor_(anchor),
promo_(NULL),
promo_label_(NULL),
name_entry_(NULL),
folder_combo_(NULL),
bubble_(NULL),
newly_bookmarked_(newly_bookmarked),
apply_edits_(true),
remove_bookmark_(false),
factory_(this) {
GtkWidget* label = gtk_label_new(l10n_util::GetStringUTF8(
newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK).c_str());
labels_.push_back(label);
remove_button_ = theme_service_->BuildChromeLinkButton(
l10n_util::GetStringUTF8(IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK));
GtkWidget* edit_button = gtk_button_new_with_label(
l10n_util::GetStringUTF8(IDS_BOOKMARK_BUBBLE_OPTIONS).c_str());
GtkWidget* close_button = gtk_button_new_with_label(
l10n_util::GetStringUTF8(IDS_DONE).c_str());
GtkWidget* bubble_container = gtk_vbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(bubble_container),
kBubbleBorderThickness);
GtkWidget* content = gtk_vbox_new(FALSE, 5);
gtk_container_set_border_width(
GTK_CONTAINER(content),
ui::kContentAreaBorder - kBubbleBorderThickness);
GtkWidget* top = gtk_hbox_new(FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
gtk_box_pack_start(GTK_BOX(top), label,
TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(top), remove_button_,
FALSE, FALSE, 0);
InitFolderComboModel();
name_entry_ = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(name_entry_), GetTitle().c_str());
GtkWidget* table = gtk_util::CreateLabeledControlsGroup(
&labels_,
l10n_util::GetStringUTF8(IDS_BOOKMARK_BUBBLE_TITLE_TEXT).c_str(),
name_entry_,
l10n_util::GetStringUTF8(IDS_BOOKMARK_BUBBLE_FOLDER_TEXT).c_str(),
folder_combo_,
NULL);
GtkWidget* bottom = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(bottom), gtk_label_new(""),
TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(bottom), edit_button,
FALSE, FALSE, 4);
gtk_box_pack_start(GTK_BOX(bottom), close_button,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(content), top, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(content), table, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(content), bottom, TRUE, TRUE, 0);
gtk_container_set_focus_child(GTK_CONTAINER(content), table);
gtk_box_pack_start(GTK_BOX(bubble_container), content, TRUE, TRUE, 0);
if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
std::string link_text =
l10n_util::GetStringUTF8(IDS_BOOKMARK_SYNC_PROMO_LINK);
char* link_markup = g_markup_printf_escaped(kPromoLinkMarkup,
link_text.c_str());
base::string16 link_markup_utf16;
base::UTF8ToUTF16(link_markup, strlen(link_markup), &link_markup_utf16);
g_free(link_markup);
std::string promo_markup = l10n_util::GetStringFUTF8(
IDS_BOOKMARK_SYNC_PROMO_MESSAGE,
link_markup_utf16);
promo_ = gtk_event_box_new();
gtk_widget_set_app_paintable(promo_, TRUE);
promo_label_ = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(promo_label_), promo_markup.c_str());
gtk_misc_set_alignment(GTK_MISC(promo_label_), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(promo_label_),
ui::kContentAreaBorder,
kPromoVerticalPadding);
gtk_rc_parse_string(kPromoLinkStyle);
UpdatePromoColors();
gtk_container_add(GTK_CONTAINER(promo_), promo_label_);
gtk_box_pack_start(GTK_BOX(bubble_container), promo_, TRUE, TRUE, 0);
g_signal_connect(promo_,
"realize",
G_CALLBACK(&OnSyncPromoRealizeThunk),
this);
g_signal_connect(promo_,
"expose-event",
G_CALLBACK(&OnSyncPromoExposeThunk),
this);
g_signal_connect(promo_label_,
"activate-link",
G_CALLBACK(&OnSignInClickedThunk),
this);
}
bubble_ = BubbleGtk::Show(anchor_,
NULL,
bubble_container,
BubbleGtk::ANCHOR_TOP_RIGHT,
BubbleGtk::MATCH_SYSTEM_THEME |
BubbleGtk::POPUP_WINDOW |
BubbleGtk::GRAB_INPUT,
theme_service_,
this);
if (!bubble_) {
NOTREACHED();
return;
}
g_signal_connect(content, "destroy",
G_CALLBACK(&OnDestroyThunk), this);
g_signal_connect(name_entry_, "activate",
G_CALLBACK(&OnNameActivateThunk), this);
g_signal_connect(folder_combo_, "changed",
G_CALLBACK(&OnFolderChangedThunk), this);
g_signal_connect(edit_button, "clicked",
G_CALLBACK(&OnEditClickedThunk), this);
g_signal_connect(close_button, "clicked",
G_CALLBACK(&OnCloseClickedThunk), this);
g_signal_connect(remove_button_, "clicked",
G_CALLBACK(&OnRemoveClickedThunk), this);
registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
content::Source<ThemeService>(theme_service_));
theme_service_->InitThemesFor(this);
}
BookmarkBubbleGtk::~BookmarkBubbleGtk() {
DCHECK(bookmark_bubble_);
bookmark_bubble_ = NULL;
if (apply_edits_) {
ApplyEdits();
} else if (remove_bookmark_) {
const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url_);
if (node)
model_->Remove(node->parent(), node->parent()->GetIndexOf(node));
}
}
void BookmarkBubbleGtk::OnDestroy(GtkWidget* widget) {
delete this;
}
void BookmarkBubbleGtk::OnNameActivate(GtkWidget* widget) {
bubble_->Close();
}
void BookmarkBubbleGtk::OnFolderChanged(GtkWidget* widget) {
int index = gtk_combo_box_get_active(GTK_COMBO_BOX(folder_combo_));
if (index == folder_combo_model_->GetItemCount() - 1) {
content::RecordAction(
UserMetricsAction("BookmarkBubble_EditFromCombobox"));
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&BookmarkBubbleGtk::ShowEditor, factory_.GetWeakPtr()));
}
}
void BookmarkBubbleGtk::OnEditClicked(GtkWidget* widget) {
content::RecordAction(UserMetricsAction("BookmarkBubble_Edit"));
ShowEditor();
}
void BookmarkBubbleGtk::OnCloseClicked(GtkWidget* widget) {
bubble_->Close();
}
void BookmarkBubbleGtk::OnRemoveClicked(GtkWidget* widget) {
content::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"));
apply_edits_ = false;
remove_bookmark_ = true;
bubble_->Close();
}
gboolean BookmarkBubbleGtk::OnSignInClicked(GtkWidget* widget, gchar* uri) {
GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(anchor_));
Browser* browser = chrome::FindBrowserWithWindow(window);
chrome::ShowBrowserSignin(browser, signin::SOURCE_BOOKMARK_BUBBLE);
bubble_->Close();
return TRUE;
}
void BookmarkBubbleGtk::OnSyncPromoRealize(GtkWidget* widget) {
int width = gtk_util::GetWidgetSize(widget).width();
gtk_util::SetLabelWidth(promo_label_, width);
}
gboolean BookmarkBubbleGtk::OnSyncPromoExpose(GtkWidget* widget,
GdkEventExpose* event) {
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
gfx::CanvasSkiaPaint canvas(event);
canvas.DrawLine(gfx::Point(0, 0),
gfx::Point(allocation.width + 1, 0),
kPromoBorderColor);
SkPaint points_paint;
points_paint.setColor(kBubbleBorderColor);
points_paint.setStrokeWidth(SkIntToScalar(1));
canvas.DrawPoint(gfx::Point(0, allocation.height - 1), points_paint);
canvas.DrawPoint(gfx::Point(allocation.width - 1, allocation.height - 1),
points_paint);
return FALSE;
}
void BookmarkBubbleGtk::UpdatePromoColors() {
if (!promo_)
return;
GdkColor promo_background_color;
if (!theme_service_->UsingNativeTheme()) {
promo_background_color = kPromoBackgroundColor;
gtk_widget_set_name(promo_label_, "sign-in-link");
gtk_util::SetLabelColor(promo_label_, &kPromoTextColor);
} else {
promo_background_color = theme_service_->GetGdkColor(
ThemeProperties::COLOR_TOOLBAR);
gtk_widget_set_name(promo_label_, "sign-in-link-theme-color");
}
gtk_widget_modify_bg(promo_, GTK_STATE_NORMAL, &promo_background_color);
gtk_widget_modify_base(promo_label_,
GTK_STATE_ACTIVE,
&promo_background_color);
gtk_widget_modify_base(promo_label_,
GTK_STATE_PRELIGHT,
&promo_background_color);
}
void BookmarkBubbleGtk::ApplyEdits() {
apply_edits_ = false;
const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url_);
if (node) {
const base::string16 new_title(
base::UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(name_entry_))));
if (new_title != node->GetTitle()) {
model_->SetTitle(node, new_title);
content::RecordAction(
UserMetricsAction("BookmarkBubble_ChangeTitleInBubble"));
}
folder_combo_model_->MaybeChangeParent(
node, gtk_combo_box_get_active(GTK_COMBO_BOX(folder_combo_)));
}
}
std::string BookmarkBubbleGtk::GetTitle() {
const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url_);
if (!node) {
NOTREACHED();
return std::string();
}
return base::UTF16ToUTF8(node->GetTitle());
}
void BookmarkBubbleGtk::ShowEditor() {
const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url_);
ApplyEdits();
Profile* profile = profile_;
GtkWindow* toplevel = GTK_WINDOW(gtk_widget_get_toplevel(anchor_));
bubble_->Close();
if (node) {
BookmarkEditor::Show(toplevel, profile,
BookmarkEditor::EditDetails::EditNode(node),
BookmarkEditor::SHOW_TREE);
}
}
void BookmarkBubbleGtk::InitFolderComboModel() {
const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url_);
DCHECK(node);
folder_combo_model_.reset(new RecentlyUsedFoldersComboModel(model_, node));
GtkListStore* store = gtk_list_store_new(COLUMN_COUNT,
G_TYPE_STRING, G_TYPE_BOOLEAN);
for (int i = 0; i < folder_combo_model_->GetItemCount(); ++i) {
const bool is_separator = folder_combo_model_->IsItemSeparatorAt(i);
const std::string name = is_separator ?
std::string() : base::UTF16ToUTF8(folder_combo_model_->GetItemAt(i));
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
COLUMN_NAME, name.c_str(),
COLUMN_IS_SEPARATOR, is_separator,
-1);
}
folder_combo_ = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
gtk_combo_box_set_active(GTK_COMBO_BOX(folder_combo_),
folder_combo_model_->GetDefaultIndex());
gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(folder_combo_),
IsSeparator, NULL, NULL);
g_object_unref(store);
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(folder_combo_), renderer, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(folder_combo_), renderer,
"text", COLUMN_NAME,
NULL);
}