1 // Copyright 2014 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/renderer_context_menu/spelling_options_submenu_observer.h"
6 
7 #include "base/check_op.h"
8 #include "base/command_line.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
13 #include "chrome/browser/spellchecker/spellcheck_service.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "components/prefs/pref_member.h"
17 #include "components/prefs/pref_service.h"
18 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
19 #include "components/spellcheck/browser/pref_names.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/models/menu_separator_types.h"
24 
25 using content::BrowserThread;
26 
SpellingOptionsSubMenuObserver(RenderViewContextMenuProxy * proxy,ui::SimpleMenuModel::Delegate * delegate,int group_id)27 SpellingOptionsSubMenuObserver::SpellingOptionsSubMenuObserver(
28     RenderViewContextMenuProxy* proxy,
29     ui::SimpleMenuModel::Delegate* delegate,
30     int group_id)
31     : proxy_(proxy),
32       submenu_model_(delegate),
33       language_group_id_(group_id),
34       num_selected_dictionaries_(0) {
35   if (proxy_ && proxy_->GetBrowserContext()) {
36     Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
37     use_spelling_service_.Init(spellcheck::prefs::kSpellCheckUseSpellingService,
38                                profile->GetPrefs());
39   }
40   DCHECK(proxy_);
41 }
42 
~SpellingOptionsSubMenuObserver()43 SpellingOptionsSubMenuObserver::~SpellingOptionsSubMenuObserver() {}
44 
InitMenu(const content::ContextMenuParams & params)45 void SpellingOptionsSubMenuObserver::InitMenu(
46     const content::ContextMenuParams& params) {
47   DCHECK_CURRENTLY_ON(BrowserThread::UI);
48 
49   // Add available spell-checker languages to the sub menu.
50   content::BrowserContext* browser_context = proxy_->GetBrowserContext();
51   DCHECK(browser_context);
52   SpellcheckService::GetDictionaries(browser_context, &dictionaries_);
53   DCHECK(dictionaries_.size() <
54          IDC_SPELLCHECK_LANGUAGES_LAST - IDC_SPELLCHECK_LANGUAGES_FIRST);
55   const std::string app_locale = g_browser_process->GetApplicationLocale();
56 
57   if (dictionaries_.size() > 1) {
58     submenu_model_.AddRadioItemWithStringId(
59         IDC_SPELLCHECK_MULTI_LINGUAL,
60         IDS_CONTENT_CONTEXT_SPELLCHECK_MULTI_LINGUAL, language_group_id_);
61   }
62 
63   const size_t kMaxLanguages = static_cast<size_t>(
64       IDC_SPELLCHECK_LANGUAGES_FIRST - IDC_SPELLCHECK_LANGUAGES_LAST);
65   for (size_t i = 0; i < dictionaries_.size() && i < kMaxLanguages; ++i) {
66     submenu_model_.AddRadioItem(
67         IDC_SPELLCHECK_LANGUAGES_FIRST + i,
68         l10n_util::GetDisplayNameForLocale(dictionaries_[i].language,
69                                            app_locale, true),
70         language_group_id_);
71     if (dictionaries_[i].used_for_spellcheck)
72       ++num_selected_dictionaries_;
73   }
74 
75   // Add an item that opens the 'Settings - Languages' page. This item is
76   // handled in RenderViewContextMenu.
77   submenu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS,
78                                      IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS);
79   submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
80 
81   if (dictionaries_.size() > 0) {
82     // Add a 'Use basic spell check' item in the sub menu.
83     submenu_model_.AddCheckItem(
84         IDC_CHECK_SPELLING_WHILE_TYPING,
85         l10n_util::GetStringUTF16(
86             IDS_CONTENT_CONTEXT_CHECK_SPELLING_WHILE_TYPING));
87 
88     // Add a check item 'Use enhanced spell check'. This item is handled in
89     // SpellingMenuObserver.
90     Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
91     RenderViewContextMenu::AddSpellCheckServiceItem(
92         &submenu_model_,
93         profile->GetPrefs()->GetBoolean(spellcheck::prefs::kSpellCheckEnable) &&
94             use_spelling_service_.GetValue());
95   }
96 
97   proxy_->AddSubMenu(
98       IDC_SPELLCHECK_MENU,
99       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLCHECK_MENU),
100       &submenu_model_);
101 }
102 
IsCommandIdSupported(int command_id)103 bool SpellingOptionsSubMenuObserver::IsCommandIdSupported(int command_id) {
104   // Allow Spell Check language items on sub menu for text area context menu.
105   if (command_id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
106       command_id < IDC_SPELLCHECK_LANGUAGES_LAST) {
107     DCHECK_GT(IDC_SPELLCHECK_LANGUAGES_FIRST + dictionaries_.size(),
108               static_cast<size_t>(command_id));
109     return true;
110   }
111 
112   switch (command_id) {
113     case IDC_CHECK_SPELLING_WHILE_TYPING:
114     case IDC_SPELLCHECK_MENU:
115     case IDC_SPELLCHECK_MULTI_LINGUAL:
116       return true;
117   }
118 
119   return false;
120 }
121 
IsCommandIdChecked(int command_id)122 bool SpellingOptionsSubMenuObserver::IsCommandIdChecked(int command_id) {
123   DCHECK(IsCommandIdSupported(command_id));
124 
125   if (command_id == IDC_SPELLCHECK_MULTI_LINGUAL)
126     return num_selected_dictionaries_ == dictionaries_.size();
127 
128   if (command_id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
129       command_id < IDC_SPELLCHECK_LANGUAGES_LAST) {
130     if (num_selected_dictionaries_ == dictionaries_.size())
131       return dictionaries_.size() == 1;
132 
133     size_t dictionary_index =
134         static_cast<size_t>(command_id - IDC_SPELLCHECK_LANGUAGES_FIRST);
135     return dictionaries_[dictionary_index].used_for_spellcheck;
136   }
137 
138   // Check box for 'Use basic spell check'.
139   if (command_id == IDC_CHECK_SPELLING_WHILE_TYPING) {
140     Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
141     return profile->GetPrefs()->GetBoolean(
142                spellcheck::prefs::kSpellCheckEnable) &&
143            !profile->GetPrefs()->GetBoolean(
144                spellcheck::prefs::kSpellCheckUseSpellingService);
145   }
146 
147   return false;
148 }
149 
IsCommandIdEnabled(int command_id)150 bool SpellingOptionsSubMenuObserver::IsCommandIdEnabled(int command_id) {
151   DCHECK(IsCommandIdSupported(command_id));
152 
153   Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
154   DCHECK(profile);
155   const PrefService* pref = profile->GetPrefs();
156   if ((command_id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
157        command_id < IDC_SPELLCHECK_LANGUAGES_LAST) ||
158       command_id == IDC_SPELLCHECK_MULTI_LINGUAL) {
159     return pref->GetBoolean(spellcheck::prefs::kSpellCheckEnable);
160   }
161 
162   switch (command_id) {
163     case IDC_CHECK_SPELLING_WHILE_TYPING:
164       return !pref->FindPreference(spellcheck::prefs::kSpellCheckEnable)
165                   ->IsManaged();
166 
167     case IDC_SPELLCHECK_MENU:
168       return true;
169   }
170 
171   return false;
172 }
173 
ExecuteCommand(int command_id)174 void SpellingOptionsSubMenuObserver::ExecuteCommand(int command_id) {
175   DCHECK(IsCommandIdSupported(command_id));
176 
177   // Check to see if one of the spell check language ids have been clicked.
178   Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
179   DCHECK(profile);
180 
181   if (command_id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
182       static_cast<size_t>(command_id) <
183           IDC_SPELLCHECK_LANGUAGES_FIRST + dictionaries_.size()) {
184     size_t dictionary_index =
185         static_cast<size_t>(command_id - IDC_SPELLCHECK_LANGUAGES_FIRST);
186     StringListPrefMember dictionaries_pref;
187     dictionaries_pref.Init(spellcheck::prefs::kSpellCheckDictionaries,
188                            profile->GetPrefs());
189     dictionaries_pref.SetValue({dictionaries_[dictionary_index].language});
190     return;
191   }
192 
193   switch (command_id) {
194     case IDC_CHECK_SPELLING_WHILE_TYPING: {
195       bool spellCheckEnabled =
196           profile->GetPrefs()->GetBoolean(spellcheck::prefs::kSpellCheckEnable);
197       bool enhancedSpellCheckEnabled = profile->GetPrefs()->GetBoolean(
198           spellcheck::prefs::kSpellCheckUseSpellingService);
199 
200       if (spellCheckEnabled && !enhancedSpellCheckEnabled) {
201         // User is turning off spell check
202         profile->GetPrefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
203                                         false);
204       } else if (enhancedSpellCheckEnabled) {
205         // Use is choosing 'basic' over 'enhanced'
206         profile->GetPrefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
207                                         true);
208         profile->GetPrefs()->SetBoolean(
209             spellcheck::prefs::kSpellCheckUseSpellingService, false);
210       } else {
211         // User is turning on spell check
212         profile->GetPrefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
213                                         true);
214       }
215       break;
216     }
217 
218     case IDC_SPELLCHECK_MULTI_LINGUAL: {
219       StringListPrefMember dictionaries_pref;
220       dictionaries_pref.Init(spellcheck::prefs::kSpellCheckDictionaries,
221                              profile->GetPrefs());
222       std::vector<std::string> all_languages;
223       for (const auto& dictionary : dictionaries_)
224         all_languages.push_back(dictionary.language);
225       dictionaries_pref.SetValue(all_languages);
226       break;
227     }
228   }
229 }
230