Add project watch support and cleanup SSH key management UI

We now import the "watched projects" from Gerrit 1, the list of projects
the user wants to know if there are new changes in.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2008-12-29 15:41:37 -08:00
parent e77e6fc878
commit 55d5e7c701
24 changed files with 833 additions and 23 deletions

View File

@@ -28,6 +28,8 @@
class='com.google.gerrit.server.ChangeListServiceSrv'/>
<servlet path='/rpc/PatchDetailService'
class='com.google.gerrit.server.PatchDetailServiceSrv'/>
<servlet path='/rpc/SuggestService'
class='com.google.gerrit.server.SuggestServiceSrv'/>
<servlet path='/rpc/SystemInfoService'
class='com.google.gerrit.server.SystemInfoServiceSrv'/>
</module>

View File

@@ -20,7 +20,19 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwtjsonrpc.client.RpcStatusListener;
class RpcStatus implements RpcStatusListener {
public class RpcStatus implements RpcStatusListener {
private static int hideDepth;
/** Execute code, hiding the RPCs they execute from being shown visually. */
public static void hide(final Runnable run) {
try {
hideDepth++;
run.run();
} finally {
hideDepth--;
}
}
private final Label loading;
private int activeCalls;
@@ -39,7 +51,9 @@ class RpcStatus implements RpcStatusListener {
public void onCallStart() {
if (++activeCalls == 1) {
loading.setVisible(true);
if (hideDepth == 0) {
loading.setVisible(true);
}
}
}

View File

@@ -38,4 +38,9 @@ public interface AccountConstants extends Constants {
String sshKeyStored();
String addSshKeyPanelHeader();
String addSshKeyHelp();
String watchedProjects();
String buttonWatchProject();
String defaultProjectName();
}

View File

@@ -18,4 +18,9 @@ sshKeyComment = Comment
sshKeyLastUsed = Last Used
sshKeyStored = Stored
addSshKeyPanelHeader = Add SSH Key
addSshKeyPanelHeader = Add SSH Public Key
addSshKeyHelp = (<a href="http://github.com/guides/providing-your-ssh-key" target="_blank">GitHub's Guide to SSH Keys</a>)
watchedProjects = Watched Projects
buttonWatchProject = Watch
defaultProjectName = Project Name

View File

@@ -0,0 +1,39 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.reviewdb.Project;
public final class AccountProjectWatchInfo {
protected AccountProjectWatch watch;
protected Project project;
protected AccountProjectWatchInfo() {
}
public AccountProjectWatchInfo(final AccountProjectWatch w, final Project p) {
watch = w;
project = p;
}
public AccountProjectWatch getWatch() {
return watch;
}
public Project getProject() {
return project;
}
}

View File

@@ -15,15 +15,30 @@
package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.rpc.SignInRequired;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.VoidResult;
import java.util.List;
import java.util.Set;
public interface AccountService extends RemoteJsonService {
@SignInRequired
void myAccount(AsyncCallback<Account> callback);
@SignInRequired
void changeDefaultContext(short newSetting, AsyncCallback<VoidResult> callback);
@SignInRequired
void myProjectWatch(AsyncCallback<List<AccountProjectWatchInfo>> callback);
@SignInRequired
void addProjectWatch(String projectName,
AsyncCallback<AccountProjectWatchInfo> callback);
@SignInRequired
void deleteProjectWatches(Set<AccountProjectWatch.Key> keys,
AsyncCallback<VoidResult> callback);
}

View File

@@ -15,15 +15,23 @@
package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.BaseServiceImplementation;
import com.google.gerrit.client.rpc.NoSuchEntityException;
import com.google.gerrit.client.rpc.RpcUtil;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.client.Transaction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
public class AccountServiceImpl extends BaseServiceImplementation implements
AccountService {
@@ -50,4 +58,74 @@ public class AccountServiceImpl extends BaseServiceImplementation implements
}
});
}
public void myProjectWatch(
final AsyncCallback<List<AccountProjectWatchInfo>> callback) {
run(callback, new Action<List<AccountProjectWatchInfo>>() {
public List<AccountProjectWatchInfo> run(ReviewDb db) throws OrmException {
final List<AccountProjectWatchInfo> r =
new ArrayList<AccountProjectWatchInfo>();
for (final AccountProjectWatch w : db.accountProjectWatches()
.byAccount(RpcUtil.getAccountId()).toList()) {
final Project p = db.projects().get(w.getProjectId());
if (p == null) {
db.accountProjectWatches().delete(Collections.singleton(w));
continue;
}
r.add(new AccountProjectWatchInfo(w, p));
}
Collections.sort(r, new Comparator<AccountProjectWatchInfo>() {
public int compare(final AccountProjectWatchInfo a,
final AccountProjectWatchInfo b) {
return a.getProject().getName().compareTo(b.getProject().getName());
}
});
return r;
}
});
}
public void addProjectWatch(final String projectName,
final AsyncCallback<AccountProjectWatchInfo> callback) {
run(callback, new Action<AccountProjectWatchInfo>() {
public AccountProjectWatchInfo run(ReviewDb db) throws OrmException,
Failure {
final Project project =
db.projects().get(new Project.NameKey(projectName));
if (project == null) {
throw new Failure(new NoSuchEntityException());
}
final AccountProjectWatch watch =
new AccountProjectWatch(new AccountProjectWatch.Key(RpcUtil
.getAccountId(), project.getId()));
db.accountProjectWatches().insert(Collections.singleton(watch));
return new AccountProjectWatchInfo(watch, project);
}
});
}
public void deleteProjectWatches(final Set<AccountProjectWatch.Key> keys,
final AsyncCallback<VoidResult> callback) {
run(callback, new Action<VoidResult>() {
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
final Account.Id me = RpcUtil.getAccountId();
for (final AccountProjectWatch.Key keyId : keys) {
if (!me.equals(keyId.getParentKey()))
throw new Failure(new NoSuchEntityException());
}
final List<AccountProjectWatch> k =
db.accountProjectWatches().get(keys).toList();
if (!k.isEmpty()) {
final Transaction txn = db.beginTransaction();
db.accountProjectWatches().delete(k, txn);
txn.commit();
}
return VoidResult.INSTANCE;
}
});
}
}

