Skip to content

Commit

Permalink
Merge pull request #25 from Petikoch/listSelectionEventSupport
Browse files Browse the repository at this point in the history
List selection event support
  • Loading branch information
epb-644 committed Sep 7, 2015
2 parents a75a3d5 + 8614999 commit 84a0f2d
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 1 deletion.
16 changes: 16 additions & 0 deletions src/main/java/rx/observables/SwingObservable.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import java.util.Set;

import javax.swing.AbstractButton;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.text.Document;

import rx.Observable;
Expand Down Expand Up @@ -201,6 +203,20 @@ public Boolean call(ItemEvent event) {
}
});
}

/**
* Creates an observable corresponding to list selection events (e.g. from a JList or a JTable row / column selection).
*
* For more info to swing list selection see <a href="https://docs.oracle.com/javase/tutorial/uiswing/events/listselectionlistener.html">
* How to Write a List Selection Listener</a>.
*
* @param listSelectionModel
* The ListSelectionModel to register the observable for.
* @return Observable emitting the list selection events.
*/
public static Observable<ListSelectionEvent> fromListSelectionEvents(ListSelectionModel listSelectionModel) {
return ListSelectionEventSource.fromListSelectionEventsOf(listSelectionModel);
}

/**
* Creates an observable corresponding to property change events.
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/rx/swing/sources/ListSelectionEventSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2015 Netflix, 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 rx.swing.sources;

import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.functions.Action0;
import rx.schedulers.SwingScheduler;
import rx.subscriptions.Subscriptions;

import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public enum ListSelectionEventSource { ; // no instances

/**
* @see rx.observables.SwingObservable#fromListSelectionEvents(ListSelectionModel)
*/
public static Observable<ListSelectionEvent> fromListSelectionEventsOf(final ListSelectionModel listSelectionModel) {
return Observable.create(new OnSubscribe<ListSelectionEvent>() {
@Override
public void call(final Subscriber<? super ListSelectionEvent> subscriber) {
final ListSelectionListener listener = new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent event) {
subscriber.onNext(event);
}

};
listSelectionModel.addListSelectionListener(listener);
subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
listSelectionModel.removeListSelectionListener(listener);
}
}));
}
}).subscribeOn(SwingScheduler.getInstance())
.unsubscribeOn(SwingScheduler.getInstance());
}
}
2 changes: 1 addition & 1 deletion src/main/java/rx/swing/sources/WindowEventSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
public enum WindowEventSource { ; // no instances

/**
* @see rx.observables.SwingObservable#fromWindowEvents(Component)
* @see rx.observables.SwingObservable#fromWindowEventsOf(Window)
*/
public static Observable<WindowEvent> fromWindowEventsOf(final Window window) {
return Observable.create(new OnSubscribe<WindowEvent>() {
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/rx/swing/sources/ItemEventSourceTest.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/**
* Copyright 2014 Netflix, 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 rx.swing.sources;

import static java.awt.event.ItemEvent.DESELECTED;
Expand Down
238 changes: 238 additions & 0 deletions src/test/java/rx/swing/sources/ListSelectionEventSourceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/**
* Copyright 2015 Netflix, 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 rx.swing.sources;

import org.junit.Test;
import rx.Subscription;
import rx.functions.Action0;
import rx.observers.TestSubscriber;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;

import static junit.framework.Assert.assertEquals;

public class ListSelectionEventSourceTest {

@Test
public void jtableRowSelectionObservingSelectionEvents() throws Throwable {
SwingTestHelper.create().runInEventDispatchThread(new Action0() {

@Override
public void call() {
TestSubscriber<ListSelectionEvent> testSubscriber = TestSubscriber.create();

JTable table = createJTable();
ListSelectionEventSource
.fromListSelectionEventsOf(table.getSelectionModel())
.subscribe(testSubscriber);

testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();

table.getSelectionModel().setSelectionInterval(0, 0);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(1);

assertListSelectionEventEquals(
new ListSelectionEvent(
table.getSelectionModel(),
0 /* start of region with selection changes */,
0 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(0));

table.getSelectionModel().setSelectionInterval(2, 2);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(2);

assertListSelectionEventEquals(
new ListSelectionEvent(
table.getSelectionModel(),
0 /* start of region with selection changes */,
2 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(1));
}
}).awaitTerminal();
}

