| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/gtk/browser_titlebar.h" | |
| 6 | |
| 7 #include <gdk/gdkkeysyms.h> | |
| 8 #include <gtk/gtk.h> | |
| 9 | |
| 10 #include <string> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "app/l10n_util.h" | |
| 14 #include "app/resource_bundle.h" | |
| 15 #include "base/command_line.h" | |
| 16 #include "base/singleton.h" | |
| 17 #include "base/string_piece.h" | |
| 18 #include "base/string_tokenizer.h" | |
| 19 #include "base/utf_string_conversions.h" | |
| 20 #include "chrome/app/chrome_command_ids.h" | |
| 21 #include "chrome/browser/gtk/accelerators_gtk.h" | |
| 22 #include "chrome/browser/gtk/browser_window_gtk.h" | |
| 23 #include "chrome/browser/gtk/custom_button.h" | |
| 24 #if defined(USE_GCONF) | |
| 25 #include "chrome/browser/gtk/gconf_titlebar_listener.h" | |
| 26 #endif | |
| 27 #include "chrome/browser/gtk/gtk_theme_provider.h" | |
| 28 #include "chrome/browser/gtk/gtk_util.h" | |
| 29 #include "chrome/browser/gtk/menu_gtk.h" | |
| 30 #include "chrome/browser/gtk/nine_box.h" | |
| 31 #include "chrome/browser/gtk/tabs/tab_strip_gtk.h" | |
| 32 #include "chrome/browser/prefs/pref_service.h" | |
| 33 #include "chrome/browser/profiles/profile.h" | |
| 34 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 35 #include "chrome/browser/ui/browser.h" | |
| 36 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h" | |
| 37 #include "chrome/browser/ui/toolbar/wrench_menu_model.h" | |
| 38 #include "chrome/common/notification_service.h" | |
| 39 #include "chrome/common/pref_names.h" | |
| 40 #include "gfx/gtk_util.h" | |
| 41 #include "gfx/skbitmap_operations.h" | |
| 42 #include "grit/app_resources.h" | |
| 43 #include "grit/generated_resources.h" | |
| 44 #include "grit/theme_resources.h" | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 // The space above the titlebars. | |
| 49 const int kTitlebarHeight = 14; | |
| 50 | |
| 51 // The thickness in pixels of the tab border. | |
| 52 const int kTabOuterThickness = 1; | |
| 53 | |
| 54 // Amount to offset the tab images relative to the background. | |
| 55 const int kNormalVerticalOffset = kTitlebarHeight + kTabOuterThickness; | |
| 56 | |
| 57 // A linux specific menu item for toggling window decorations. | |
| 58 const int kShowWindowDecorationsCommand = 200; | |
| 59 | |
| 60 const int kOTRBottomSpacing = 1; | |
| 61 // There are 2 px on each side of the OTR avatar (between the frame border and | |
| 62 // it on the left, and between it and the tabstrip on the right). | |
| 63 const int kOTRSideSpacing = 2; | |
| 64 | |
| 65 // The thickness of the custom frame border; we need it here to enlarge the | |
| 66 // close button whent the custom frame border isn't showing but the custom | |
| 67 // titlebar is showing. | |
| 68 const int kFrameBorderThickness = 4; | |
| 69 | |
| 70 // There is a 4px gap between the icon and the title text. | |
| 71 const int kIconTitleSpacing = 4; | |
| 72 | |
| 73 // Padding around the icon when in app mode or popup mode. | |
| 74 const int kAppModePaddingTop = 5; | |
| 75 const int kAppModePaddingBottom = 4; | |
| 76 const int kAppModePaddingLeft = 2; | |
| 77 | |
| 78 // The left padding of the tab strip. In Views, the tab strip has a left | |
| 79 // margin of FrameBorderThickness + kClientEdgeThickness. This offset is to | |
| 80 // account for kClientEdgeThickness. | |
| 81 const int kTabStripLeftPadding = 1; | |
| 82 | |
| 83 // Spacing between buttons of the titlebar. | |
| 84 const int kButtonSpacing = 2; | |
| 85 | |
| 86 // Spacing around outside of titlebar buttons. | |
| 87 const int kButtonOuterPadding = 2; | |
| 88 | |
| 89 // Spacing between tabstrip and window control buttons (when the window is | |
| 90 // maximized). | |
| 91 const int kMaximizedTabstripPadding = 16; | |
| 92 | |
| 93 gboolean OnMouseMoveEvent(GtkWidget* widget, GdkEventMotion* event, | |
| 94 BrowserWindowGtk* browser_window) { | |
| 95 // Reset to the default mouse cursor. | |
| 96 browser_window->ResetCustomFrameCursor(); | |
| 97 return TRUE; | |
| 98 } | |
| 99 | |
| 100 GdkPixbuf* GetOTRAvatar() { | |
| 101 static GdkPixbuf* otr_avatar = NULL; | |
| 102 if (!otr_avatar) { | |
| 103 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 104 otr_avatar = rb.GetRTLEnabledPixbufNamed(IDR_OTR_ICON); | |
| 105 } | |
| 106 return otr_avatar; | |
| 107 } | |
| 108 | |
| 109 // Converts a GdkColor to a color_utils::HSL. | |
| 110 color_utils::HSL GdkColorToHSL(const GdkColor* color) { | |
| 111 color_utils::HSL hsl; | |
| 112 color_utils::SkColorToHSL(SkColorSetRGB(color->red >> 8, | |
| 113 color->green >> 8, | |
| 114 color->blue >> 8), &hsl); | |
| 115 return hsl; | |
| 116 } | |
| 117 | |
| 118 // Returns either |one| or |two| based on which has a greater difference in | |
| 119 // luminosity. | |
| 120 GdkColor PickLuminosityContrastingColor(const GdkColor* base, | |
| 121 const GdkColor* one, | |
| 122 const GdkColor* two) { | |
| 123 // Convert all GdkColors to color_utils::HSLs. | |
| 124 color_utils::HSL baseHSL = GdkColorToHSL(base); | |
| 125 color_utils::HSL oneHSL = GdkColorToHSL(one); | |
| 126 color_utils::HSL twoHSL = GdkColorToHSL(two); | |
| 127 double one_difference = fabs(baseHSL.l - oneHSL.l); | |
| 128 double two_difference = fabs(baseHSL.l - twoHSL.l); | |
| 129 | |
| 130 // Be biased towards the first color presented. | |
| 131 if (two_difference > one_difference + 0.1) | |
| 132 return *two; | |
| 133 else | |
| 134 return *one; | |
| 135 } | |
| 136 | |
| 137 } // namespace | |
| 138 | |
| 139 //////////////////////////////////////////////////////////////////////////////// | |
| 140 // PopupPageMenuModel | |
| 141 | |
| 142 // A menu model that builds the contents of the menu shown for popups (when the | |
| 143 // user clicks on the favicon) and all of its submenus. | |
| 144 class PopupPageMenuModel : public menus::SimpleMenuModel { | |
| 145 public: | |
| 146 explicit PopupPageMenuModel(menus::SimpleMenuModel::Delegate* delegate, | |
| 147 Browser* browser); | |
| 148 virtual ~PopupPageMenuModel() { } | |
| 149 | |
| 150 private: | |
| 151 void Build(); | |
| 152 | |
| 153 // Models for submenus referenced by this model. SimpleMenuModel only uses | |
| 154 // weak references so these must be kept for the lifetime of the top-level | |
| 155 // model. | |
| 156 scoped_ptr<ZoomMenuModel> zoom_menu_model_; | |
| 157 scoped_ptr<EncodingMenuModel> encoding_menu_model_; | |
| 158 Browser* browser_; // weak | |
| 159 | |
| 160 DISALLOW_COPY_AND_ASSIGN(PopupPageMenuModel); | |
| 161 }; | |
| 162 | |
| 163 PopupPageMenuModel::PopupPageMenuModel( | |
| 164 menus::SimpleMenuModel::Delegate* delegate, | |
| 165 Browser* browser) | |
| 166 : menus::SimpleMenuModel(delegate), browser_(browser) { | |
| 167 Build(); | |
| 168 } | |
| 169 | |
| 170 void PopupPageMenuModel::Build() { | |
| 171 AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); | |
| 172 AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); | |
| 173 AddItemWithStringId(IDC_RELOAD, IDS_APP_MENU_RELOAD); | |
| 174 AddSeparator(); | |
| 175 AddItemWithStringId(IDC_SHOW_AS_TAB, IDS_SHOW_AS_TAB); | |
| 176 AddItemWithStringId(IDC_COPY_URL, IDS_APP_MENU_COPY_URL); | |
| 177 AddSeparator(); | |
| 178 AddItemWithStringId(IDC_CUT, IDS_CUT); | |
| 179 AddItemWithStringId(IDC_COPY, IDS_COPY); | |
| 180 AddItemWithStringId(IDC_PASTE, IDS_PASTE); | |
| 181 AddSeparator(); | |
| 182 AddItemWithStringId(IDC_FIND, IDS_FIND); | |
| 183 AddItemWithStringId(IDC_PRINT, IDS_PRINT); | |
| 184 zoom_menu_model_.reset(new ZoomMenuModel(delegate())); | |
| 185 AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_ZOOM_MENU, zoom_menu_model_.get()); | |
| 186 | |
| 187 encoding_menu_model_.reset(new EncodingMenuModel(browser_)); | |
| 188 AddSubMenuWithStringId(IDC_ENCODING_MENU, IDS_ENCODING_MENU, | |
| 189 encoding_menu_model_.get()); | |
| 190 | |
| 191 AddSeparator(); | |
| 192 AddItemWithStringId(IDC_CLOSE_WINDOW, IDS_CLOSE); | |
| 193 } | |
| 194 | |
| 195 //////////////////////////////////////////////////////////////////////////////// | |
| 196 // BrowserTitlebar | |
| 197 | |
| 198 // static | |
| 199 const char BrowserTitlebar::kDefaultButtonString[] = ":minimize,maximize,close"; | |
| 200 | |
| 201 BrowserTitlebar::BrowserTitlebar(BrowserWindowGtk* browser_window, | |
| 202 GtkWindow* window) | |
| 203 : browser_window_(browser_window), | |
| 204 window_(window), | |
| 205 titlebar_left_buttons_vbox_(NULL), | |
| 206 titlebar_right_buttons_vbox_(NULL), | |
| 207 titlebar_left_buttons_hbox_(NULL), | |
| 208 titlebar_right_buttons_hbox_(NULL), | |
| 209 titlebar_left_spy_frame_(NULL), | |
| 210 titlebar_right_spy_frame_(NULL), | |
| 211 top_padding_left_(NULL), | |
| 212 top_padding_right_(NULL), | |
| 213 app_mode_favicon_(NULL), | |
| 214 app_mode_title_(NULL), | |
| 215 using_custom_frame_(false), | |
| 216 window_has_focus_(false), | |
| 217 theme_provider_(NULL) { | |
| 218 Init(); | |
| 219 } | |
| 220 | |
| 221 void BrowserTitlebar::Init() { | |
| 222 // The widget hierarchy is shown below. | |
| 223 // | |
| 224 // +- EventBox (container_) ------------------------------------------------+ | |
| 225 // +- HBox (container_hbox_) -----------------------------------------------+ | |
| 226 // |+ VBox ---++- Algn. -++- Alignment --------------++- Algn. -++ VBox ---+| | |
| 227 // || titlebar||titlebar || (titlebar_alignment_) ||titlebar || titlebar|| | |
| 228 // || left ||left || ||right || right || | |
| 229 // || button ||spy || ||spy || button || | |
| 230 // || vbox ||frame ||+- TabStripGtk ---------+||frame || vbox || | |
| 231 // || || || tab tab tabclose ||| || || | |
| 232 // || || ||+------------------------+|| || || | |
| 233 // |+---------++---------++--------------------------++---------++---------+| | |
| 234 // +------------------------------------------------------------------------+ | |
| 235 // | |
| 236 // There are two vboxes on either side of |container_hbox_| because when the | |
| 237 // desktop is GNOME, the button placement is configurable based on a metacity | |
| 238 // gconf key. We can't just have one vbox and move it back and forth because | |
| 239 // the gconf language allows you to place buttons on both sides of the | |
| 240 // window. This being Linux, I'm sure there's a bunch of people who have | |
| 241 // already configured their window manager to do so and we don't want to get | |
| 242 // confused when that happens. The actual contents of these vboxes are lazily | |
| 243 // generated so they don't interfere with alignment when everything is | |
| 244 // stuffed in the other box. | |
| 245 // | |
| 246 // Each vbox has the contains the following hierarchy if it contains buttons: | |
| 247 // | |
| 248 // +- VBox (titlebar_{l,r}_buttons_vbox_) ---------+ | |
| 249 // |+- Fixed (top_padding_{l,r}_) ----------------+| | |
| 250 // ||+- HBox (titlebar_{l,r}_buttons_hbox_ ------+|| | |
| 251 // ||| (buttons of a configurable layout) ||| | |
| 252 // ||+-------------------------------------------+|| | |
| 253 // |+---------------------------------------------+| | |
| 254 // +-----------------------------------------------+ | |
| 255 // | |
| 256 // The two spy alignments are only allocated if this window is an incognito | |
| 257 // window. Only one of them holds the spy image. | |
| 258 // | |
| 259 // If we're a popup window or in app mode, we don't display the spy guy or | |
| 260 // the tab strip. Instead, put an hbox in titlebar_alignment_ in place of | |
| 261 // the tab strip. | |
| 262 // +- Alignment (titlebar_alignment_) -----------------------------------+ | |
| 263 // |+ HBox -------------------------------------------------------------+| | |
| 264 // ||+- TabStripGtk -++- Image ----------------++- Label --------------+|| | |
| 265 // ||| hidden ++ (app_mode_favicon_) || (app_mode_title_) ||| | |
| 266 // ||| || favicon || page title ||| | |
| 267 // ||+---------------++------------------------++----------------------+|| | |
| 268 // |+-------------------------------------------------------------------+| | |
| 269 // +---------------------------------------------------------------------+ | |
| 270 container_hbox_ = gtk_hbox_new(FALSE, 0); | |
| 271 | |
| 272 container_ = gtk_event_box_new(); | |
| 273 gtk_widget_set_name(container_, "chrome-browser-titlebar"); | |
| 274 gtk_event_box_set_visible_window(GTK_EVENT_BOX(container_), FALSE); | |
| 275 gtk_container_add(GTK_CONTAINER(container_), container_hbox_); | |
| 276 | |
| 277 g_signal_connect(container_, "scroll-event", G_CALLBACK(OnScrollThunk), this); | |
| 278 | |
| 279 g_signal_connect(window_, "window-state-event", | |
| 280 G_CALLBACK(OnWindowStateChangedThunk), this); | |
| 281 | |
| 282 // Allocate the two button boxes on the left and right parts of the bar, and | |
| 283 // spyguy frames in case of incognito mode. | |
| 284 titlebar_left_buttons_vbox_ = gtk_vbox_new(FALSE, 0); | |
| 285 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_buttons_vbox_, | |
| 286 FALSE, FALSE, 0); | |
| 287 if (browser_window_->browser()->profile()->IsOffTheRecord() && | |
| 288 browser_window_->browser()->type() == Browser::TYPE_NORMAL) { | |
| 289 titlebar_left_spy_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 290 gtk_widget_set_no_show_all(titlebar_left_spy_frame_, TRUE); | |
| 291 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_left_spy_frame_), 0, | |
| 292 kOTRBottomSpacing, kOTRSideSpacing, kOTRSideSpacing); | |
| 293 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_spy_frame_, | |
| 294 FALSE, FALSE, 0); | |
| 295 | |
| 296 titlebar_right_spy_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 297 gtk_widget_set_no_show_all(titlebar_right_spy_frame_, TRUE); | |
| 298 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_right_spy_frame_), 0, | |
| 299 kOTRBottomSpacing, kOTRSideSpacing, kOTRSideSpacing); | |
| 300 gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_spy_frame_, | |
| 301 FALSE, FALSE, 0); | |
| 302 } | |
| 303 titlebar_right_buttons_vbox_ = gtk_vbox_new(FALSE, 0); | |
| 304 gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_buttons_vbox_, | |
| 305 FALSE, FALSE, 0); | |
| 306 | |
| 307 #if defined(USE_GCONF) | |
| 308 // Either read the gconf database and register for updates (on GNOME), or use | |
| 309 // the default value (anywhere else). | |
| 310 GConfTitlebarListener::GetInstance()->SetTitlebarButtons(this); | |
| 311 #else | |
| 312 BuildButtons(kDefaultButtonString); | |
| 313 #endif | |
| 314 | |
| 315 // We use an alignment to control the titlebar height. | |
| 316 titlebar_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 317 if (browser_window_->browser()->type() == Browser::TYPE_NORMAL) { | |
| 318 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_alignment_, TRUE, | |
| 319 TRUE, 0); | |
| 320 | |
| 321 // Put the tab strip in the titlebar. | |
| 322 gtk_container_add(GTK_CONTAINER(titlebar_alignment_), | |
| 323 browser_window_->tabstrip()->widget()); | |
| 324 } else { | |
| 325 // App mode specific widgets. | |
| 326 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_alignment_, TRUE, | |
| 327 TRUE, 0); | |
| 328 GtkWidget* app_mode_hbox = gtk_hbox_new(FALSE, kIconTitleSpacing); | |
| 329 gtk_container_add(GTK_CONTAINER(titlebar_alignment_), app_mode_hbox); | |
| 330 | |
| 331 // Put the tab strip in the hbox even though we don't show it. Sometimes | |
| 332 // we need the position of the tab strip so make sure it's in our widget | |
| 333 // hierarchy. | |
| 334 gtk_box_pack_start(GTK_BOX(app_mode_hbox), | |
| 335 browser_window_->tabstrip()->widget(), FALSE, FALSE, 0); | |
| 336 | |
| 337 GtkWidget* favicon_event_box = gtk_event_box_new(); | |
| 338 gtk_event_box_set_visible_window(GTK_EVENT_BOX(favicon_event_box), FALSE); | |
| 339 g_signal_connect(favicon_event_box, "button-press-event", | |
| 340 G_CALLBACK(OnButtonPressedThunk), this); | |
| 341 gtk_box_pack_start(GTK_BOX(app_mode_hbox), favicon_event_box, FALSE, | |
| 342 FALSE, 0); | |
| 343 // We use the app logo as a placeholder image so the title doesn't jump | |
| 344 // around. | |
| 345 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 346 app_mode_favicon_ = gtk_image_new_from_pixbuf( | |
| 347 rb.GetRTLEnabledPixbufNamed(IDR_PRODUCT_LOGO_16)); | |
| 348 g_object_set_data(G_OBJECT(app_mode_favicon_), "left-align-popup", | |
| 349 reinterpret_cast<void*>(true)); | |
| 350 gtk_container_add(GTK_CONTAINER(favicon_event_box), app_mode_favicon_); | |
| 351 | |
| 352 app_mode_title_ = gtk_label_new(NULL); | |
| 353 gtk_label_set_ellipsize(GTK_LABEL(app_mode_title_), PANGO_ELLIPSIZE_END); | |
| 354 gtk_misc_set_alignment(GTK_MISC(app_mode_title_), 0.0, 0.5); | |
| 355 gtk_box_pack_start(GTK_BOX(app_mode_hbox), app_mode_title_, TRUE, TRUE, | |
| 356 0); | |
| 357 | |
| 358 // Register with the theme provider to set the |app_mode_title_| label | |
| 359 // color. | |
| 360 theme_provider_ = GtkThemeProvider::GetFrom( | |
| 361 browser_window_->browser()->profile()); | |
| 362 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | |
| 363 NotificationService::AllSources()); | |
| 364 theme_provider_->InitThemesFor(this); | |
| 365 UpdateTitleAndIcon(); | |
| 366 } | |
| 367 | |
| 368 gtk_widget_show_all(container_); | |
| 369 | |
| 370 ActiveWindowWatcherX::AddObserver(this); | |
| 371 } | |
| 372 | |
| 373 BrowserTitlebar::~BrowserTitlebar() { | |
| 374 ActiveWindowWatcherX::RemoveObserver(this); | |
| 375 #if defined(USE_GCONF) | |
| 376 GConfTitlebarListener::GetInstance()->RemoveObserver(this); | |
| 377 #endif | |
| 378 } | |
| 379 | |
| 380 void BrowserTitlebar::BuildButtons(const std::string& button_string) { | |
| 381 // Clear out all previous data. | |
| 382 close_button_.reset(); | |
| 383 restore_button_.reset(); | |
| 384 maximize_button_.reset(); | |
| 385 minimize_button_.reset(); | |
| 386 gtk_util::RemoveAllChildren(titlebar_left_buttons_vbox_); | |
| 387 gtk_util::RemoveAllChildren(titlebar_right_buttons_vbox_); | |
| 388 titlebar_left_buttons_hbox_ = NULL; | |
| 389 titlebar_right_buttons_hbox_ = NULL; | |
| 390 top_padding_left_ = NULL; | |
| 391 top_padding_right_ = NULL; | |
| 392 | |
| 393 bool left_side = true; | |
| 394 StringTokenizer tokenizer(button_string, ":,"); | |
| 395 tokenizer.set_options(StringTokenizer::RETURN_DELIMS); | |
| 396 int left_count = 0; | |
| 397 int right_count = 0; | |
| 398 while (tokenizer.GetNext()) { | |
| 399 if (tokenizer.token_is_delim()) { | |
| 400 if (*tokenizer.token_begin() == ':') | |
| 401 left_side = false; | |
| 402 } else { | |
| 403 base::StringPiece token = tokenizer.token_piece(); | |
| 404 if (token == "minimize") { | |
| 405 (left_side ? left_count : right_count)++; | |
| 406 GtkWidget* parent_box = GetButtonHBox(left_side); | |
| 407 minimize_button_.reset( | |
| 408 BuildTitlebarButton(IDR_MINIMIZE, IDR_MINIMIZE_P, | |
| 409 IDR_MINIMIZE_H, parent_box, | |
| 410 IDS_XPFRAME_MINIMIZE_TOOLTIP)); | |
| 411 | |
| 412 gtk_widget_size_request(minimize_button_->widget(), | |
| 413 &minimize_button_req_); | |
| 414 } else if (token == "maximize") { | |
| 415 (left_side ? left_count : right_count)++; | |
| 416 GtkWidget* parent_box = GetButtonHBox(left_side); | |
| 417 restore_button_.reset( | |
| 418 BuildTitlebarButton(IDR_RESTORE, IDR_RESTORE_P, | |
| 419 IDR_RESTORE_H, parent_box, | |
| 420 IDS_XPFRAME_RESTORE_TOOLTIP)); | |
| 421 maximize_button_.reset( | |
| 422 BuildTitlebarButton(IDR_MAXIMIZE, IDR_MAXIMIZE_P, | |
| 423 IDR_MAXIMIZE_H, parent_box, | |
| 424 IDS_XPFRAME_MAXIMIZE_TOOLTIP)); | |
| 425 | |
| 426 gtk_util::SetButtonClickableByMouseButtons(maximize_button_->widget(), | |
| 427 true, true, true); | |
| 428 gtk_widget_size_request(restore_button_->widget(), | |
| 429 &restore_button_req_); | |
| 430 } else if (token == "close") { | |
| 431 (left_side ? left_count : right_count)++; | |
| 432 GtkWidget* parent_box = GetButtonHBox(left_side); | |
| 433 close_button_.reset( | |
| 434 BuildTitlebarButton(IDR_CLOSE, IDR_CLOSE_P, | |
| 435 IDR_CLOSE_H, parent_box, | |
| 436 IDS_XPFRAME_CLOSE_TOOLTIP)); | |
| 437 close_button_->set_flipped(left_side); | |
| 438 | |
| 439 gtk_widget_size_request(close_button_->widget(), &close_button_req_); | |
| 440 } | |
| 441 // Ignore any other values like "pin" since we don't have images for | |
| 442 // those. | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 // If we are in incognito mode, add the spy guy to either the end of the left | |
| 447 // or the beginning of the right depending on which side has fewer buttons. | |
| 448 if (browser_window_->browser()->profile()->IsOffTheRecord() && | |
| 449 browser_window_->browser()->type() == Browser::TYPE_NORMAL) { | |
| 450 GtkWidget* spy_guy = gtk_image_new_from_pixbuf(GetOTRAvatar()); | |
| 451 gtk_misc_set_alignment(GTK_MISC(spy_guy), 0.0, 1.0); | |
| 452 gtk_widget_set_size_request(spy_guy, -1, 0); | |
| 453 gtk_widget_show(spy_guy); | |
| 454 | |
| 455 // Remove previous state. | |
| 456 gtk_util::RemoveAllChildren(titlebar_left_spy_frame_); | |
| 457 gtk_util::RemoveAllChildren(titlebar_right_spy_frame_); | |
| 458 | |
| 459 if (right_count > left_count) { | |
| 460 gtk_container_add(GTK_CONTAINER(titlebar_left_spy_frame_), spy_guy); | |
| 461 gtk_widget_show(titlebar_left_spy_frame_); | |
| 462 gtk_widget_hide(titlebar_right_spy_frame_); | |
| 463 } else { | |
| 464 gtk_container_add(GTK_CONTAINER(titlebar_right_spy_frame_), spy_guy); | |
| 465 gtk_widget_show(titlebar_right_spy_frame_); | |
| 466 gtk_widget_hide(titlebar_left_spy_frame_); | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 // Now show the correct widgets in the two hierarchies. | |
| 471 if (using_custom_frame_) { | |
| 472 gtk_widget_show_all(titlebar_left_buttons_vbox_); | |
| 473 gtk_widget_show_all(titlebar_right_buttons_vbox_); | |
| 474 } | |
| 475 UpdateMaximizeRestoreVisibility(); | |
| 476 } | |
| 477 | |
| 478 GtkWidget* BrowserTitlebar::GetButtonHBox(bool left_side) { | |
| 479 if (left_side && titlebar_left_buttons_hbox_) | |
| 480 return titlebar_left_buttons_hbox_; | |
| 481 else if (!left_side && titlebar_right_buttons_hbox_) | |
| 482 return titlebar_right_buttons_hbox_; | |
| 483 | |
| 484 // We put the min/max/restore/close buttons in a vbox so they are top aligned | |
| 485 // (up to padding) and don't vertically stretch. | |
| 486 GtkWidget* vbox = left_side ? titlebar_left_buttons_vbox_ : | |
| 487 titlebar_right_buttons_vbox_; | |
| 488 | |
| 489 GtkWidget* top_padding = gtk_fixed_new(); | |
| 490 gtk_widget_set_size_request(top_padding, -1, kButtonOuterPadding); | |
| 491 gtk_box_pack_start(GTK_BOX(vbox), top_padding, FALSE, FALSE, 0); | |
| 492 | |
| 493 GtkWidget* buttons_hbox = gtk_hbox_new(FALSE, kButtonSpacing); | |
| 494 gtk_box_pack_start(GTK_BOX(vbox), buttons_hbox, FALSE, FALSE, 0); | |
| 495 | |
| 496 if (left_side) { | |
| 497 titlebar_left_buttons_hbox_ = buttons_hbox; | |
| 498 top_padding_left_ = top_padding; | |
| 499 } else { | |
| 500 titlebar_right_buttons_hbox_ = buttons_hbox; | |
| 501 top_padding_right_ = top_padding; | |
| 502 } | |
| 503 | |
| 504 return buttons_hbox; | |
| 505 } | |
| 506 | |
| 507 CustomDrawButton* BrowserTitlebar::BuildTitlebarButton(int image, | |
| 508 int image_pressed, int image_hot, GtkWidget* box, int tooltip) { | |
| 509 CustomDrawButton* button = new CustomDrawButton(image, image_pressed, | |
| 510 image_hot, 0); | |
| 511 gtk_widget_add_events(GTK_WIDGET(button->widget()), GDK_POINTER_MOTION_MASK); | |
| 512 g_signal_connect(button->widget(), "clicked", | |
| 513 G_CALLBACK(OnButtonClickedThunk), this); | |
| 514 g_signal_connect(button->widget(), "motion-notify-event", | |
| 515 G_CALLBACK(OnMouseMoveEvent), browser_window_); | |
| 516 std::string localized_tooltip = l10n_util::GetStringUTF8(tooltip); | |
| 517 gtk_widget_set_tooltip_text(button->widget(), | |
| 518 localized_tooltip.c_str()); | |
| 519 gtk_box_pack_start(GTK_BOX(box), button->widget(), FALSE, FALSE, 0); | |
| 520 return button; | |
| 521 } | |
| 522 | |
| 523 void BrowserTitlebar::UpdateCustomFrame(bool use_custom_frame) { | |
| 524 using_custom_frame_ = use_custom_frame; | |
| 525 if (use_custom_frame) { | |
| 526 if (titlebar_left_buttons_vbox_) | |
| 527 gtk_widget_show_all(titlebar_left_buttons_vbox_); | |
| 528 if (titlebar_right_buttons_vbox_) | |
| 529 gtk_widget_show_all(titlebar_right_buttons_vbox_); | |
| 530 } else { | |
| 531 if (titlebar_left_buttons_vbox_) | |
| 532 gtk_widget_hide(titlebar_left_buttons_vbox_); | |
| 533 if (titlebar_right_buttons_vbox_) | |
| 534 gtk_widget_hide(titlebar_right_buttons_vbox_); | |
| 535 } | |
| 536 UpdateTitlebarAlignment(); | |
| 537 } | |
| 538 | |
| 539 void BrowserTitlebar::UpdateTitleAndIcon() { | |
| 540 if (!app_mode_title_) | |
| 541 return; | |
| 542 | |
| 543 // Get the page title and elide it to the available space. | |
| 544 string16 title = browser_window_->browser()->GetWindowTitleForCurrentTab(); | |
| 545 gtk_label_set_text(GTK_LABEL(app_mode_title_), UTF16ToUTF8(title).c_str()); | |
| 546 | |
| 547 // Note: this isn't browser_window_->browser()->type() & Browser::TYPE_APP | |
| 548 // because we want to exclude Browser::TYPE_APP_POPUP. | |
| 549 if (browser_window_->browser()->type() == Browser::TYPE_APP || | |
| 550 browser_window_->browser()->type() == Browser::TYPE_APP_PANEL) { | |
| 551 // Update the system app icon. We don't need to update the icon in the top | |
| 552 // left of the custom frame, that will get updated when the throbber is | |
| 553 // updated. | |
| 554 SkBitmap icon = browser_window_->browser()->GetCurrentPageIcon(); | |
| 555 if (icon.empty()) { | |
| 556 gtk_util::SetWindowIcon(window_); | |
| 557 } else { | |
| 558 GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); | |
| 559 gtk_window_set_icon(window_, icon_pixbuf); | |
| 560 g_object_unref(icon_pixbuf); | |
| 561 } | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 void BrowserTitlebar::UpdateThrobber(TabContents* tab_contents) { | |
| 566 DCHECK(app_mode_favicon_); | |
| 567 | |
| 568 if (tab_contents && tab_contents->is_loading()) { | |
| 569 GdkPixbuf* icon_pixbuf = | |
| 570 throbber_.GetNextFrame(tab_contents->waiting_for_response()); | |
| 571 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), icon_pixbuf); | |
| 572 } else { | |
| 573 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 574 | |
| 575 // Note: this isn't browser_window_->browser()->type() & Browser::TYPE_APP | |
| 576 // because we want to exclude Browser::TYPE_APP_POPUP. | |
| 577 if (browser_window_->browser()->type() == Browser::TYPE_APP || | |
| 578 browser_window_->browser()->type() == Browser::TYPE_APP_PANEL) { | |
| 579 SkBitmap icon = browser_window_->browser()->GetCurrentPageIcon(); | |
| 580 if (icon.empty()) { | |
| 581 // Fallback to the Chromium icon if the page has no icon. | |
| 582 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), | |
| 583 rb.GetPixbufNamed(IDR_PRODUCT_LOGO_16)); | |
| 584 } else { | |
| 585 GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); | |
| 586 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), icon_pixbuf); | |
| 587 g_object_unref(icon_pixbuf); | |
| 588 } | |
| 589 } else { | |
| 590 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), | |
| 591 rb.GetPixbufNamed(IDR_PRODUCT_LOGO_16)); | |
| 592 } | |
| 593 throbber_.Reset(); | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 void BrowserTitlebar::UpdateTitlebarAlignment() { | |
| 598 if (browser_window_->browser()->type() == Browser::TYPE_NORMAL) { | |
| 599 int top_padding = 0; | |
| 600 int side_padding = 0; | |
| 601 int vertical_offset = kNormalVerticalOffset; | |
| 602 | |
| 603 if (using_custom_frame_) { | |
| 604 if (!browser_window_->IsMaximized()) { | |
| 605 top_padding = kTitlebarHeight; | |
| 606 } else if (using_custom_frame_ && browser_window_->IsMaximized()) { | |
| 607 vertical_offset = 0; | |
| 608 side_padding = kMaximizedTabstripPadding; | |
| 609 } | |
| 610 } | |
| 611 | |
| 612 int right_padding = 0; | |
| 613 int left_padding = kTabStripLeftPadding; | |
| 614 if (titlebar_right_buttons_hbox_) | |
| 615 right_padding = side_padding; | |
| 616 if (titlebar_left_buttons_hbox_) | |
| 617 left_padding = side_padding; | |
| 618 | |
| 619 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_alignment_), | |
| 620 top_padding, 0, | |
| 621 left_padding, right_padding); | |
| 622 browser_window_->tabstrip()->SetVerticalOffset(vertical_offset); | |
| 623 } else { | |
| 624 if (using_custom_frame_ && !browser_window_->IsFullscreen()) { | |
| 625 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_alignment_), | |
| 626 kAppModePaddingTop, kAppModePaddingBottom, kAppModePaddingLeft, 0); | |
| 627 gtk_widget_show(titlebar_alignment_); | |
| 628 } else { | |
| 629 gtk_widget_hide(titlebar_alignment_); | |
| 630 } | |
| 631 } | |
| 632 | |
| 633 // Resize the buttons so that the clickable area extends all the way to the | |
| 634 // edge of the browser window. | |
| 635 GtkRequisition close_button_req = close_button_req_; | |
| 636 GtkRequisition minimize_button_req = minimize_button_req_; | |
| 637 GtkRequisition restore_button_req = restore_button_req_; | |
| 638 if (using_custom_frame_ && browser_window_->IsMaximized()) { | |
| 639 close_button_req.width += kButtonOuterPadding; | |
| 640 close_button_req.height += kButtonOuterPadding; | |
| 641 minimize_button_req.height += kButtonOuterPadding; | |
| 642 restore_button_req.height += kButtonOuterPadding; | |
| 643 if (top_padding_left_) | |
| 644 gtk_widget_hide(top_padding_left_); | |
| 645 if (top_padding_right_) | |
| 646 gtk_widget_hide(top_padding_right_); | |
| 647 } else { | |
| 648 if (top_padding_left_) | |
| 649 gtk_widget_show(top_padding_left_); | |
| 650 if (top_padding_right_) | |
| 651 gtk_widget_show(top_padding_right_); | |
| 652 } | |
| 653 if (close_button_.get()) { | |
| 654 gtk_widget_set_size_request(close_button_->widget(), | |
| 655 close_button_req.width, | |
| 656 close_button_req.height); | |
| 657 } | |
| 658 if (minimize_button_.get()) { | |
| 659 gtk_widget_set_size_request(minimize_button_->widget(), | |
| 660 minimize_button_req.width, | |
| 661 minimize_button_req.height); | |
| 662 } | |
| 663 if (maximize_button_.get()) { | |
| 664 gtk_widget_set_size_request(restore_button_->widget(), | |
| 665 restore_button_req.width, | |
| 666 restore_button_req.height); | |
| 667 } | |
| 668 } | |
| 669 | |
| 670 void BrowserTitlebar::UpdateTextColor() { | |
| 671 if (!app_mode_title_) | |
| 672 return; | |
| 673 | |
| 674 if (theme_provider_ && theme_provider_->UseGtkTheme()) { | |
| 675 // We don't really have any good options here. | |
| 676 // | |
| 677 // Colors from window manager themes aren't exposed in GTK; the window | |
| 678 // manager is a separate component and when there is information sharing | |
| 679 // (in the case of metacity), it's one way where the window manager reads | |
| 680 // data from the GTK theme (which allows us to do a decent job with | |
| 681 // picking the frame color). | |
| 682 // | |
| 683 // We probably won't match in the majority of cases, but we can at the | |
| 684 // very least make things legible. The default metacity and xfwm themes | |
| 685 // on ubuntu have white text hardcoded. Determine whether black or white | |
| 686 // has more luminosity contrast and then set that color as the text | |
| 687 // color. | |
| 688 GdkColor frame_color; | |
| 689 if (window_has_focus_) { | |
| 690 frame_color = theme_provider_->GetGdkColor( | |
| 691 BrowserThemeProvider::COLOR_FRAME); | |
| 692 } else { | |
| 693 frame_color = theme_provider_->GetGdkColor( | |
| 694 BrowserThemeProvider::COLOR_FRAME_INACTIVE); | |
| 695 } | |
| 696 GdkColor text_color = PickLuminosityContrastingColor( | |
| 697 &frame_color, >k_util::kGdkWhite, >k_util::kGdkBlack); | |
| 698 gtk_util::SetLabelColor(app_mode_title_, &text_color); | |
| 699 } else { | |
| 700 gtk_util::SetLabelColor(app_mode_title_, >k_util::kGdkWhite); | |
| 701 } | |
| 702 } | |
| 703 | |
| 704 void BrowserTitlebar::ShowFaviconMenu(GdkEventButton* event) { | |
| 705 if (!favicon_menu_model_.get()) { | |
| 706 favicon_menu_model_.reset( | |
| 707 new PopupPageMenuModel(this, browser_window_->browser())); | |
| 708 | |
| 709 favicon_menu_.reset(new MenuGtk(NULL, favicon_menu_model_.get())); | |
| 710 } | |
| 711 | |
| 712 favicon_menu_->Popup(app_mode_favicon_, reinterpret_cast<GdkEvent*>(event)); | |
| 713 } | |
| 714 | |
| 715 void BrowserTitlebar::MaximizeButtonClicked() { | |
| 716 GdkEvent* event = gtk_get_current_event(); | |
| 717 if (event->button.button == 1) { | |
| 718 gtk_window_maximize(window_); | |
| 719 } else { | |
| 720 GtkWidget* widget = GTK_WIDGET(window_); | |
| 721 GdkScreen* screen = gtk_widget_get_screen(widget); | |
| 722 gint monitor = gdk_screen_get_monitor_at_window(screen, widget->window); | |
| 723 GdkRectangle screen_rect; | |
| 724 gdk_screen_get_monitor_geometry(screen, monitor, &screen_rect); | |
| 725 | |
| 726 gint x, y; | |
| 727 gtk_window_get_position(window_, &x, &y); | |
| 728 gint width = widget->allocation.width; | |
| 729 gint height = widget->allocation.height; | |
| 730 | |
| 731 if (event->button.button == 3) { | |
| 732 x = 0; | |
| 733 width = screen_rect.width; | |
| 734 } else if (event->button.button == 2) { | |
| 735 y = 0; | |
| 736 height = screen_rect.height; | |
| 737 } | |
| 738 | |
| 739 browser_window_->SetBounds(gfx::Rect(x, y, width, height)); | |
| 740 } | |
| 741 gdk_event_free(event); | |
| 742 } | |
| 743 | |
| 744 void BrowserTitlebar::UpdateMaximizeRestoreVisibility() { | |
| 745 if (maximize_button_.get()) { | |
| 746 if (browser_window_->IsMaximized()) { | |
| 747 gtk_widget_hide(maximize_button_->widget()); | |
| 748 gtk_widget_show(restore_button_->widget()); | |
| 749 } else { | |
| 750 gtk_widget_hide(restore_button_->widget()); | |
| 751 gtk_widget_show(maximize_button_->widget()); | |
| 752 } | |
| 753 } | |
| 754 } | |
| 755 | |
| 756 gboolean BrowserTitlebar::OnWindowStateChanged(GtkWindow* window, | |
| 757 GdkEventWindowState* event) { | |
| 758 UpdateMaximizeRestoreVisibility(); | |
| 759 UpdateTitlebarAlignment(); | |
| 760 UpdateTextColor(); | |
| 761 return FALSE; | |
| 762 } | |
| 763 | |
| 764 gboolean BrowserTitlebar::OnScroll(GtkWidget* widget, GdkEventScroll* event) { | |
| 765 TabStripModel* tabstrip_model = browser_window_->browser()->tabstrip_model(); | |
| 766 int index = tabstrip_model->selected_index(); | |
| 767 if (event->direction == GDK_SCROLL_LEFT || | |
| 768 event->direction == GDK_SCROLL_UP) { | |
| 769 if (index != 0) | |
| 770 tabstrip_model->SelectPreviousTab(); | |
| 771 } else if (index + 1 < tabstrip_model->count()) { | |
| 772 tabstrip_model->SelectNextTab(); | |
| 773 } | |
| 774 return TRUE; | |
| 775 } | |
| 776 | |
| 777 // static | |
| 778 void BrowserTitlebar::OnButtonClicked(GtkWidget* button) { | |
| 779 if (close_button_.get() && close_button_->widget() == button) { | |
| 780 browser_window_->Close(); | |
| 781 } else if (restore_button_.get() && restore_button_->widget() == button) { | |
| 782 browser_window_->UnMaximize(); | |
| 783 } else if (maximize_button_.get() && maximize_button_->widget() == button) { | |
| 784 MaximizeButtonClicked(); | |
| 785 } else if (minimize_button_.get() && minimize_button_->widget() == button) { | |
| 786 gtk_window_iconify(window_); | |
| 787 } | |
| 788 } | |
| 789 | |
| 790 gboolean BrowserTitlebar::OnButtonPressed(GtkWidget* widget, | |
| 791 GdkEventButton* event) { | |
| 792 if (event->button != 1) | |
| 793 return FALSE; | |
| 794 | |
| 795 ShowFaviconMenu(event); | |
| 796 return TRUE; | |
| 797 } | |
| 798 | |
| 799 void BrowserTitlebar::ShowContextMenu() { | |
| 800 if (!context_menu_.get()) { | |
| 801 context_menu_model_.reset(new ContextMenuModel(this)); | |
| 802 context_menu_.reset(new MenuGtk(NULL, context_menu_model_.get())); | |
| 803 } | |
| 804 | |
| 805 context_menu_->PopupAsContext(gtk_get_current_event_time()); | |
| 806 } | |
| 807 | |
| 808 bool BrowserTitlebar::IsCommandIdEnabled(int command_id) const { | |
| 809 if (command_id == kShowWindowDecorationsCommand) | |
| 810 return true; | |
| 811 | |
| 812 return browser_window_->browser()->command_updater()-> | |
| 813 IsCommandEnabled(command_id); | |
| 814 } | |
| 815 | |
| 816 bool BrowserTitlebar::IsCommandIdChecked(int command_id) const { | |
| 817 if (command_id == kShowWindowDecorationsCommand) { | |
| 818 PrefService* prefs = browser_window_->browser()->profile()->GetPrefs(); | |
| 819 return !prefs->GetBoolean(prefs::kUseCustomChromeFrame); | |
| 820 } | |
| 821 | |
| 822 EncodingMenuController controller; | |
| 823 if (controller.DoesCommandBelongToEncodingMenu(command_id)) { | |
| 824 TabContents* tab_contents = | |
| 825 browser_window_->browser()->GetSelectedTabContents(); | |
| 826 if (tab_contents) { | |
| 827 return controller.IsItemChecked(browser_window_->browser()->profile(), | |
| 828 tab_contents->encoding(), | |
| 829 command_id); | |
| 830 } | |
| 831 return false; | |
| 832 } | |
| 833 | |
| 834 NOTREACHED(); | |
| 835 return false; | |
| 836 } | |
| 837 | |
| 838 void BrowserTitlebar::ExecuteCommand(int command_id) { | |
| 839 if (command_id == kShowWindowDecorationsCommand) { | |
| 840 PrefService* prefs = browser_window_->browser()->profile()->GetPrefs(); | |
| 841 prefs->SetBoolean(prefs::kUseCustomChromeFrame, | |
| 842 !prefs->GetBoolean(prefs::kUseCustomChromeFrame)); | |
| 843 return; | |
| 844 } | |
| 845 | |
| 846 browser_window_->browser()->ExecuteCommand(command_id); | |
| 847 } | |
| 848 | |
| 849 bool BrowserTitlebar::GetAcceleratorForCommandId( | |
| 850 int command_id, menus::Accelerator* accelerator) { | |
| 851 const menus::AcceleratorGtk* accelerator_gtk = | |
| 852 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand( | |
| 853 command_id); | |
| 854 if (accelerator_gtk) | |
| 855 *accelerator = *accelerator_gtk; | |
| 856 return accelerator_gtk; | |
| 857 } | |
| 858 | |
| 859 void BrowserTitlebar::Observe(NotificationType type, | |
| 860 const NotificationSource& source, | |
| 861 const NotificationDetails& details) { | |
| 862 switch (type.value) { | |
| 863 case NotificationType::BROWSER_THEME_CHANGED: | |
| 864 UpdateTextColor(); | |
| 865 break; | |
| 866 | |
| 867 default: | |
| 868 NOTREACHED(); | |
| 869 } | |
| 870 } | |
| 871 | |
| 872 void BrowserTitlebar::ActiveWindowChanged(GdkWindow* active_window) { | |
| 873 // Can be called during shutdown; BrowserWindowGtk will set our |window_| | |
| 874 // to NULL during that time. | |
| 875 if (!window_) | |
| 876 return; | |
| 877 | |
| 878 window_has_focus_ = GTK_WIDGET(window_)->window == active_window; | |
| 879 UpdateTextColor(); | |
| 880 } | |
| 881 | |
| 882 /////////////////////////////////////////////////////////////////////////////// | |
| 883 // BrowserTitlebar::Throbber implementation | |
| 884 // TODO(tc): Handle anti-clockwise spinning when waiting for a connection. | |
| 885 | |
| 886 // We don't bother to clean up these or the pixbufs they contain when we exit. | |
| 887 static std::vector<GdkPixbuf*>* g_throbber_frames = NULL; | |
| 888 static std::vector<GdkPixbuf*>* g_throbber_waiting_frames = NULL; | |
| 889 | |
| 890 // Load |resource_id| from the ResourceBundle and split it into a series of | |
| 891 // square GdkPixbufs that get stored in |frames|. | |
| 892 static void MakeThrobberFrames(int resource_id, | |
| 893 std::vector<GdkPixbuf*>* frames) { | |
| 894 ResourceBundle &rb = ResourceBundle::GetSharedInstance(); | |
| 895 SkBitmap* frame_strip = rb.GetBitmapNamed(resource_id); | |
| 896 | |
| 897 // Each frame of the animation is a square, so we use the height as the | |
| 898 // frame size. | |
| 899 int frame_size = frame_strip->height(); | |
| 900 size_t num_frames = frame_strip->width() / frame_size; | |
| 901 | |
| 902 // Make a separate GdkPixbuf for each frame of the animation. | |
| 903 for (size_t i = 0; i < num_frames; ++i) { | |
| 904 SkBitmap frame = SkBitmapOperations::CreateTiledBitmap(*frame_strip, | |
| 905 i * frame_size, 0, frame_size, frame_size); | |
| 906 frames->push_back(gfx::GdkPixbufFromSkBitmap(&frame)); | |
| 907 } | |
| 908 } | |
| 909 | |
| 910 GdkPixbuf* BrowserTitlebar::Throbber::GetNextFrame(bool is_waiting) { | |
| 911 Throbber::InitFrames(); | |
| 912 if (is_waiting) { | |
| 913 return (*g_throbber_waiting_frames)[current_waiting_frame_++ % | |
| 914 g_throbber_waiting_frames->size()]; | |
| 915 } else { | |
| 916 return (*g_throbber_frames)[current_frame_++ % g_throbber_frames->size()]; | |
| 917 } | |
| 918 } | |
| 919 | |
| 920 void BrowserTitlebar::Throbber::Reset() { | |
| 921 current_frame_ = 0; | |
| 922 current_waiting_frame_ = 0; | |
| 923 } | |
| 924 | |
| 925 // static | |
| 926 void BrowserTitlebar::Throbber::InitFrames() { | |
| 927 if (g_throbber_frames) | |
| 928 return; | |
| 929 | |
| 930 // We load the light version of the throbber since it'll be in the titlebar. | |
| 931 g_throbber_frames = new std::vector<GdkPixbuf*>; | |
| 932 MakeThrobberFrames(IDR_THROBBER_LIGHT, g_throbber_frames); | |
| 933 | |
| 934 g_throbber_waiting_frames = new std::vector<GdkPixbuf*>; | |
| 935 MakeThrobberFrames(IDR_THROBBER_WAITING_LIGHT, g_throbber_waiting_frames); | |
| 936 } | |
| 937 | |
| 938 BrowserTitlebar::ContextMenuModel::ContextMenuModel( | |
| 939 menus::SimpleMenuModel::Delegate* delegate) | |
| 940 : SimpleMenuModel(delegate) { | |
| 941 AddItemWithStringId(IDC_NEW_TAB, IDS_TAB_CXMENU_NEWTAB); | |
| 942 AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB); | |
| 943 AddSeparator(); | |
| 944 AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); | |
| 945 AddSeparator(); | |
| 946 AddCheckItemWithStringId(kShowWindowDecorationsCommand, | |
| 947 IDS_SHOW_WINDOW_DECORATIONS_MENU); | |
| 948 } | |
| OLD | NEW |