View File

@@ -20,6 +20,7 @@ import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.AccountScreen;
import com.google.gerrit.client.ui.LazyTabChild;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
@@ -36,7 +37,6 @@ public class AccountSettings extends AccountScreen {
private PreferencePanel prefsPanel;
private Panel agreementsPanel;
private SshKeyPanel keysPanel;
public AccountSettings() {
super(Util.C.accountSettingsHeading());
@@ -64,15 +64,18 @@ public class AccountSettings extends AccountScreen {
fmt.addStyleName(2, 0, "bottomheader");
prefsPanel = new PreferencePanel();
keysPanel = new SshKeyPanel();
agreementsPanel = new FlowPanel();
agreementsPanel.add(new Label("Not Implemented"));
tabs = new TabPanel();
tabs.setWidth("100%");
tabs.setWidth("98%");
tabs.add(prefsPanel, Util.C.tabPreferences());
tabs.add(keysPanel, Util.C.tabSshKeys());
tabs.add(new LazyTabChild<SshKeyPanel>() {
@Override
protected SshKeyPanel create() {
return new SshKeyPanel();
}
}, Util.C.tabSshKeys());
tabs.add(agreementsPanel, Util.C.tabAgreements());
add(tabs);

View File

@@ -22,15 +22,17 @@ import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtjsonrpc.client.VoidResult;
public class PreferencePanel extends Composite {
class PreferencePanel extends Composite {
private ListBox defaultContext;
private short oldDefaultContext;
private ProjectWatchPanel watchPanel;
public PreferencePanel() {
PreferencePanel() {
final FlowPanel body = new FlowPanel();
defaultContext = new ListBox();
@@ -80,6 +82,19 @@ public class PreferencePanel extends Composite {
body.add(formGrid);
{
final FlowPanel fp = new FlowPanel();
fp.setStyleName("gerrit-WatchedProjectPanel");
final Label hdr = new Label(Util.C.watchedProjects());
hdr.setStyleName("gerrit-SmallHeading");
fp.add(hdr);
watchPanel = new ProjectWatchPanel();
fp.add(watchPanel);
body.add(fp);
}
initWidget(body);
}

View File

@@ -0,0 +1,264 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusListenerAdapter;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwtjsonrpc.client.VoidResult;
import java.util.HashSet;
import java.util.List;
class ProjectWatchPanel extends Composite {
private WatchTable watches;
private Button addNew;
private SuggestBox nameTxt;
private Button delSel;
ProjectWatchPanel() {
final FlowPanel body = new FlowPanel();
{
final FlowPanel fp = new FlowPanel();
fp.setStyleName("gerrit-ProjectWatchPanel-AddPanel");
final TextBox box = new TextBox();
nameTxt = new SuggestBox(new ProjectNameSuggestOracle(), box);
box.setVisibleLength(50);
box.setText(Util.C.defaultProjectName());
box.addStyleName("gerrit-InputFieldTypeHint");
box.addFocusListener(new FocusListenerAdapter() {
@Override
public void onFocus(Widget sender) {
if (Util.C.defaultProjectName().equals(box.getText())) {
box.setText("");
box.removeStyleName("gerrit-InputFieldTypeHint");
}
}
@Override
public void onLostFocus(Widget sender) {
if ("".equals(box.getText())) {
box.setText(Util.C.defaultProjectName());
box.addStyleName("gerrit-InputFieldTypeHint");
}
}
});
fp.add(nameTxt);
addNew = new Button(Util.C.buttonWatchProject());
addNew.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
doAddNew();
}
});
fp.add(addNew);
body.add(fp);
}
watches = new WatchTable();
body.add(watches);
{
final FlowPanel fp = new FlowPanel();
delSel = new Button(Util.C.buttonDeleteSshKey());
delSel.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
watches.deleteChecked();
}
});
fp.add(delSel);
body.add(fp);
}
initWidget(body);
}
void doAddNew() {
final String projectName = nameTxt.getText();
if (projectName == null || projectName.length() == 0) {
return;
}
if (watches.moveToExistingProject(projectName)) {
nameTxt.setText("");
return;
}
addNew.setEnabled(false);
Util.ACCOUNT_SVC.addProjectWatch(projectName,
new GerritCallback<AccountProjectWatchInfo>() {
public void onSuccess(final AccountProjectWatchInfo result) {
addNew.setEnabled(true);
nameTxt.setText("");
watches.insertWatch(result);
}
@Override
public void onFailure(final Throwable caught) {
addNew.setEnabled(true);
super.onFailure(caught);
}
});
}
@Override
public void onLoad() {
super.onLoad();
Util.ACCOUNT_SVC
.myProjectWatch(new GerritCallback<List<AccountProjectWatchInfo>>() {
public void onSuccess(final List<AccountProjectWatchInfo> result) {
watches.display(result);
watches.finishDisplay(true);
}
});
}
private class WatchTable extends FancyFlexTable<AccountProjectWatchInfo> {
WatchTable() {
table.setText(0, 2, com.google.gerrit.client.changes.Util.C
.changeTableColumnProject());
table.addTableListener(new TableListener() {
public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
if (cell != 1 && getRowItem(row) != null) {
movePointerTo(row);
}
}
});
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(0, 1, S_ICON_HEADER);
fmt.addStyleName(0, 2, S_DATA_HEADER);
}
@Override
protected Object getRowItemKey(final AccountProjectWatchInfo item) {
return item.getWatch().getKey();
}
@Override
protected boolean onKeyPress(final char keyCode, final int modifiers) {
if (super.onKeyPress(keyCode, modifiers)) {
return true;
}
if (modifiers == 0) {
switch (keyCode) {
case 's':
case 'c':
toggleCurrentRow();
return true;
}
}
return false;
}
@Override
protected void onOpenItem(final AccountProjectWatchInfo item) {
toggleCurrentRow();
}
private void toggleCurrentRow() {
final CheckBox cb = (CheckBox) table.getWidget(getCurrentRow(), 1);
cb.setChecked(!cb.isChecked());
}
void deleteChecked() {
final HashSet<AccountProjectWatch.Key> ids =
new HashSet<AccountProjectWatch.Key>();
for (int row = 1; row < table.getRowCount(); row++) {
final AccountProjectWatchInfo k = getRowItem(row);
if (k != null && ((CheckBox) table.getWidget(row, 1)).isChecked()) {
ids.add(k.getWatch().getKey());
}
}
if (!ids.isEmpty()) {
Util.ACCOUNT_SVC.deleteProjectWatches(ids,
new GerritCallback<VoidResult>() {
public void onSuccess(final VoidResult result) {
for (int row = 1; row < table.getRowCount();) {
final AccountProjectWatchInfo k = getRowItem(row);
if (k != null && ids.contains(k.getWatch().getKey())) {
table.removeRow(row);
} else {
row++;
}
}
}
});
}
}
boolean moveToExistingProject(final String projectName) {
for (int row = 1; row < table.getRowCount(); row++) {
final AccountProjectWatchInfo i = getRowItem(row);
if (i != null && i.getProject().getName().equals(projectName)) {
movePointerTo(row);
return true;
}
}
return false;
}
void insertWatch(final AccountProjectWatchInfo k) {
final String newName = k.getProject().getName();
int row = 1;
for (; row < table.getRowCount(); row++) {
final AccountProjectWatchInfo i = getRowItem(row);
if (i != null && i.getProject().getName().compareTo(newName) >= 0) {
break;
}
}
table.insertRow(row);
populate(row, k);
}
void display(final List<AccountProjectWatchInfo> result) {
while (1 < table.getRowCount())
table.removeRow(table.getRowCount() - 1);
for (final AccountProjectWatchInfo k : result) {
final int row = table.getRowCount();
table.insertRow(row);
populate(row, k);
}
}
void populate(final int row, final AccountProjectWatchInfo k) {
table.setWidget(row, 1, new CheckBox());
table.setText(row, 2, k.getProject().getName());
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(row, 1, S_ICON_CELL);
fmt.addStyleName(row, 2, S_DATA_CELL);
setRowItem(row, k);
}
}
}