@Test
public void jtableColumnSelectionObservingSelectionEvents() throws Throwable {
SwingTestHelper.create().runInEventDispatchThread(new Action0() {

@Override
public void call() {
TestSubscriber<ListSelectionEvent> testSubscriber = TestSubscriber.create();

JTable table = createJTable();
table.getColumnModel().getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);

ListSelectionEventSource
.fromListSelectionEventsOf(table.getColumnModel().getSelectionModel())
.subscribe(testSubscriber);

testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();

table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(1);

assertListSelectionEventEquals(
new ListSelectionEvent(
table.getColumnModel().getSelectionModel(),
0 /* start of region with selection changes */,
0 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(0));

table.getColumnModel().getSelectionModel().setSelectionInterval(2, 2);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(2);

assertListSelectionEventEquals(
new ListSelectionEvent(
table.getColumnModel().getSelectionModel(),
0 /* start of region with selection changes */,
2 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(1));

}
}).awaitTerminal();
}

@Test
public void jlistSelectionObservingSelectionEvents() throws Throwable {
SwingTestHelper.create().runInEventDispatchThread(new Action0() {

@Override
public void call() {
TestSubscriber<ListSelectionEvent> testSubscriber = TestSubscriber.create();

JList<String> jList = new JList<String>(new String[]{"a", "b", "c", "d", "e", "f"});
jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

ListSelectionEventSource
.fromListSelectionEventsOf(jList.getSelectionModel())
.subscribe(testSubscriber);

testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();

jList.getSelectionModel().setSelectionInterval(0, 0);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(1);

assertListSelectionEventEquals(
new ListSelectionEvent(
jList.getSelectionModel(),
0 /* start of region with selection changes */,
0 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(0));

jList.getSelectionModel().setSelectionInterval(2, 2);

testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(2);

assertListSelectionEventEquals(
new ListSelectionEvent(
jList.getSelectionModel(),
0 /* start of region with selection changes */,
2 /* end of region with selection changes */,
false),
testSubscriber.getOnNextEvents().get(1));
}
}).awaitTerminal();
}

@Test
public void jtableRowSelectionUnsubscribeRemovesRowSelectionListener() throws Throwable {
SwingTestHelper.create().runInEventDispatchThread(new Action0() {

@Override
public void call() {
TestSubscriber<ListSelectionEvent> testSubscriber = TestSubscriber.create();

JTable table = createJTable();
int numberOfListenersBefore = getNumberOfRowListSelectionListeners(table);

Subscription sub = ListSelectionEventSource
.fromListSelectionEventsOf(table.getSelectionModel())
.subscribe(testSubscriber);

testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();

sub.unsubscribe();

testSubscriber.assertUnsubscribed();

table.getSelectionModel().setSelectionInterval(0, 0);

testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();

assertEquals(numberOfListenersBefore, getNumberOfRowListSelectionListeners(table));
}
}).awaitTerminal();
}

private static int getNumberOfRowListSelectionListeners(final JTable table) {
return ((DefaultListSelectionModel) table.getSelectionModel()).getListSelectionListeners().length;
}

private static JTable createJTable() {
return new JTable(new Object[][]{
{"A1", "B1", "C1"},
{"A2", "B2", "C2"},
{"A3", "B3", "C3"},
},
new String[]{
"A", "B", "C"
});
}

private static void assertListSelectionEventEquals(ListSelectionEvent expected, ListSelectionEvent actual) {
if (expected == null) {
throw new IllegalArgumentException("missing expected");
}

if (actual == null) {
throw new AssertionError("Expected " + expected + ", but was: " + actual);
}
if (!expected.getSource().equals(actual.getSource())) {
throw new AssertionError("Expected " + expected + ", but was: " + actual + ". Different source.");
}
if (expected.getFirstIndex() != actual.getFirstIndex()) {
throw new AssertionError("Expected " + expected + ", but was: " + actual + ". Different first index.");
}
if (expected.getLastIndex() != actual.getLastIndex()) {
throw new AssertionError("Expected " + expected + ", but was: " + actual + ". Different last index.");
}
if (expected.getValueIsAdjusting() != actual.getValueIsAdjusting()) {
throw new AssertionError("Expected " + expected + ", but was: " + actual + ". Different ValueIsAdjusting.");
}
}
}
15 changes: 15 additions & 0 deletions src/test/java/rx/swing/sources/PropertyChangeEventSourceTest.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/**
* Copyright 2014 Netflix, 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 rx.swing.sources;

import static org.junit.Assert.*;
Expand Down

0 comments on commit 84a0f2d

Please sign in to comment.