1 /*
2  * PythonInterpreterSelectionDialog.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 package org.rstudio.studio.client.workbench.prefs.views.python;
16 
17 import java.util.HashMap;
18 import java.util.Map;
19 
20 import org.rstudio.core.client.StringUtil;
21 import org.rstudio.core.client.js.JsUtil;
22 import org.rstudio.core.client.theme.DialogTabLayoutPanel;
23 import org.rstudio.core.client.theme.VerticalTabPanel;
24 import org.rstudio.core.client.widget.ModalDialog;
25 import org.rstudio.core.client.widget.OperationWithInput;
26 import org.rstudio.core.client.widget.ThemedButton;
27 import org.rstudio.core.client.widget.WidgetListBox;
28 import org.rstudio.studio.client.workbench.prefs.views.PythonInterpreter;
29 
30 import com.google.gwt.aria.client.Roles;
31 import com.google.gwt.core.client.JsArray;
32 import com.google.gwt.event.dom.client.ChangeEvent;
33 import com.google.gwt.event.dom.client.DoubleClickEvent;
34 import com.google.gwt.user.client.ui.Widget;
35 
36 public class PythonInterpreterSelectionDialog extends ModalDialog<PythonInterpreter>
37 {
38    private static final String LABEL_SYSTEM  = "System";
39    private static final String LABEL_VIRTUAL = "Virtual Environments";
40    private static final String LABEL_CONDA   = "Conda Environments";
41 
PythonInterpreterSelectionDialog(final JsArray<PythonInterpreter> interpreters, final OperationWithInput<PythonInterpreter> operation)42    public PythonInterpreterSelectionDialog(final JsArray<PythonInterpreter> interpreters,
43                                            final OperationWithInput<PythonInterpreter> operation)
44    {
45       super("Python Interpreters", Roles.getDialogRole(), operation);
46       setOkButtonCaption("Select");
47 
48       // initialize widget list boxes
49       widgets_ = new HashMap<>();
50       for (String label : new String[] { LABEL_SYSTEM, LABEL_VIRTUAL, LABEL_CONDA })
51       {
52          WidgetListBox<PythonInterpreterListEntryUi> listBox = new WidgetListBox<>();
53          listBox.setSize("598px", "468px");
54          listBox.setAriaLabel(label);
55 
56          listBox.setEmptyText("(None available)");
57 
58          // allow double-click to select the requested interpreter
59          listBox.addDoubleClickHandler((DoubleClickEvent event) ->
60          {
61             if (listBox.getSelectedItem() != null)
62             {
63                selectedItem_ = listBox.getSelectedItem();
64                clickOkButton();
65             }
66          });
67 
68          listBox.addChangeHandler((ChangeEvent event) ->
69          {
70             selectedItem_ = listBox.getSelectedItem();
71          });
72 
73          widgets_.put(label, listBox);
74       }
75 
76       // add interpreters to their appropriate buckets
77       for (PythonInterpreter interpreter : JsUtil.asIterable(interpreters))
78       {
79          if (interpreter == null || !interpreter.isValid())
80             continue;
81 
82          String type = interpreter.getType();
83          if (type == null)
84             continue;
85 
86          String version = interpreter.getVersion();
87          if (version == null || version.startsWith("2"))
88             continue;
89 
90          if (StringUtil.equals(type, "system"))
91          {
92             widgets_.get(LABEL_SYSTEM).addItem(new PythonInterpreterListEntryUi(interpreter));
93          }
94          else if (StringUtil.equals(type, "virtualenv"))
95          {
96             widgets_.get(LABEL_VIRTUAL).addItem(new PythonInterpreterListEntryUi(interpreter));
97          }
98          else if (StringUtil.equals(type, "conda"))
99          {
100             widgets_.get(LABEL_CONDA).addItem(new PythonInterpreterListEntryUi(interpreter));
101          }
102       }
103 
104       // initialize tab panel
105       tabPanel_ = new DialogTabLayoutPanel("General");
106       tabPanel_.setSize("620px", "520px");
107       for (Map.Entry<String, WidgetListBox<PythonInterpreterListEntryUi>> entry : widgets_.entrySet())
108       {
109          VerticalTabPanel panel = new VerticalTabPanel(entry.getKey());
110          panel.add(entry.getValue());
111          tabPanel_.add(panel, entry.getKey(), panel.getBasePanelId());
112       }
113 
114       tabPanel_.selectTab(0);
115    }
116 
117    @Override
collectInput()118    protected PythonInterpreter collectInput()
119    {
120       if (selectedItem_ == null)
121          return null;
122 
123       return selectedItem_.getInterpreter();
124    }
125 
126    @Override
createMainWidget()127    protected Widget createMainWidget()
128    {
129       return tabPanel_;
130    }
131 
132    final DialogTabLayoutPanel tabPanel_;
133    final Map<String, WidgetListBox<PythonInterpreterListEntryUi>> widgets_;
134 
135    PythonInterpreterListEntryUi selectedItem_;
136    ThemedButton useDefaultBtn_;
137 }
138