View File

@@ -24,6 +24,7 @@ import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
@@ -36,14 +37,14 @@ import com.google.gwtjsonrpc.client.VoidResult;
import java.util.HashSet;
import java.util.List;
public class SshKeyPanel extends Composite {
class SshKeyPanel extends Composite {
private SshKeyTable keys;
private Button addNew;
private TextArea addTxt;
private Button delSel;
public SshKeyPanel() {
SshKeyPanel() {
final FlowPanel body = new FlowPanel();
keys = new SshKeyTable();
@@ -63,7 +64,10 @@ public class SshKeyPanel extends Composite {
{
final VerticalPanel fp = new VerticalPanel();
fp.setStyleName("gerrit-AddSshKeyPanel");
fp.add(new Label(Util.C.addSshKeyPanelHeader()));
final Label hdr = new Label(Util.C.addSshKeyPanelHeader());
hdr.setStyleName("gerrit-SmallHeading");
fp.add(hdr);
fp.add(new HTML(Util.C.addSshKeyHelp()));
addTxt = new TextArea();
addTxt.setVisibleLines(12);
@@ -104,14 +108,8 @@ public class SshKeyPanel extends Composite {
}
@Override
public void setVisible(final boolean visible) {
if (!isVisible()) {
update();
}
super.setVisible(visible);
}
public void update() {
public void onLoad() {
super.onLoad();
Util.ACCOUNT_SEC.mySshKeys(new GerritCallback<List<AccountSshKey>>() {
public void onSuccess(final List<AccountSshKey> result) {
keys.display(result);
@@ -230,6 +228,8 @@ public class SshKeyPanel extends Composite {
for (int c = 3; c <= 7; c++) {
fmt.addStyleName(row, c, S_DATA_CELL);
}
fmt.addStyleName(row, 6, "C_LAST_UPDATE");
fmt.addStyleName(row, 7, "C_LAST_UPDATE");
setRowItem(row, k);
}

View File

@@ -0,0 +1,71 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.reviewdb;
import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.CompoundKey;
/** An {@link Account} interested in a {@link Project}. */
public final class AccountProjectWatch {
public static class Key extends CompoundKey<Account.Id> {
@Column
protected Account.Id accountId;
@Column
protected Project.Id projectId;
protected Key() {
accountId = new Account.Id();
projectId = new Project.Id();
}
public Key(final Account.Id a, final Project.Id g) {
accountId = a;
projectId = g;
}
@Override
public Account.Id getParentKey() {
return accountId;
}
@Override
public com.google.gwtorm.client.Key<?>[] members() {
return new com.google.gwtorm.client.Key<?>[] {projectId};
}
}
@Column(name = Column.NONE)
protected Key key;
protected AccountProjectWatch() {
}
public AccountProjectWatch(final AccountProjectWatch.Key k) {
key = k;
}
public AccountProjectWatch.Key getKey() {
return key;
}
public Account.Id getAccountId() {
return key.accountId;
}
public Project.Id getProjectId() {
return key.projectId;
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.reviewdb;
import com.google.gwtorm.client.Access;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.PrimaryKey;
import com.google.gwtorm.client.Query;
import com.google.gwtorm.client.ResultSet;
public interface AccountProjectWatchAccess extends
Access<AccountProjectWatch, AccountProjectWatch.Key> {
@PrimaryKey("key")
AccountProjectWatch get(AccountProjectWatch.Key key) throws OrmException;
@Query("WHERE key.accountId = ?")
ResultSet<AccountProjectWatch> byAccount(Account.Id id) throws OrmException;
}

View File

@@ -30,4 +30,8 @@ public interface ProjectAccess extends Access<Project, Project.NameKey> {
@Query("ORDER BY name")
ResultSet<Project> all() throws OrmException;
@Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
throws OrmException;
}

View File

@@ -53,6 +53,9 @@ public interface ReviewDb extends Schema {
@Relation
StarredChangeAccess starredChanges();
@Relation
AccountProjectWatchAccess accountProjectWatches();
@Relation
ProjectAccess projects();

View File

@@ -0,0 +1,32 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.ui;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
/** Panel for use within a TabPanel to create the widget only on selection. */
public abstract class LazyTabChild<T extends Widget> extends SimplePanel {
/** @return the widget to display in this panel. */
protected abstract T create();
@Override
public void setVisible(final boolean visible) {
if (visible && getWidget() == null) {
setWidget(create());
}
super.setVisible(visible);
}
}

View File

@@ -0,0 +1,62 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.ui;
import com.google.gerrit.client.RpcStatus;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gwt.user.client.ui.SuggestOracle;
import java.util.ArrayList;
import java.util.List;
/** Suggestion Oracle for Project.NameKey entities. */
public class ProjectNameSuggestOracle extends SuggestOracle {
@Override
public void requestSuggestions(final Request req, final Callback callback) {
RpcStatus.hide(new Runnable() {
public void run() {
SuggestUtil.SVC.suggestProjectNameKey(req.getQuery(), req.getLimit(),
new GerritCallback<List<Project.NameKey>>() {
public void onSuccess(final List<Project.NameKey> result) {
final ArrayList<ProjectNameSuggestion> r =
new ArrayList<ProjectNameSuggestion>(result.size());
for (final Project.NameKey p : result) {
r.add(new ProjectNameSuggestion(p));
}
callback.onSuggestionsReady(req, new Response(r));
}
});
}
});
}
private static class ProjectNameSuggestion implements
SuggestOracle.Suggestion {
private final Project.NameKey key;
ProjectNameSuggestion(final Project.NameKey k) {
key = k;
}
public String getDisplayString() {
return key.get();
}
public String getReplacementString() {
return key.get();
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.ui;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import java.util.List;
public interface SuggestService extends RemoteJsonService {
@AllowCrossSiteRequest
void suggestProjectNameKey(String query, int limit,
AsyncCallback<List<Project.NameKey>> callback);
}

View File

@@ -0,0 +1,50 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.ui;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.BaseServiceImplementation;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import java.util.ArrayList;
import java.util.List;
public class SuggestServiceImpl extends BaseServiceImplementation implements
SuggestService {
public SuggestServiceImpl(final SchemaFactory<ReviewDb> rdf) {
super(rdf);
}
public void suggestProjectNameKey(final String query, final int limit,
final AsyncCallback<List<Project.NameKey>> callback) {
run(callback, new Action<List<Project.NameKey>>() {
public List<Project.NameKey> run(final ReviewDb db) throws OrmException {
final String a = query;
final String b = a + "\uffff";
final int max = 10;
final int n = limit <= 0 ? max : Math.min(limit, max);
final List<Project.NameKey> r = new ArrayList<Project.NameKey>();
for (final Project p : db.projects().suggestByName(a, b, n)) {
r.add(p.getNameKey());
}
return r;
}
});
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwtjsonrpc.client.JsonUtil;
public class SuggestUtil {
public static final SuggestService SVC;
static {
SVC = GWT.create(SuggestService.class);
JsonUtil.bind(SVC, "rpc/SuggestService");
}
private SuggestUtil() {
}
}

View File

@@ -34,6 +34,14 @@
font-size: 8pt;
}
.gerrit-InputFieldTypeHint {
color: grey;
}
.gerrit-SmallHeading {
font-weight: bold;
}
/** Menu **/
.gerrit-LinkMenuBar .gwt-MenuItem {
@@ -71,6 +79,7 @@
position: absolute;
left: 50%;
float: left;
top: 6px;
}
.gerrit-RpcStatus {
@@ -496,6 +505,10 @@
background-color: #d4e9a9;
padding: 5px 5px 5px 5px;
}
.gerrit-AddSshKeyPanel .gwt-Label {
font-weight: bold;
.gerrit-WatchedProjectPanel {
margin-top: 10px;
padding: 5px 5px 5px 5px;
display: table;
border: 1px solid #B0BDCC;
}

View File

@@ -0,0 +1,25 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server;
import com.google.gerrit.client.ui.SuggestServiceImpl;
/** Publishes {@link SuggestServiceImpl} over JSON. */
public class SuggestServiceSrv extends GerritJsonServlet {
@Override
protected Object createServiceHandle() throws Exception {
return new SuggestServiceImpl(GerritServer.getInstance().getDatabase());
}
}