1 /*
2  * SpellingPreferencesPane.java
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 package org.rstudio.studio.client.workbench.prefs.views;
17 
18 import com.google.gwt.aria.client.Roles;
19 import com.google.gwt.resources.client.ImageResource;
20 import com.google.gwt.user.client.ui.CheckBox;
21 import com.google.gwt.user.client.ui.HasVerticalAlignment;
22 import com.google.gwt.user.client.ui.HorizontalPanel;
23 import com.google.gwt.user.client.ui.Label;
24 import com.google.inject.Inject;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.function.Consumer;
31 import org.rstudio.core.client.Size;
32 import org.rstudio.core.client.StringUtil;
33 import org.rstudio.core.client.prefs.PreferencesDialogBaseResources;
34 import org.rstudio.core.client.prefs.RestartRequirement;
35 import org.rstudio.core.client.resources.ImageResource2x;
36 import org.rstudio.core.client.widget.SmallButton;
37 import org.rstudio.studio.client.common.spelling.SpellingService;
38 import org.rstudio.studio.client.common.spelling.ui.SpellingCustomDictionariesWidget;
39 import org.rstudio.studio.client.common.spelling.ui.SpellingLanguageSelectWidget;
40 import org.rstudio.studio.client.workbench.WorkbenchList;
41 import org.rstudio.studio.client.workbench.WorkbenchListManager;
42 import org.rstudio.studio.client.workbench.prefs.model.SpellingPrefsContext;
43 import org.rstudio.studio.client.workbench.prefs.model.UserPrefs;
44 import org.rstudio.studio.client.workbench.views.edit.ui.EditDialog;
45 
46 public class SpellingPreferencesPane extends PreferencesPane
47 {
48    @Inject
SpellingPreferencesPane(PreferencesDialogResources res, SpellingService spellingService, WorkbenchListManager workbenchListManager, UserPrefs prefs)49    public SpellingPreferencesPane(PreferencesDialogResources res,
50                                   SpellingService spellingService,
51                                   WorkbenchListManager workbenchListManager,
52                                   UserPrefs prefs)
53    {
54       res_ = res;
55       uiPrefs_ = prefs;
56 
57       add(headerLabel("Dictionaries"));
58 
59       languageWidget_ = new SpellingLanguageSelectWidget(spellingService);
60       spaced(languageWidget_);
61       add(languageWidget_);
62 
63       customDictsWidget_ =  new SpellingCustomDictionariesWidget();
64       mediumSpaced(customDictsWidget_);
65       nudgeRight(customDictsWidget_);
66       add(customDictsWidget_);
67 
68       addUserDictionariesEditor(workbenchListManager);
69 
70       add(headerLabel("Ignore"));
71 
72       add(checkboxPref("Ignore words in UPPERCASE", prefs.ignoreUppercaseWords()));
73       add(mediumSpaced(checkboxPref("Ignore words with numbers", prefs.ignoreWordsWithNumbers(), false)));
74 
75 
76       add(headerLabel("Checking"));
77 
78       realtimeSpellcheckingCheckbox_ = checkboxPref("Use real time spell-checking", prefs.realTimeSpellchecking(), false);
79       spaced(realtimeSpellcheckingCheckbox_);
80       add(realtimeSpellcheckingCheckbox_);
81 
82       languageWidget_.addChangeHandler((event) ->
83       {
84          realtimeSpellcheckingCheckbox_.setValue(realtimeSpellcheckingCheckbox_.getValue());
85       });
86    }
87 
addUserDictionariesEditor(WorkbenchListManager workbenchListManager)88    private void addUserDictionariesEditor(WorkbenchListManager workbenchListManager)
89    {
90       final String kUserDictionary = "User dictionary: ";
91       final Label userDictLabel = new Label(kUserDictionary);
92       final Consumer<Integer> setUserDictLabel = (Integer entries) -> {
93          userDictLabel.setText(kUserDictionary + StringUtil.formatGeneralNumber(entries) + " words");
94       };
95 
96       final ArrayList<String> userDictWords = new ArrayList<>();
97       WorkbenchList userDict = workbenchListManager.getUserDictionaryList();
98       userDict.addListChangedHandler((e) -> {
99          userDictWords.clear();
100          userDictWords.addAll(e.getList());
101          Collections.sort(userDictWords, String.CASE_INSENSITIVE_ORDER);
102          setUserDictLabel.accept(userDictWords.size());
103       });
104 
105       HorizontalPanel userDictPanel = new HorizontalPanel();
106       userDictPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
107       userDictPanel.add(userDictLabel);
108       SmallButton editUserDict = new SmallButton("Edit User Dictionary...");
109       editUserDict.addStyleName(res_.styles().userDictEditButton());
110       editUserDict.addClickHandler((e) -> {
111          EditDialog editDialog = new EditDialog(
112             "Edit User Dictionary",
113             "Save",
114             String.join("\n", userDictWords),
115             Roles.getDialogRole(),
116             false,
117             true,
118             new Size(400, 425),
119             (dictionary, progress) -> {
120                if (dictionary != null)
121                {
122                   List<String> dictSplitItems = Arrays.asList(dictionary.split("\n"));
123                   ArrayList<String> dictWords = new ArrayList<>();
124                   for (String item : dictSplitItems)
125                   {
126                      item = item.trim();
127                      if (!item.isEmpty())
128                         dictWords.add(item);
129                   }
130                   userDict.setContents(dictWords);
131                   setUserDictLabel.accept(dictWords.size());
132                }
133                progress.onCompleted();
134             }
135          );
136          editDialog.showModal();
137       });
138       userDictPanel.add(editUserDict);
139 
140       mediumSpaced(userDictPanel);
141       add(userDictPanel);
142    }
143 
144 
145    @Override
initialize(UserPrefs rPrefs)146    protected void initialize(UserPrefs rPrefs)
147    {
148       SpellingPrefsContext context = uiPrefs_.spellingPrefsContext().getValue();
149       languageWidget_.setProgressIndicator(getProgressIndicator());
150       languageWidget_.setLanguages(context.getAllLanguagesInstalled(),
151                                    context.getAvailableLanguages());
152 
153       languageWidget_.setSelectedLanguage(
154                         uiPrefs_.spellingDictionaryLanguage().getValue());
155 
156       customDictsWidget_.setDictionaries(context.getCustomDictionaries());
157       customDictsWidget_.setProgressIndicator(getProgressIndicator());
158    }
159 
160    @Override
onApply(UserPrefs rPrefs)161    public RestartRequirement onApply(UserPrefs rPrefs)
162    {
163       RestartRequirement restart = super.onApply(rPrefs);
164 
165       uiPrefs_.spellingDictionaryLanguage().setGlobalValue(
166             languageWidget_.getSelectedLanguage());
167 
168       restart.setDesktopRestartRequired(
169             restart.getDesktopRestartRequired() ||
170             customDictsWidget_.getCustomDictsModified() ||
171             !StringUtil.equals(
172                   rPrefs.spellingDictionaryLanguage().getValue(),
173                   languageWidget_.getSelectedLanguage()));
174 
175       return restart;
176    }
177 
178    @Override
getIcon()179    public ImageResource getIcon()
180    {
181       return new ImageResource2x(PreferencesDialogBaseResources.INSTANCE.iconSpelling2x());
182    }
183 
184    @Override
validate()185    public boolean validate()
186    {
187       return true;
188    }
189 
190    @Override
getName()191    public String getName()
192    {
193       return "Spelling";
194    }
195 
196 
197    private final PreferencesDialogResources res_;
198 
199    private final UserPrefs uiPrefs_;
200    private final SpellingLanguageSelectWidget languageWidget_;
201    private final SpellingCustomDictionariesWidget customDictsWidget_;
202    private final CheckBox realtimeSpellcheckingCheckbox_;
203 }
204