Skip to content

Commit 84f96a5

Browse files
authored
Merge pull request #58 from qupath/advanced-search-improvements
Advanced search improvements
2 parents bfe3e8a + 4578711 commit 84f96a5

File tree

13 files changed

+204
-232
lines changed

13 files changed

+204
-232
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins {
77
qupathExtension {
88
name = "qupath-extension-omero"
99
group = "io.github.qupath"
10-
version = "0.1.0-rc9"
10+
version = "0.1.0-rc10"
1111
description = "QuPath extension to support image reading using OMERO APIs"
1212
automaticModule = "io.github.qupath.extension.omero"
1313
}

src/main/java/qupath/ext/omero/core/entities/search/SearchResult.java

Lines changed: 12 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package qupath.ext.omero.core.entities.search;
22

3-
import org.checkerframework.checker.nullness.qual.Nullable;
43
import org.slf4j.Logger;
54
import org.slf4j.LoggerFactory;
65
import qupath.ext.omero.core.entities.repositoryentities.RepositoryEntity;
@@ -23,8 +22,16 @@
2322
* <p>
2423
* This class can create usable results from an HTML search query response
2524
* (usually from {@code https://omero-server/webclient/load_searching/form}).
25+
*
26+
* @param type the type of the object (e.g. dataset, image)
27+
* @param id the id of the object
28+
* @param name the name of the object
29+
* @param group the group name whose object belongs to
30+
* @param link a URL linking to the object
31+
* @param dateAcquired the date this object was acquired. Can be null
32+
* @param dateImported the date this object was imported. Can be null
2633
*/
27-
public class SearchResult {
34+
public record SearchResult(String type, int id, String name, Date dateAcquired, Date dateImported, String group, String link) {
2835

2936
private static final Logger logger = LoggerFactory.getLogger(SearchResult.class);
3037
private static final DateFormat OMERO_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss Z");
@@ -33,44 +40,6 @@ public class SearchResult {
3340
private static final Pattern DATE_PATTERN = Pattern.compile("<td class=\"date\" data-isodate='(.+?)'></td>");
3441
private static final Pattern GROUP_PATTERN = Pattern.compile("<td class=\"group\">(.+?)</td>");
3542
private static final Pattern LINK_PATTERN = Pattern.compile("<td><a href=\"(.+?)\"");
36-
private final String type;
37-
private final int id;
38-
private final String name;
39-
private final Date dateAcquired;
40-
private final Date dateImported;
41-
private final String group;
42-
private final String link;
43-
44-
/**
45-
* Creates a new search result corresponding to an OMERO object.
46-
* You should rather use {@link #createFromHTMLResponse(String, URI)}
47-
* to create search results.
48-
*
49-
* @param type the type of the object (e.g. dataset, image)
50-
* @param id the id of the object
51-
* @param name the name of the object
52-
* @param group the group name whose object belongs to
53-
* @param link a URL linking to the object
54-
* @param dateAcquired the date this object was acquired
55-
* @param dateImported the date this object was imported
56-
*/
57-
public SearchResult(
58-
String type,
59-
int id,
60-
String name,
61-
String group,
62-
String link,
63-
@Nullable Date dateAcquired,
64-
@Nullable Date dateImported
65-
) {
66-
this.type = type;
67-
this.id = id;
68-
this.name = name;
69-
this.dateAcquired = dateAcquired;
70-
this.dateImported = dateImported;
71-
this.group = group;
72-
this.link = link;
73-
}
7443

7544
@Override
7645
public boolean equals(Object obj) {
@@ -119,10 +88,10 @@ public static List<SearchResult> createFromHTMLResponse(String htmlResponse, URI
11988
rowMatcher.group(1),
12089
Integer.parseInt(rowMatcher.group(2)),
12190
findPatternInText(DESCRIPTION_PATTERN, row).orElse("-"),
122-
findPatternInText(GROUP_PATTERN, row).orElse("-"),
123-
serverURI + findPatternInText(LINK_PATTERN, row).orElse(""),
12491
acquiredDate,
125-
importedDate
92+
importedDate,
93+
findPatternInText(GROUP_PATTERN, row).orElse("-"),
94+
serverURI + findPatternInText(LINK_PATTERN, row).orElse("")
12695
));
12796
} catch (Exception e) {
12897
logger.warn("Could not parse search result.", e);
@@ -132,11 +101,6 @@ public static List<SearchResult> createFromHTMLResponse(String htmlResponse, URI
132101
return searchResults;
133102
}
134103

135-
@Override
136-
public String toString() {
137-
return String.format("Search result: %s of ID %d and name %s", type, id, name);
138-
}
139-
140104
/**
141105
* @return the class of the type (e.g. image, dataset) of the result, or an empty Optional if not found
142106
*/
@@ -158,50 +122,6 @@ public Optional<Class<? extends RepositoryEntity>> getType() {
158122
}
159123
}
160124

161-
/**
162-
* @return the ID of the result
163-
*/
164-
public int getId() {
165-
return id;
166-
}
167-
168-
/**
169-
* @return the name of the result
170-
*/
171-
public String getName() {
172-
return name;
173-
}
174-
175-
/**
176-
* @return the date the result was acquired, or an empty Optional
177-
* if the date couldn't be retrieved
178-
*/
179-
public Optional<Date> getDateAcquired() {
180-
return Optional.ofNullable(dateAcquired);
181-
}
182-
183-
/**
184-
* @return the date the result was imported, or an empty Optional
185-
* if the date couldn't be retrieved
186-
*/
187-
public Optional<Date> getDateImported() {
188-
return Optional.ofNullable(dateImported);
189-
}
190-
191-
/**
192-
* @return the group name of the result
193-
*/
194-
public String getGroupName() {
195-
return group;
196-
}
197-
198-
/**
199-
* @return a link to open the result in a web browser
200-
*/
201-
public String getLink() {
202-
return link;
203-
}
204-
205125
private static Optional<String> findPatternInText(Pattern pattern, String text) {
206126
return findPatternInText(pattern, text, 0);
207127
}

src/main/java/qupath/ext/omero/gui/browser/advancedsearch/AdvancedSearch.java

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import javafx.scene.control.TableColumn;
1616
import javafx.scene.control.TableView;
1717
import javafx.scene.control.TextField;
18-
import javafx.scene.input.KeyCode;
1918
import javafx.scene.input.KeyEvent;
2019
import javafx.scene.layout.GridPane;
2120
import javafx.stage.Stage;
@@ -31,6 +30,7 @@
3130
import qupath.ext.omero.core.entities.search.SearchResult;
3231
import qupath.ext.omero.gui.UiUtilities;
3332
import qupath.ext.omero.gui.browser.advancedsearch.cellfactories.LinkCellFactory;
33+
import qupath.ext.omero.gui.browser.advancedsearch.cellfactories.TextCellFactory;
3434
import qupath.ext.omero.gui.browser.advancedsearch.cellfactories.TypeCellFactory;
3535
import qupath.fx.dialogs.Dialogs;
3636

@@ -74,10 +74,10 @@ public class AdvancedSearch extends Stage {
7474
@FXML
7575
private CheckBox screens;
7676
@FXML
77-
private ComboBox<Owner> owner;
78-
@FXML
7977
private ComboBox<Group> group;
8078
@FXML
79+
private ComboBox<Owner> owner;
80+
@FXML
8181
private Button search;
8282
@FXML
8383
private Button importImage;
@@ -178,65 +178,79 @@ private void onImportButtonClicked(ActionEvent ignoredEvent) {
178178
private void initUI(Stage ownerWindow) throws IOException {
179179
UiUtilities.loadFXML(this, AdvancedSearch.class.getResource("advanced_search.fxml"));
180180

181-
owner.getItems().setAll(server.getOwners());
182-
owner.getItems().add(Owner.getAllMembersOwner());
183-
owner.getSelectionModel().select(server.getConnectedOwner());
184-
owner.setConverter(new StringConverter<>() {
181+
group.getItems().setAll(Group.getAllGroupsGroup());
182+
group.getItems().addAll(server.getGroups());
183+
group.getSelectionModel().select(server.getDefaultGroup());
184+
group.setConverter(new StringConverter<>() {
185185
@Override
186-
public String toString(Owner owner) {
187-
return owner == null ? "" : owner.getFullName();
186+
public String toString(Group object) {
187+
return object == null ? "" : object.getName();
188188
}
189+
189190
@Override
190-
public Owner fromString(String string) {
191+
public Group fromString(String string) {
191192
return null;
192193
}
193194
});
194195

195-
group.getItems().setAll(server.getGroups());
196-
group.getItems().add(Group.getAllGroupsGroup());
197-
group.getSelectionModel().select(server.getDefaultGroup());
198-
group.setConverter(new StringConverter<>() {
196+
owner.getItems().setAll(Owner.getAllMembersOwner());
197+
owner.getItems().addAll(group.getSelectionModel().getSelectedItem().getOwners());
198+
owner.getSelectionModel().select(server.getConnectedOwner());
199+
owner.setConverter(new StringConverter<>() {
199200
@Override
200-
public String toString(Group object) {
201-
return object == null ? "" : object.getName();
201+
public String toString(Owner owner) {
202+
return owner == null ? "" : owner.getFullName();
202203
}
203-
204204
@Override
205-
public Group fromString(String string) {
205+
public Owner fromString(String string) {
206206
return null;
207207
}
208208
});
209209

210210
results.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
211211

212212
typeColumn.setCellValueFactory(n -> new ReadOnlyObjectWrapper<>(n.getValue()));
213-
nameColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().getName()));
214-
acquiredColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().getDateAcquired().isPresent() ?
215-
DATE_FORMAT.format(n.getValue().getDateAcquired().get()) : ""
213+
nameColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().name()));
214+
acquiredColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().dateAcquired() == null ?
215+
"" : DATE_FORMAT.format(n.getValue().dateAcquired())
216216
));
217-
importedColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().getDateImported().isPresent() ?
218-
DATE_FORMAT.format(n.getValue().getDateImported().get()) : ""
217+
importedColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().dateImported() == null ?
218+
"" : DATE_FORMAT.format(n.getValue().dateImported())
219219
));
220-
groupColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().getGroupName()));
220+
groupColumn.setCellValueFactory(n -> new ReadOnlyStringWrapper(n.getValue().group()));
221221
linkColumn.setCellValueFactory(n -> new ReadOnlyObjectWrapper<>(n.getValue()));
222222

223223
typeColumn.setCellFactory(n -> new TypeCellFactory(apisHandler));
224+
nameColumn.setCellFactory(n -> new TextCellFactory());
225+
acquiredColumn.setCellFactory(n -> new TextCellFactory());
226+
importedColumn.setCellFactory(n -> new TextCellFactory());
227+
groupColumn.setCellFactory(n -> new TextCellFactory());
224228
linkColumn.setCellFactory(n -> new LinkCellFactory());
225229

226230
initOwner(ownerWindow);
227231
show();
228232
}
229233

230234
private void setUpListeners() {
231-
addEventHandler(KeyEvent.KEY_PRESSED, e -> {
232-
if (e.getCode() == KeyCode.ESCAPE) {
233-
close();
235+
group.getSelectionModel().selectedItemProperty().addListener((p, o, n) -> {
236+
owner.getItems().setAll(Owner.getAllMembersOwner());
237+
238+
if (n == null) {
239+
owner.getSelectionModel().select(null);
240+
} else {
241+
if (n.equals(Group.getAllGroupsGroup())) {
242+
owner.getItems().addAll(server.getOwners());
243+
owner.getSelectionModel().select(Owner.getAllMembersOwner());
244+
} else {
245+
owner.getItems().addAll(n.getOwners());
246+
owner.getSelectionModel().selectFirst();
247+
}
234248
}
235249
});
236250

237251
importImage.textProperty().bind(Bindings.createStringBinding(
238252
() -> results.getSelectionModel().getSelectedItems().size() == 1 ?
239-
resources.getString("Browser.ServerBrowser.AdvancedSearch.import") + " " + results.getSelectionModel().getSelectedItems().getFirst().getName() :
253+
resources.getString("Browser.ServerBrowser.AdvancedSearch.import") + " " + results.getSelectionModel().getSelectedItems().getFirst().name() :
240254
resources.getString("Browser.ServerBrowser.AdvancedSearch.importObjects"),
241255
results.getSelectionModel().getSelectedItems()
242256
));
@@ -248,17 +262,31 @@ private void setUpListeners() {
248262
}
249263
});
250264

251-
typeColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
252-
nameColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
253-
acquiredColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
254-
importedColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
255-
groupColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
256-
linkColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.16));
265+
typeColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
266+
nameColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
267+
acquiredColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
268+
importedColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
269+
groupColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
270+
linkColumn.prefWidthProperty().bind(results.widthProperty().multiply(0.1667));
271+
272+
getScene().addEventFilter(
273+
KeyEvent.KEY_PRESSED,
274+
keyEvent -> {
275+
switch (keyEvent.getCode()) {
276+
case ENTER:
277+
onSearchClicked(null);
278+
break;
279+
case ESCAPE:
280+
close();
281+
break;
282+
}
283+
}
284+
);
257285
}
258286

259287
private void importSelectedImages() {
260288
UiUtilities.openImages(results.getSelectionModel().getSelectedItems().stream()
261-
.map(SearchResult::getLink)
289+
.map(SearchResult::link)
262290
.toList()
263291
);
264292
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
package qupath.ext.omero.gui.browser.advancedsearch.cellfactories;
22

3-
import javafx.scene.control.Button;
3+
import javafx.scene.control.Hyperlink;
4+
import javafx.scene.control.OverrunStyle;
45
import javafx.scene.control.TableCell;
5-
import qupath.ext.omero.Utils;
6+
import javafx.scene.control.Tooltip;
67
import qupath.ext.omero.core.entities.search.SearchResult;
78
import qupath.lib.gui.QuPathGUI;
89

9-
import java.util.ResourceBundle;
10-
1110
/**
1211
* Cell factory that displays a button that opens the link of a search result in a browser.
1312
*/
1413
public class LinkCellFactory extends TableCell<SearchResult, SearchResult> {
1514

16-
private static final ResourceBundle resources = Utils.getResources();
17-
private final Button button;
15+
private final Hyperlink hyperlink;
1816

1917
public LinkCellFactory() {
20-
button = new Button(resources.getString("Browser.ServerBrowser.AdvancedSearch.link"));
18+
hyperlink = new Hyperlink();
19+
hyperlink.setTextOverrun(OverrunStyle.LEADING_WORD_ELLIPSIS);
2120
}
2221

2322
@Override
@@ -27,8 +26,10 @@ protected void updateItem(SearchResult item, boolean empty) {
2726
setGraphic(null);
2827

2928
if (item != null && !empty) {
30-
button.setOnAction(e -> QuPathGUI.openInBrowser(item.getLink()));
31-
setGraphic(button);
29+
hyperlink.setText(item.link());
30+
hyperlink.setTooltip(new Tooltip(item.link()));
31+
hyperlink.setOnAction(e -> QuPathGUI.openInBrowser(item.link()));
32+
setGraphic(hyperlink);
3233
}
3334
}
3435
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package qupath.ext.omero.gui.browser.advancedsearch.cellfactories;
2+
3+
import javafx.scene.control.TableCell;
4+
import javafx.scene.control.Tooltip;
5+
import qupath.ext.omero.core.entities.search.SearchResult;
6+
7+
/**
8+
* Cell factory that displays a text in the cell and in an associated tooltip.
9+
*/
10+
public class TextCellFactory extends TableCell<SearchResult, String> {
11+
12+
@Override
13+
protected void updateItem(String item, boolean empty) {
14+
super.updateItem(item, empty);
15+
16+
setText(null);
17+
setTooltip(null);
18+
19+
if (item != null && !empty) {
20+
setText(item);
21+
setTooltip(new Tooltip(item));
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)