aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar/web/client/view
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2013-01-09 20:32:40 +1300
committerAnton Tananaev <anton.tananaev@gmail.com>2013-01-09 20:32:40 +1300
commitf0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd (patch)
treecd9beb89c07dbeacbb26d9df8a1a42502d206c74 /src/org/traccar/web/client/view
parenta7c6a5f03c0ac94603b5238c59a275a239d16b73 (diff)
downloadtrackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.tar.gz
trackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.tar.bz2
trackermap-web-f0b99fd68cb3b6b484d50c1f9ebc0e77997fd2dd.zip
Completely rewritten project
Diffstat (limited to 'src/org/traccar/web/client/view')
-rw-r--r--src/org/traccar/web/client/view/ApplicationView.java33
-rw-r--r--src/org/traccar/web/client/view/ApplicationView.ui.xml42
-rw-r--r--src/org/traccar/web/client/view/ArchiveView.java79
-rw-r--r--src/org/traccar/web/client/view/ArchiveView.ui.xml60
-rw-r--r--src/org/traccar/web/client/view/DeviceDialog.java69
-rw-r--r--src/org/traccar/web/client/view/DeviceDialog.ui.xml40
-rw-r--r--src/org/traccar/web/client/view/DeviceView.java128
-rw-r--r--src/org/traccar/web/client/view/DeviceView.ui.xml46
-rw-r--r--src/org/traccar/web/client/view/LoginDialog.java59
-rw-r--r--src/org/traccar/web/client/view/LoginDialog.ui.xml40
-rw-r--r--src/org/traccar/web/client/view/MapView.java149
11 files changed, 745 insertions, 0 deletions
diff --git a/src/org/traccar/web/client/view/ApplicationView.java b/src/org/traccar/web/client/view/ApplicationView.java
new file mode 100644
index 00000000..bff8854f
--- /dev/null
+++ b/src/org/traccar/web/client/view/ApplicationView.java
@@ -0,0 +1,33 @@
+package org.traccar.web.client.view;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+import com.sencha.gxt.widget.core.client.ContentPanel;
+
+public class ApplicationView extends Composite {
+
+ private static ApplicationViewUiBinder uiBinder = GWT.create(ApplicationViewUiBinder.class);
+
+ interface ApplicationViewUiBinder extends UiBinder<Widget, ApplicationView> {
+ }
+
+ @UiField(provided = true)
+ ContentPanel devicePanel;
+
+ @UiField(provided = true)
+ ContentPanel mapPanel;
+
+ @UiField(provided = true)
+ ContentPanel archivePanel;
+
+ public ApplicationView(ContentPanel deviceView, ContentPanel mapView, ContentPanel archiveView) {
+ devicePanel = deviceView;
+ mapPanel = mapView;
+ archivePanel = archiveView;
+ initWidget(uiBinder.createAndBindUi(this));
+ }
+
+}
diff --git a/src/org/traccar/web/client/view/ApplicationView.ui.xml b/src/org/traccar/web/client/view/ApplicationView.ui.xml
new file mode 100644
index 00000000..2faae1e7
--- /dev/null
+++ b/src/org/traccar/web/client/view/ApplicationView.ui.xml
@@ -0,0 +1,42 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder
+ xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui"
+ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
+ xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client">
+
+ <ui:with type="com.sencha.gxt.core.client.util.Margins" field="westMargins">
+ <ui:attributes top="5" right="5" bottom="0" left="5" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.core.client.util.Margins" field="centerMargins">
+ <ui:attributes top="5" right="5" bottom="0" left="0" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.core.client.util.Margins" field="southMargins">
+ <ui:attributes top="5" right="5" bottom="5" left="5" />
+ </ui:with>
+
+ <ui:with type="com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData" field="westData">
+ <ui:attributes size="0.15" maxSize="2147483647" margins="{westMargins}" split="true" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.widget.core.client.container.MarginData" field="centerData">
+ <ui:attributes margins="{centerMargins}" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData" field="southData">
+ <ui:attributes size="0.25" maxSize="2147483647" margins="{southMargins}" split="true" />
+ </ui:with>
+
+ <container:Viewport>
+ <container:BorderLayoutContainer>
+ <container:west layoutData="{westData}">
+ <gxt:ContentPanel ui:field="devicePanel" />
+ </container:west>
+ <container:center layoutData="{centerData}">
+ <gxt:ContentPanel ui:field="mapPanel" />
+ </container:center>
+ <container:south layoutData="{southData}">
+ <gxt:ContentPanel ui:field="archivePanel" />
+ </container:south>
+ </container:BorderLayoutContainer>
+ </container:Viewport>
+
+</ui:UiBinder>
diff --git a/src/org/traccar/web/client/view/ArchiveView.java b/src/org/traccar/web/client/view/ArchiveView.java
new file mode 100644
index 00000000..79621ba9
--- /dev/null
+++ b/src/org/traccar/web/client/view/ArchiveView.java
@@ -0,0 +1,79 @@
+package org.traccar.web.client.view;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.traccar.web.client.model.PositionProperties;
+import org.traccar.web.shared.model.Position;
+
+import com.google.gwt.cell.client.DateCell;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+import com.sencha.gxt.data.shared.ListStore;
+import com.sencha.gxt.widget.core.client.ContentPanel;
+import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
+import com.sencha.gxt.widget.core.client.grid.ColumnModel;
+import com.sencha.gxt.widget.core.client.grid.Grid;
+
+public class ArchiveView extends Composite {
+
+ private static ArchiveViewUiBinder uiBinder = GWT.create(ArchiveViewUiBinder.class);
+
+ interface ArchiveViewUiBinder extends UiBinder<Widget, ArchiveView> {
+ }
+
+ public interface ArchiveHandler {
+ public void onSelected(Position position);
+ }
+
+ private ArchiveHandler archiveHandler;
+
+ @UiField
+ ContentPanel contentPanel;
+
+ public ContentPanel getView() {
+ return contentPanel;
+ }
+
+ @UiField(provided = true)
+ ColumnModel<Position> columnModel;
+
+ @UiField(provided = true)
+ ListStore<Position> store;
+
+ @UiField
+ Grid<Position> grid;
+
+ public ArchiveView(ArchiveHandler archiveHandler) {
+ this.archiveHandler = archiveHandler;
+
+ PositionProperties positionProperties = GWT.create(PositionProperties.class);
+
+ List<ColumnConfig<Position, ?>> columnConfigList = new LinkedList<ColumnConfig<Position, ?>>();
+
+ columnConfigList.add(new ColumnConfig<Position, Boolean>(positionProperties.valid(), 0, "Valid"));
+
+ ColumnConfig<Position, Date> columnConfig = new ColumnConfig<Position, Date>(positionProperties.time(), 0, "Time");
+ columnConfig.setCell(new DateCell(DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss")));
+ columnConfigList.add(columnConfig);
+
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.latitude(), 0, "Latitude"));
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.longitude(), 0, "Longitude"));
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.altitude(), 0, "Altitude"));
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.speed(), 0, "Speed"));
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.course(), 0, "Course"));
+ columnConfigList.add(new ColumnConfig<Position, Double>(positionProperties.power(), 0, "Power"));
+
+ columnModel = new ColumnModel<Position>(columnConfigList);
+
+ store = new ListStore<Position>(positionProperties.id());
+
+ uiBinder.createAndBindUi(this);
+ }
+
+}
diff --git a/src/org/traccar/web/client/view/ArchiveView.ui.xml b/src/org/traccar/web/client/view/ArchiveView.ui.xml
new file mode 100644
index 00000000..370eb024
--- /dev/null
+++ b/src/org/traccar/web/client/view/ArchiveView.ui.xml
@@ -0,0 +1,60 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder
+ xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui"
+ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
+ xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"
+ xmlns:toolbar="urn:import:com.sencha.gxt.widget.core.client.toolbar"
+ xmlns:grid="urn:import:com.sencha.gxt.widget.core.client.grid"
+ xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button"
+ xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form">
+
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="toolBarRowData">
+ <ui:attributes width="1" height="-1" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="layoutData">
+ <ui:attributes width="1" height="1" />
+ </ui:with>
+
+ <ui:with type="com.sencha.gxt.data.shared.ListStore" field="store" />
+ <ui:with type="com.sencha.gxt.widget.core.client.grid.ColumnModel" field="columnModel" />
+
+ <ui:with type="com.sencha.gxt.widget.core.client.grid.GridView" field="view">
+ <ui:attributes stripeRows="true" autoFill="true" />
+ </ui:with>
+
+ <gxt:ContentPanel ui:field="contentPanel" headingText="Archive">
+ <container:VerticalLayoutContainer>
+
+ <container:child layoutData="{toolBarRowData}">
+ <toolbar:ToolBar>
+
+ <toolbar:LabelToolItem label="Device:" />
+ <form:TimeField /> <!-- put combobox here -->
+ <toolbar:SeparatorToolItem />
+
+ <toolbar:LabelToolItem label="From:" />
+ <form:DateField width="125" />
+ <toolbar:LabelToolItem width="5" />
+ <form:TimeField width="75" />
+ <toolbar:SeparatorToolItem />
+
+ <toolbar:LabelToolItem label="To:" />
+ <form:DateField width="125" />
+ <toolbar:LabelToolItem width="5" />
+ <form:TimeField width="75" />
+ <toolbar:SeparatorToolItem />
+
+ <button:TextButton ui:field="loadButton" text="Load" />
+
+ </toolbar:ToolBar>
+ </container:child>
+
+ <container:child layoutData="{layoutData}">
+ <grid:Grid ui:field="grid" store="{store}" cm="{columnModel}" view="{view}" />
+ </container:child>
+
+ </container:VerticalLayoutContainer>
+ </gxt:ContentPanel>
+
+</ui:UiBinder>
diff --git a/src/org/traccar/web/client/view/DeviceDialog.java b/src/org/traccar/web/client/view/DeviceDialog.java
new file mode 100644
index 00000000..891dd4ef
--- /dev/null
+++ b/src/org/traccar/web/client/view/DeviceDialog.java
@@ -0,0 +1,69 @@
+package org.traccar.web.client.view;
+
+import org.traccar.web.shared.model.Device;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.SimpleBeanEditorDriver;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Widget;
+import com.sencha.gxt.widget.core.client.Window;
+import com.sencha.gxt.widget.core.client.event.SelectEvent;
+import com.sencha.gxt.widget.core.client.form.TextField;
+
+public class DeviceDialog implements Editor<Device> {
+
+ private static DeviceDialogUiBinder uiBinder = GWT.create(DeviceDialogUiBinder.class);
+
+ interface DeviceDialogUiBinder extends UiBinder<Widget, DeviceDialog> {
+ }
+
+ private DeviceDriver driver = GWT.create(DeviceDriver.class);
+
+ interface DeviceDriver extends SimpleBeanEditorDriver<Device, DeviceDialog> {
+ }
+
+ public interface DeviceHandler {
+ public void onSave(Device device);
+ }
+
+ private DeviceHandler deviceHandler;
+
+ @UiField
+ Window window;
+
+ @UiField
+ TextField name;
+
+ @UiField
+ TextField uniqueId;
+
+ public DeviceDialog(Device device, DeviceHandler deviceHandler) {
+ this.deviceHandler = deviceHandler;
+ uiBinder.createAndBindUi(this);
+ driver.initialize(this);
+ driver.edit(device);
+ }
+
+ public void show() {
+ window.show();
+ }
+
+ public void hide() {
+ window.hide();
+ }
+
+ @UiHandler("saveButton")
+ public void onLoginClicked(SelectEvent event) {
+ window.hide();
+ deviceHandler.onSave(driver.flush());
+ }
+
+ @UiHandler("cancelButton")
+ public void onRegisterClicked(SelectEvent event) {
+ window.hide();
+ }
+
+}
diff --git a/src/org/traccar/web/client/view/DeviceDialog.ui.xml b/src/org/traccar/web/client/view/DeviceDialog.ui.xml
new file mode 100644
index 00000000..4f7ccc56
--- /dev/null
+++ b/src/org/traccar/web/client/view/DeviceDialog.ui.xml
@@ -0,0 +1,40 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder
+ xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui"
+ xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"
+ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
+ xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form"
+ xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button">
+
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData">
+ <ui:attributes width="1" height="-1" />
+ </ui:with>
+
+ <gxt:Window ui:field="window" pixelSize="300, 130" modal="true" headingText="Device" focusWidget="{saveButton}">
+ <container:VerticalLayoutContainer>
+ <container:child layoutData="{verticalLayoutData}">
+ <form:FieldLabel text="Name">
+ <form:widget>
+ <form:TextField ui:field="name" />
+ </form:widget>
+ </form:FieldLabel>
+ </container:child>
+ <container:child layoutData="{verticalLayoutData}">
+ <form:FieldLabel text="Unique Identifier">
+ <form:widget>
+ <form:TextField ui:field="uniqueId" />
+ </form:widget>
+ </form:FieldLabel>
+ </container:child>
+ </container:VerticalLayoutContainer>
+
+ <gxt:button>
+ <button:TextButton ui:field="saveButton" text="Save" />
+ </gxt:button>
+ <gxt:button>
+ <button:TextButton ui:field="cancelButton" text="Cancel" />
+ </gxt:button>
+ </gxt:Window>
+
+</ui:UiBinder>
diff --git a/src/org/traccar/web/client/view/DeviceView.java b/src/org/traccar/web/client/view/DeviceView.java
new file mode 100644
index 00000000..157973e0
--- /dev/null
+++ b/src/org/traccar/web/client/view/DeviceView.java
@@ -0,0 +1,128 @@
+package org.traccar.web.client.view;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.traccar.web.client.model.DeviceProperties;
+import org.traccar.web.shared.model.Device;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+import com.sencha.gxt.core.client.Style.SelectionMode;
+import com.sencha.gxt.data.shared.ListStore;
+import com.sencha.gxt.widget.core.client.ContentPanel;
+import com.sencha.gxt.widget.core.client.button.TextButton;
+import com.sencha.gxt.widget.core.client.event.SelectEvent;
+import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
+import com.sencha.gxt.widget.core.client.grid.ColumnModel;
+import com.sencha.gxt.widget.core.client.grid.Grid;
+import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
+
+public class DeviceView extends Composite implements SelectionChangedEvent.SelectionChangedHandler<Device> {
+
+ private static DeviceViewUiBinder uiBinder = GWT.create(DeviceViewUiBinder.class);
+
+ interface DeviceViewUiBinder extends UiBinder<Widget, DeviceView> {
+ }
+
+ public interface DeviceHandler {
+ public void onSelected(Device device);
+ public void onAdd();
+ public void onEdit(Device device);
+ public void onRemove(Device device);
+ }
+
+ private DeviceHandler deviceHandler;
+
+ @UiField
+ ContentPanel contentPanel;
+
+ public ContentPanel getView() {
+ return contentPanel;
+ }
+
+ @UiField
+ TextButton addButton;
+
+ @UiField
+ TextButton editButton;
+
+ @UiField
+ TextButton removeButton;
+
+ @UiField(provided = true)
+ ColumnModel<Device> columnModel;
+
+ @UiField(provided = true)
+ ListStore<Device> store;
+
+ @UiField
+ Grid<Device> grid;
+
+ public DeviceView(DeviceHandler deviceHandler) {
+ this.deviceHandler = deviceHandler;
+
+ DeviceProperties deviceProperties = GWT.create(DeviceProperties.class);
+
+ List<ColumnConfig<Device, ?>> columnConfigList = new LinkedList<ColumnConfig<Device, ?>>();
+ columnConfigList.add(new ColumnConfig<Device, String>(deviceProperties.name(), 0, "Name"));
+ columnConfigList.add(new ColumnConfig<Device, String>(deviceProperties.uniqueId(), 0, "Unique Identifier"));
+ columnModel = new ColumnModel<Device>(columnConfigList);
+
+ store = new ListStore<Device>(deviceProperties.id());
+
+ uiBinder.createAndBindUi(this);
+
+ grid.getSelectionModel().addSelectionChangedHandler(this);
+ grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
+ }
+
+ @Override
+ public void onSelectionChanged(SelectionChangedEvent<Device> event) {
+ editButton.setEnabled(!event.getSelection().isEmpty());
+ removeButton.setEnabled(!event.getSelection().isEmpty());
+
+ if (event.getSelection().isEmpty()) {
+ deviceHandler.onSelected(null);
+ } else {
+ deviceHandler.onSelected(event.getSelection().get(0));
+ }
+ }
+
+ public void load(Collection<Device> devices) {
+ store.addAll(devices);
+ }
+
+ @UiHandler("addButton")
+ public void onAddClicked(SelectEvent event) {
+ deviceHandler.onAdd();
+ }
+
+ public void add(Device device) {
+ store.add(store.size(), device);
+ }
+
+ @UiHandler("editButton")
+ public void onEditClicked(SelectEvent event) {
+ deviceHandler.onEdit(grid.getSelectionModel().getSelectedItem());
+ }
+
+ public void update(Device device) {
+ store.update(device);
+ }
+
+ @UiHandler("removeButton")
+ public void onRemoveClicked(SelectEvent event) {
+ deviceHandler.onRemove(grid.getSelectionModel().getSelectedItem());
+ }
+
+ public void remove(Device device) {
+ store.remove(device);
+ }
+
+}
diff --git a/src/org/traccar/web/client/view/DeviceView.ui.xml b/src/org/traccar/web/client/view/DeviceView.ui.xml
new file mode 100644
index 00000000..d9988d5e
--- /dev/null
+++ b/src/org/traccar/web/client/view/DeviceView.ui.xml
@@ -0,0 +1,46 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder
+ xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui"
+ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
+ xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"
+ xmlns:toolbar="urn:import:com.sencha.gxt.widget.core.client.toolbar"
+ xmlns:grid="urn:import:com.sencha.gxt.widget.core.client.grid"
+ xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button">
+
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="toolBarRowData">
+ <ui:attributes width="1" height="-1" />
+ </ui:with>
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="layoutData">
+ <ui:attributes width="1" height="1" />
+ </ui:with>
+
+ <ui:with type="com.sencha.gxt.data.shared.ListStore" field="store" />
+ <ui:with type="com.sencha.gxt.widget.core.client.grid.ColumnModel" field="columnModel" />
+
+ <ui:with type="com.sencha.gxt.widget.core.client.grid.GridView" field="view">
+ <ui:attributes stripeRows="true" autoFill="true" />
+ </ui:with>
+
+ <gxt:ContentPanel ui:field="contentPanel" headingText="Devices">
+ <container:VerticalLayoutContainer>
+
+ <container:child layoutData="{toolBarRowData}">
+ <toolbar:ToolBar>
+ <button:TextButton ui:field="addButton" text="Add" />
+ <button:TextButton ui:field="editButton" text="Edit" enabled="false" />
+ <button:TextButton ui:field="removeButton" text="Remove" enabled="false" />
+ <toolbar:FillToolItem />
+ <toolbar:SeparatorToolItem />
+ <button:TextButton ui:field="settingsButton" text="Settings" enabled="false" />
+ </toolbar:ToolBar>
+ </container:child>
+
+ <container:child layoutData="{layoutData}">
+ <grid:Grid ui:field="grid" store="{store}" cm="{columnModel}" view="{view}" />
+ </container:child>
+
+ </container:VerticalLayoutContainer>
+ </gxt:ContentPanel>
+
+</ui:UiBinder>
diff --git a/src/org/traccar/web/client/view/LoginDialog.java b/src/org/traccar/web/client/view/LoginDialog.java
new file mode 100644
index 00000000..44474179
--- /dev/null
+++ b/src/org/traccar/web/client/view/LoginDialog.java
@@ -0,0 +1,59 @@
+package org.traccar.web.client.view;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Widget;
+import com.sencha.gxt.widget.core.client.Window;
+import com.sencha.gxt.widget.core.client.event.SelectEvent;
+import com.sencha.gxt.widget.core.client.form.PasswordField;
+import com.sencha.gxt.widget.core.client.form.TextField;
+
+public class LoginDialog {
+
+ private static LoginDialogUiBinder uiBinder = GWT.create(LoginDialogUiBinder.class);
+
+ interface LoginDialogUiBinder extends UiBinder<Widget, LoginDialog> {
+ }
+
+ public interface LoginHandler {
+ public void onLogin(String login, String password);
+ public void onRegister(String login, String password);
+ }
+
+ private LoginHandler loginHandler;
+
+ @UiField
+ Window window;
+
+ @UiField
+ TextField login;
+
+ @UiField
+ PasswordField password;
+
+ public LoginDialog(LoginHandler loginHandler) {
+ this.loginHandler = loginHandler;
+ uiBinder.createAndBindUi(this);
+ }
+
+ public void show() {
+ window.show();
+ }
+
+ public void hide() {
+ window.hide();
+ }
+
+ @UiHandler("loginButton")
+ public void onLoginClicked(SelectEvent event) {
+ loginHandler.onLogin(login.getText(), password.getText());
+ }
+
+ @UiHandler("registerButton")
+ public void onRegisterClicked(SelectEvent event) {
+ loginHandler.onRegister(login.getText(), password.getText());
+ }
+
+}
diff --git a/src/org/traccar/web/client/view/LoginDialog.ui.xml b/src/org/traccar/web/client/view/LoginDialog.ui.xml
new file mode 100644
index 00000000..8c31b308
--- /dev/null
+++ b/src/org/traccar/web/client/view/LoginDialog.ui.xml
@@ -0,0 +1,40 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder
+ xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui"
+ xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"
+ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
+ xmlns:form="urn:import:com.sencha.gxt.widget.core.client.form"
+ xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button">
+
+ <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="verticalLayoutData">
+ <ui:attributes width="1" height="-1" />
+ </ui:with>
+
+ <gxt:Window ui:field="window" pixelSize="300, 130" modal="true" closable="false" headingText="Authentication" focusWidget="{loginButton}">
+ <container:VerticalLayoutContainer>
+ <container:child layoutData="{verticalLayoutData}">
+ <form:FieldLabel text="User">
+ <form:widget>
+ <form:TextField ui:field="login" />
+ </form:widget>
+ </form:FieldLabel>
+ </container:child>
+ <container:child layoutData="{verticalLayoutData}">
+ <form:FieldLabel text="Password">
+ <form:widget>
+ <form:PasswordField ui:field="password" />
+ </form:widget>
+ </form:FieldLabel>
+ </container:child>
+ </container:VerticalLayoutContainer>
+
+ <gxt:button>
+ <button:TextButton ui:field="loginButton" text="Login" />
+ </gxt:button>
+ <gxt:button>
+ <button:TextButton ui:field="registerButton" text="Register" />
+ </gxt:button>
+ </gxt:Window>
+
+</ui:UiBinder>
diff --git a/src/org/traccar/web/client/view/MapView.java b/src/org/traccar/web/client/view/MapView.java
new file mode 100644
index 00000000..af38c443
--- /dev/null
+++ b/src/org/traccar/web/client/view/MapView.java
@@ -0,0 +1,149 @@
+package org.traccar.web.client.view;
+
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.List;
+
+import org.gwtopenmaps.openlayers.client.Icon;
+import org.gwtopenmaps.openlayers.client.LonLat;
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.MapOptions;
+import org.gwtopenmaps.openlayers.client.MapWidget;
+import org.gwtopenmaps.openlayers.client.Marker;
+import org.gwtopenmaps.openlayers.client.Pixel;
+import org.gwtopenmaps.openlayers.client.Projection;
+import org.gwtopenmaps.openlayers.client.Size;
+import org.gwtopenmaps.openlayers.client.control.ScaleLine;
+import org.gwtopenmaps.openlayers.client.layer.Markers;
+import org.gwtopenmaps.openlayers.client.layer.OSM;
+import org.traccar.web.shared.model.Device;
+import org.traccar.web.shared.model.Position;
+
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.user.client.Command;
+import com.sencha.gxt.widget.core.client.ContentPanel;
+
+class MarkerIconFactory {
+
+ private static final Size iconSize = new Size(21, 25);
+ private static final Pixel iconOffset = new Pixel(-10.5f, -25.0f);
+
+ private static final String iconUrl = "http://www.openlayers.org/api/img/";
+ private static final String iconRed = iconUrl + "marker.png";
+ private static final String iconBlue = iconUrl + "marker-blue.png";
+ private static final String iconGreen = iconUrl + "marker-green.png";
+ private static final String iconGold = iconUrl + "marker-gold.png";
+
+ public static Icon getLocationIcon() {
+ return new Icon(iconRed, iconSize, iconOffset);
+ }
+
+ public static Icon getSelectedLocationIcon() {
+ return new Icon(iconGreen, iconSize, iconOffset);
+ }
+
+ public static Icon getArchiveIcon() {
+ return new Icon(iconBlue, iconSize, iconOffset);
+ }
+
+ public static Icon getSelectedArchiveIcon() {
+ return new Icon(iconGold, iconSize, iconOffset);
+ }
+
+}
+
+public class MapView {
+
+ private ContentPanel contentPanel;
+
+ public ContentPanel getView() {
+ return contentPanel;
+ }
+
+ private MapWidget mapWidget;
+ private Map map;
+ private Markers markerLayer;
+
+ private LonLat createPoint(double longitude, double latitude) {
+ LonLat point = new LonLat(longitude, latitude);
+ point.transform(new Projection("EPSG:4326").getProjectionCode(), map.getProjection());
+ return point;
+ }
+
+ public MapView() {
+ contentPanel = new ContentPanel();
+ contentPanel.setHeadingText("Map");
+
+ MapOptions defaultMapOptions = new MapOptions();
+ defaultMapOptions.setNumZoomLevels(16);
+
+ mapWidget = new MapWidget("100%", "100%", defaultMapOptions);
+ map = mapWidget.getMap();
+
+ OSM mapLayer = OSM.Mapnik("Mapnik");
+ mapLayer.setIsBaseLayer(true);
+
+ markerLayer = new Markers("Markers");
+
+ map.addLayer(mapLayer);
+ map.addLayer(markerLayer);
+ map.addControl(new ScaleLine());
+ map.setCenter(createPoint(30, 60), 1);
+
+ contentPanel.add(mapWidget);
+
+ // Update map size
+ contentPanel.addResizeHandler(new ResizeHandler() {
+ @Override
+ public void onResize(ResizeEvent event) {
+ Scheduler.get().scheduleDeferred(new Command() {
+ @Override
+ public void execute() {
+ map.updateSize();
+ }
+ });
+ }
+ });
+ }
+
+ private AbstractMap<Long, Marker> markerMap = new HashMap<Long, Marker>();
+ private Device selectedDevice;
+
+ public void showPositions(List<Position> positions) {
+ markerMap.clear();
+ markerLayer.clearMarkers();
+ for (Position position : positions) {
+ Marker marker = new Marker(
+ createPoint(position.getLongitude(), position.getLatitude()), MarkerIconFactory.getLocationIcon());
+ markerMap.put(position.getDevice().getId(), marker);
+ markerLayer.addMarker(marker);
+ }
+ if (selectedDevice != null) {
+ select(selectedDevice, false);
+ }
+ }
+
+ private void changeMarkerIcon(Marker marker, Icon icon) {
+ Marker newMarker = new Marker(marker.getLonLat(), icon);
+ markerLayer.removeMarker(marker);
+ markerLayer.addMarker(newMarker);
+ }
+
+ public void select(Device device, boolean center) {
+ if (selectedDevice != null) {
+ changeMarkerIcon(markerMap.get(selectedDevice.getId()), MarkerIconFactory.getLocationIcon());
+ selectedDevice = null;
+ }
+ if (device != null && markerMap.containsKey(device.getId())) {
+ Marker marker = markerMap.get(device.getId());
+ if (center) {
+ map.panTo(marker.getLonLat());
+ }
+ changeMarkerIcon(marker, MarkerIconFactory.getSelectedLocationIcon());
+ selectedDevice = device;
+ }
+ }
+
+}