Index: chrome/browser/profiles/profile_shortcut_manager_win.cc |
=================================================================== |
--- chrome/browser/profiles/profile_shortcut_manager_win.cc (revision 112717) |
+++ chrome/browser/profiles/profile_shortcut_manager_win.cc (working copy) |
@@ -10,9 +10,11 @@ |
#include "base/path_service.h" |
#include "base/stringprintf.h" |
#include "base/utf_string_conversions.h" |
+#include "chrome/browser/app_icon_win.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/prefs/pref_service.h" |
#include "chrome/browser/profiles/profile_info_cache.h" |
+#include "chrome/browser/profiles/profile_info_util.h" |
#include "chrome/browser/profiles/profile_manager.h" |
#include "chrome/common/chrome_constants.h" |
#include "chrome/common/chrome_switches.h" |
@@ -21,12 +23,21 @@ |
#include "chrome/installer/util/shell_util.h" |
#include "content/public/browser/browser_thread.h" |
#include "grit/generated_resources.h" |
+#include "skia/ext/image_operations.h" |
+#include "skia/ext/platform_canvas.h" |
#include "ui/base/l10n/l10n_util.h" |
+#include "ui/gfx/icon_util.h" |
+#include "ui/gfx/image/image.h" |
using content::BrowserThread; |
namespace { |
+const char kProfileIconFileName[] = "Google Profile.ico"; |
+const int kProfileAvatarShortcutBadgeWidth = 28; |
+const int kProfileAvatarShortcutBadgeHeight = 28; |
+const int kShortcutIconSize = 48; |
+ |
// Creates the argument to pass to the Windows executable that launches Chrome |
// with the profile in |profile_base_dir|. |
// For example: --profile-directory="Profile 2" |
@@ -44,6 +55,172 @@ |
bool_function.Run(); |
} |
+// Creates a desktop shortcut icon file (.ico) on the disk for a given profile, |
+// badging the browser distribution icon with the profile avatar. |
+// |profile_base_dir| is the base directory (and key) of the profile. Returns |
+// a path to the shortcut icon file on disk, which is empty if this fails. |
+// Use index 0 when assigning the resulting file as the icon. |
+FilePath CreateChromeDesktopShortcutIconForProfile( |
+ const string16& profile_path, |
+ const gfx::Image* avatar_image) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ if (!avatar_image) |
+ return FilePath(); |
+ const SkBitmap* avatar_bitmap = avatar_image->ToSkBitmap(); |
+ DCHECK(avatar_bitmap); |
+ HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize); |
+ scoped_ptr<SkBitmap> app_icon_bitmap( |
+ IconUtil::CreateSkBitmapFromHICON(app_icon_handle)); |
+ DestroyIcon(app_icon_handle); |
+ if (!app_icon_bitmap.get()) |
+ return FilePath(); |
+ |
+ // TODO(stevet): Share this chunk of code with |
+ // avatar_menu_button::DrawTaskBarDecoration. |
+ const SkBitmap* source_bitmap = NULL; |
+ SkBitmap squarer_bitmap; |
+ if ((avatar_bitmap->width() == profiles::kAvatarIconWidth) && |
+ (avatar_bitmap->height() == profiles::kAvatarIconHeight)) { |
+ // Shave a couple of columns so the bitmap is more square. So when |
+ // resized to a square aspect ratio it looks pretty. |
+ int x = 2; |
+ avatar_bitmap->extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0, |
+ profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight)); |
+ source_bitmap = &squarer_bitmap; |
+ } else { |
+ source_bitmap = avatar_bitmap; |
+ } |
+ SkBitmap sk_icon = skia::ImageOperations::Resize( |
+ *source_bitmap, |
+ skia::ImageOperations::RESIZE_LANCZOS3, |
+ kProfileAvatarShortcutBadgeWidth, |
+ kProfileAvatarShortcutBadgeHeight); |
+ |
+ // Overlay the avatar on the icon, anchoring it to the bottom-right of the |
+ // icon. |
+ scoped_ptr<SkCanvas> offscreen_canvas( |
+ skia::CreateBitmapCanvas(app_icon_bitmap->width(), |
+ app_icon_bitmap->height(), |
+ false)); |
+ DCHECK(offscreen_canvas.get()); |
+ offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0); |
+ offscreen_canvas->drawBitmap( |
+ sk_icon, |
+ app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth, |
+ app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight); |
+ const SkBitmap& final_bitmap = |
+ offscreen_canvas->getDevice()->accessBitmap(false); |
+ |
+ // Finally, write the .ico file containing this new bitmap. |
+ FilePath icon_path(profile_path); |
+ icon_path = icon_path.AppendASCII(kProfileIconFileName); |
+ if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path)) |
+ return FilePath(); |
+ |
+ return icon_path; |
+} |
+ |
+// Creates a desktop shortcut to open Chrome with the given profile name and |
+// base directory. Iff |create|, create shortcut if it doesn't already exist. |
+// Must be called on the FILE thread. This method retains ownership of |
+// |avatar_image| and will delete it when it is done with it. |
+void CreateChromeDesktopShortcutForProfile( |
+ const string16& profile_name, |
+ const string16& profile_base_dir, |
+ const string16& profile_path, |
+ const gfx::Image* avatar_image, |
+ bool create) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ FilePath chrome_exe; |
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe)) |
+ return; |
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
+ string16 description; |
+ if (!dist) |
+ return; |
+ else |
+ description = WideToUTF16(dist->GetAppDescription()); |
+ const string16& directory = CreateProfileShortcutSwitch(profile_base_dir); |
+ FilePath icon_path = |
+ CreateChromeDesktopShortcutIconForProfile(profile_path, avatar_image); |
+ |
+ ShellUtil::CreateChromeDesktopShortcut( |
+ dist, |
+ chrome_exe.value(), |
+ description, |
+ profile_name, |
+ directory, |
+ icon_path.empty() ? chrome_exe.value() : icon_path.value(), |
+ icon_path.empty() ? dist->GetIconIndex() : 0, |
+ ShellUtil::CURRENT_USER, |
+ false, // Use alternate text. |
+ create); // Create if it doesn't already exist. |
+ |
+ // We have ownership of avatar_image, so clean it up here. |
+ if (avatar_image) { |
+ delete avatar_image; |
+ avatar_image = NULL; |
+ } |
+} |
+ |
+// Renames an existing Chrome desktop profile shortcut. Must be called on the |
+// FILE thread. |
+void RenameChromeDesktopShortcutForProfile( |
+ const string16& old_shortcut, |
+ const string16& new_shortcut) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ FilePath shortcut_path; |
+ if (ShellUtil::GetDesktopPath(false, // User's directory instead of system. |
+ &shortcut_path)) { |
+ FilePath old_profile = shortcut_path.Append(old_shortcut); |
+ FilePath new_profile = shortcut_path.Append(new_shortcut); |
+ file_util::Move(old_profile, new_profile); |
+ } |
+} |
+ |
+// Updates the arguments to a Chrome desktop shortcut for a profile. Must be |
+// called on the FILE thread. This method retains ownership of |avatar_image| |
+// and will delete it when it is done with it. |
+void UpdateChromeDesktopShortcutForProfile( |
+ const string16& shortcut, |
+ const string16& arguments, |
+ const string16& profile_path, |
+ const gfx::Image* avatar_image) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ FilePath shortcut_path; |
+ if (!ShellUtil::GetDesktopPath(false, &shortcut_path)) |
+ return; |
+ |
+ shortcut_path = shortcut_path.Append(shortcut); |
+ FilePath chrome_exe; |
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe)) |
+ return; |
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
+ string16 description; |
+ if (!dist) |
+ return; |
+ else |
+ description = WideToUTF16(dist->GetAppDescription()); |
+ FilePath icon_path = |
+ CreateChromeDesktopShortcutIconForProfile(profile_path, avatar_image); |
+ |
+ ShellUtil::UpdateChromeShortcut( |
+ dist, |
+ chrome_exe.value(), |
+ shortcut_path.value(), |
+ arguments, |
+ description, |
+ icon_path.empty() ? chrome_exe.value() : icon_path.value(), |
+ icon_path.empty() ? dist->GetIconIndex() : 0, |
+ false); |
+ |
+ // We have ownership of avatar_image, so clean it up here. |
+ if (avatar_image) { |
+ delete avatar_image; |
+ avatar_image = NULL; |
+ } |
+} |
+ |
} // namespace |
ProfileShortcutManagerWin::ProfileShortcutManagerWin() { |
@@ -54,17 +231,25 @@ |
void ProfileShortcutManagerWin::OnProfileAdded( |
const string16& profile_name, |
- const string16& profile_base_dir) { |
+ const string16& profile_base_dir, |
+ const string16& profile_path, |
+ const gfx::Image* avatar_image) { |
// Launch task to add shortcut to desktop on Windows. If this is the very |
// first profile created, don't add the user name to the shortcut. |
// TODO(mirandac): respect master_preferences choice to create no shortcuts |
// (see http://crbug.com/104463) |
if (g_browser_process->profile_manager()->GetNumberOfProfiles() > 1) { |
- string16 profile_directory = |
- CreateProfileShortcutSwitch(profile_base_dir); |
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
- base::Bind(&CreateChromeDesktopShortcutForProfile, |
- profile_name, profile_directory, true)); |
+ // There is no guarantee that avatar_image won't be deleted before it's |
+ // used, so we'll make a deep copy of it and rely on the user to delete the |
+ // copy. |
+ gfx::Image* avatar_copy = avatar_image ? |
+ new gfx::Image(avatar_image->CopySkBitmap()) : NULL; |
robertshield
2011/12/04 05:02:02
indent +2
SteveT
2011/12/04 17:27:50
Done here and below.
|
+ if (!BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&CreateChromeDesktopShortcutForProfile, |
+ profile_name, profile_base_dir, profile_path, |
+ avatar_copy, true))) { |
+ delete avatar_copy; |
+ } |
robertshield
2011/12/04 05:02:02
since it's never valid to use avatar_copy after th
SteveT
2011/12/04 17:27:50
Done.
|
// If this is the very first multi-user account created, change the |
// original shortcut to launch with the First User profile. |
@@ -72,8 +257,6 @@ |
if (local_state->GetInteger(prefs::kProfilesNumCreated) == 2) { |
string16 default_name = l10n_util::GetStringUTF16( |
IDS_DEFAULT_PROFILE_NAME); |
- string16 default_directory = |
- CreateProfileShortcutSwitch(UTF8ToUTF16(chrome::kInitialProfile)); |
BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
string16 old_shortcut; |
@@ -85,15 +268,18 @@ |
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
base::Bind(&RenameChromeDesktopShortcutForProfile, |
old_shortcut, new_shortcut)); |
+ // TODO(stevet): We actually need to retrieve the newly assigned avatar |
+ // icon for the original profile here and update it with that. |
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
base::Bind(&UpdateChromeDesktopShortcutForProfile, |
- new_shortcut, default_directory)); |
+ new_shortcut, UTF8ToUTF16(chrome::kInitialProfile), |
+ profile_path, static_cast<gfx::Image*>(NULL))); |
} |
} |
- } else { // Only one profile, so create original shortcut. |
+ } else { // Only one profile, so create original shortcut, with no avatar. |
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
base::Bind(&CreateChromeDesktopShortcutForProfile, |
- L"", L"", true)); |
+ L"", L"", L"", static_cast<gfx::Image*>(NULL), true)); |
} |
} |
@@ -131,6 +317,32 @@ |
} |
} |
+void ProfileShortcutManagerWin::OnProfileAvatarChanged( |
+ const string16& profile_name, |
+ const string16& profile_base_dir, |
+ const string16& profile_path, |
+ const gfx::Image* avatar_image) { |
+ // Launch task to change the icon of the desktop shortcut on windows. |
+ string16 new_shortcut; |
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
+ if (ShellUtil::GetChromeShortcutName(dist, false, profile_name, |
+ &new_shortcut)) { |
+ // There is no guarantee that avatar_image won't be deleted before it's |
+ // used, so we'll make a deep copy of it and rely on the user to delete the |
+ // copy. |
+ gfx::Image* avatar_copy = avatar_image ? |
+ new gfx::Image(avatar_image->CopySkBitmap()) : NULL; |
+ if (!BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&UpdateChromeDesktopShortcutForProfile, |
+ new_shortcut, |
+ CreateProfileShortcutSwitch(profile_base_dir), |
+ profile_path, |
+ avatar_copy))) { |
+ delete avatar_copy; |
+ } |
+ } |
+} |
+ |
// static |
std::vector<string16> ProfileShortcutManagerWin::GenerateShortcutsFromProfiles( |
const std::vector<string16>& profile_names) { |
@@ -146,71 +358,3 @@ |
} |
return shortcuts; |
} |
- |
-// static |
-void ProfileShortcutManagerWin::CreateChromeDesktopShortcutForProfile( |
- const string16& profile_name, |
- const string16& directory, |
- bool create) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- FilePath chrome_exe; |
- if (!PathService::Get(base::FILE_EXE, &chrome_exe)) |
- return; |
- BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
- string16 description; |
- if (!dist) |
- return; |
- else |
- description = WideToUTF16(dist->GetAppDescription()); |
- ShellUtil::CreateChromeDesktopShortcut( |
- dist, |
- chrome_exe.value(), |
- description, |
- profile_name, |
- directory, |
- ShellUtil::CURRENT_USER, |
- false, // Use alternate text. |
- create); // Create if it doesn't already exist. |
-} |
- |
-// static |
-void ProfileShortcutManagerWin::RenameChromeDesktopShortcutForProfile( |
- const string16& old_shortcut, |
- const string16& new_shortcut) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- FilePath shortcut_path; |
- if (ShellUtil::GetDesktopPath(false, // User's directory instead of system. |
- &shortcut_path)) { |
- FilePath old_profile = shortcut_path.Append(old_shortcut); |
- FilePath new_profile = shortcut_path.Append(new_shortcut); |
- file_util::Move(old_profile, new_profile); |
- } |
-} |
- |
-// static |
-void ProfileShortcutManagerWin::UpdateChromeDesktopShortcutForProfile( |
- const string16& shortcut, |
- const string16& arguments) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- FilePath shortcut_path; |
- if (!ShellUtil::GetDesktopPath(false, &shortcut_path)) |
- return; |
- |
- shortcut_path = shortcut_path.Append(shortcut); |
- FilePath chrome_exe; |
- if (!PathService::Get(base::FILE_EXE, &chrome_exe)) |
- return; |
- BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
- string16 description; |
- if (!dist) |
- return; |
- else |
- description = WideToUTF16(dist->GetAppDescription()); |
- ShellUtil::UpdateChromeShortcut( |
- dist, |
- chrome_exe.value(), |
- shortcut_path.value(), |
- arguments, |
- description, |
- false); |
-} |