changeset 208:83d42891fa09

Bug 3248: Log and SnapShot tab should have slide bar for selecting time range Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/72
author Yasumasa Suenaga <yasuenag@gmail.com>
date Fri, 13 Jan 2017 21:26:52 +0900
parents 92fe90d04400
children 03baa55848c2
files ChangeLog analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.css analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.fxml analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.css analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.fxml analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/threadrecorder.fxml
diffstat 9 files changed, 201 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jan 07 04:00:57 2017 +0900
+++ b/ChangeLog	Fri Jan 13 21:26:52 2017 +0900
@@ -1,3 +1,7 @@
+2017-01-13  Yasumasa Suenaga <yasuenag@gmail.com>
+
+	* Bug 3248: Log and SnapShot tab should have slide bar for selecting time range
+
 2017-01-07  KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
 
 	* Bug 3278: Fix to add G1 vtable offset correctly
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java	Fri Jan 13 21:26:52 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,15 +17,6 @@
  */
 package jp.co.ntt.oss.heapstats.plugin.builtin.log;
 
-import java.io.File;
-import java.net.URL;
-import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.stream.Collectors;
 import javafx.application.Platform;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
@@ -38,11 +29,11 @@
 import javafx.fxml.FXML;
 import javafx.fxml.Initializable;
 import javafx.scene.control.Button;
-import javafx.scene.control.ComboBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.SplitPane;
 import javafx.scene.control.TextField;
 import javafx.stage.FileChooser;
 import javafx.stage.FileChooser.ExtensionFilter;
-import javafx.util.converter.LocalDateTimeStringConverter;
 import jp.co.ntt.oss.heapstats.WindowController;
 import jp.co.ntt.oss.heapstats.container.log.ArchiveData;
 import jp.co.ntt.oss.heapstats.container.log.DiffData;
@@ -56,6 +47,13 @@
 import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils;
 import jp.co.ntt.oss.heapstats.utils.TaskAdapter;
 
+import java.io.File;
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
 /**
  * FXML Controller of LOG builtin plugin.
  *
@@ -70,10 +68,10 @@
     private LogDetailsController logDetailsController;
 
     @FXML
-    private ComboBox<LocalDateTime> startCombo;
+    private Label startTimeLabel;
 
     @FXML
-    private ComboBox<LocalDateTime> endCombo;
+    private Label endTimeLabel;
 
     @FXML
     private TextField logFileList;
@@ -81,6 +79,13 @@
     @FXML
     private Button okBtn;
 
+    @FXML
+    private SplitPane rangePane;
+
+    private ObjectProperty<LocalDateTime> rangeStart;
+
+    private ObjectProperty<LocalDateTime> rangeEnd;
+
     private List<LogData> logEntries;
 
     private List<DiffData> diffEntries;
@@ -88,17 +93,44 @@
     private ObjectProperty<ObservableList<ArchiveData>> archiveList;
 
     /**
+     * Update caption of label which represents time of selection.
+     *
+     * @param target Label compornent to draw.
+     * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 .
+     */
+    private void updateRangeLabel(Label target, double newValue){
+        if(!Optional.ofNullable(logEntries).map(List::isEmpty).orElse(true)){
+            LocalDateTime start = logEntries.get(0).getDateTime();
+            LocalDateTime end = logEntries.get(logEntries.size() - 1).getDateTime();
+            long diff = start.until(end, ChronoUnit.MILLIS);
+            LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS);
+
+            if(target == startTimeLabel){
+                rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS));
+            }
+            else{
+                rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS));
+            }
+
+            target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter()));
+        }
+    }
+
+    /**
      * Initializes the controller class.
      */
     @Override
     public void initialize(URL url, ResourceBundle rb) {
         super.initialize(url, rb);
 
-        LocalDateTimeStringConverter converter = new LocalDateTimeStringConverter(HeapStatsUtils.getDateTimeFormatter(), null);
-        startCombo.setConverter(converter);
-        endCombo.setConverter(converter);
+        logEntries = null;
+        diffEntries = null;
+        rangeStart = new SimpleObjectProperty<>();
+        rangeEnd = new SimpleObjectProperty<>();
 
-        okBtn.disableProperty().bind(startCombo.getSelectionModel().selectedIndexProperty().greaterThanOrEqualTo(endCombo.getSelectionModel().selectedIndexProperty()));
+        rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue()));
+        rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue()));
+
         archiveList = new SimpleObjectProperty<>(FXCollections.emptyObservableList());
         logResourcesController.archiveListProperty().bind(archiveList);
         logDetailsController.archiveListProperty().bind(archiveList);
@@ -111,18 +143,14 @@
      * @param parser Targeted LogFileParser.
      */
     private void onLogFileParserSucceeded(ParseLogFile parser) {
-        startCombo.getItems().clear();
-        endCombo.getItems().clear();
-
         logEntries = parser.getLogEntries();
         diffEntries = parser.getDiffEntries();
-        List<LocalDateTime> timeline = logEntries.stream()
-                .map(d -> d.getDateTime())
-                .collect(Collectors.toList());
-        startCombo.getItems().addAll(timeline);
-        startCombo.getSelectionModel().selectFirst();
-        endCombo.getItems().addAll(timeline);
-        endCombo.getSelectionModel().selectLast();
+
+        rangePane.getDividers().get(0).setPosition(0.0d);
+        rangePane.getDividers().get(1).setPosition(1.0d);
+
+        rangePane.setDisable(false);
+        okBtn.setDisable(false);
     }
 
     /**
@@ -170,8 +198,8 @@
     @FXML
     private void onOkClick(ActionEvent event) {
         /* Get range */
-        LocalDateTime start = startCombo.getValue();
-        LocalDateTime end = endCombo.getValue();
+        LocalDateTime start = rangeStart.getValue();
+        LocalDateTime end = rangeEnd.getValue();
 
         List<LogData> targetLogData = logEntries.parallelStream()
                 .filter(d -> ((d.getDateTime().compareTo(start) >= 0) && (d.getDateTime().compareTo(end) <= 0)))
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java	Fri Jan 13 21:26:52 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -21,13 +21,8 @@
 import java.net.URL;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import java.util.ResourceBundle;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -51,14 +46,7 @@
 import javafx.scene.chart.Axis;
 import javafx.scene.chart.NumberAxis;
 import javafx.scene.chart.XYChart;
-import javafx.scene.control.Button;
-import javafx.scene.control.ComboBox;
-import javafx.scene.control.Label;
-import javafx.scene.control.RadioButton;
-import javafx.scene.control.SelectionModel;
-import javafx.scene.control.Tab;
-import javafx.scene.control.TabPane;
-import javafx.scene.control.TextField;
+import javafx.scene.control.*;
 import javafx.scene.layout.AnchorPane;
 import javafx.scene.shape.Rectangle;
 import javafx.stage.FileChooser;
@@ -98,10 +86,13 @@
     private RefTreeController reftreeController;
 
     @FXML
-    private ComboBox<SnapShotHeader> startCombo;
+    private SplitPane rangePane;
 
     @FXML
-    private ComboBox<SnapShotHeader> endCombo;
+    private Label startTimeLabel;
+
+    @FXML
+    private Label endTimeLabel;
 
     @FXML
     private TextField snapshotList;
@@ -134,10 +125,40 @@
 
     private ObjectProperty<ObservableMap<LocalDateTime, List<ObjectData>>> topNList;
 
+    private List<SnapShotHeader> snapShotHeaders;
+
     private ObjectProperty<SnapShotHeader> currentSnapShotHeader;
 
     private LongProperty currentObjectTag;
 
+    private ObjectProperty<LocalDateTime> rangeStart;
+
+    private ObjectProperty<LocalDateTime> rangeEnd;
+
+    /**
+     * Update caption of label which represents time of selection.
+     *
+     * @param target Label compornent to draw.
+     * @param newValue Percentage of timeline. This value is between 0.0 and 1.0 .
+     */
+    private void updateRangeLabel(Label target, double newValue){
+        if(!Optional.ofNullable(snapShotHeaders).map(List::isEmpty).orElse(true)){
+            LocalDateTime start = snapShotHeaders.get(0).getSnapShotDate();
+            LocalDateTime end = snapShotHeaders.get(snapShotHeaders.size() - 1).getSnapShotDate();
+            long diff = start.until(end, ChronoUnit.MILLIS);
+            LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS);
+
+            if(target == startTimeLabel){
+                rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS));
+            }
+            else{
+                rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS));
+            }
+
+            target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter()));
+        }
+    }
+
     /**
      * Initializes the controller class.
      */
@@ -180,10 +201,12 @@
 
         snapshotMain.getSelectionModel().selectedItemProperty().addListener(this::onTabChanged);
 
-        startCombo.setConverter(new SnapShotHeaderConverter());
-        endCombo.setConverter(new SnapShotHeaderConverter());
+        snapShotHeaders = null;
+        rangeStart = new SimpleObjectProperty<>();
+        rangeEnd = new SimpleObjectProperty<>();
 
-        okBtn.disableProperty().bind(startCombo.getSelectionModel().selectedIndexProperty().greaterThanOrEqualTo(endCombo.getSelectionModel().selectedIndexProperty()));
+        rangePane.getDividers().get(0).positionProperty().addListener((v, o, n) -> updateRangeLabel(startTimeLabel, n.doubleValue()));
+        rangePane.getDividers().get(1).positionProperty().addListener((v, o, n) -> updateRangeLabel(endTimeLabel, n.doubleValue()));
 
         setOnWindowResize((v, o, n) -> Platform.runLater(() -> Stream.of(summaryController.getHeapChart(),
                 summaryController.getInstanceChart(),
@@ -209,6 +232,21 @@
     }
 
     /**
+     * onSucceeded event handler for ParseHeader.
+     *
+     * @param headers New SnapShotHeader list.
+     */
+    private void onSnapShotParserSucceeded(List<SnapShotHeader> headers) {
+        snapShotHeaders = headers;
+
+        rangePane.getDividers().get(0).setPosition(0.0d);
+        rangePane.getDividers().get(1).setPosition(1.0d);
+
+        rangePane.setDisable(false);
+        okBtn.setDisable(false);
+    }
+
+    /**
      * Event handler of SnapShot file button.
      *
      * @param event ActionEvent of this event.
@@ -235,13 +273,7 @@
             snapshotList.setText(files.stream().collect(Collectors.joining("; ")));
 
             TaskAdapter<ParseHeader> task = new TaskAdapter<>(new ParseHeader(files, HeapStatsUtils.getReplaceClassName(), true));
-            task.setOnSucceeded(evt -> {
-                ObservableList<SnapShotHeader> list = FXCollections.observableArrayList(task.getTask().getSnapShotList());
-                startCombo.setItems(list);
-                endCombo.setItems(list);
-                startCombo.getSelectionModel().selectFirst();
-                endCombo.getSelectionModel().selectLast();
-            });
+            task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList()));
             super.bindTask(task);
 
             Thread parseThread = new Thread(task);
@@ -257,9 +289,12 @@
      */
     @FXML
     private void onOkClick(ActionEvent event) {
-        int startIdx = startCombo.getSelectionModel().getSelectedIndex();
-        int endIdx = endCombo.getSelectionModel().getSelectedIndex();
-        currentTarget.set(FXCollections.observableArrayList(startCombo.getItems().subList(startIdx, endIdx + 1)));
+        /* Get range */
+        LocalDateTime start = rangeStart.getValue();
+        LocalDateTime end = rangeEnd.getValue();
+        currentTarget.set(FXCollections.observableArrayList(snapShotHeaders.stream()
+                                                                           .filter(d -> ((d.getSnapShotDate().compareTo(start) >= 0) && (d.getSnapShotDate().compareTo(end) <= 0)))
+                                                                           .collect(Collectors.toList())));
         currentClassNameSet.set(FXCollections.observableSet());
         summaryData.set(new SummaryData(currentTarget.get()));
 
@@ -317,7 +352,7 @@
         File csvFile = dialog.showSaveDialog(WindowController.getInstance().getOwner());
 
         if (csvFile != null) {
-            TaskAdapter<CSVDumpGC> task = new TaskAdapter<>(new CSVDumpGC(csvFile, isSelected ? currentTarget.get() : startCombo.getItems()));
+            TaskAdapter<CSVDumpGC> task = new TaskAdapter<>(new CSVDumpGC(csvFile, isSelected ? currentTarget.get() : snapShotHeaders));
             super.bindTask(task);
 
             Thread parseThread = new Thread(task);
@@ -344,7 +379,7 @@
 
         if (csvFile != null) {
             Predicate<? super ObjectData> filter = histogramController.getFilter();
-            TaskAdapter<CSVDumpHeap> task = new TaskAdapter<>(new CSVDumpHeap(csvFile, isSelected ? currentTarget.get() : startCombo.getItems(), isSelected ? filter : null, HeapStatsUtils.getReplaceClassName()));
+            TaskAdapter<CSVDumpHeap> task = new TaskAdapter<>(new CSVDumpHeap(csvFile, isSelected ? currentTarget.get() : snapShotHeaders, isSelected ? filter : null, HeapStatsUtils.getReplaceClassName()));
             super.bindTask(task);
 
             Thread parseThread = new Thread(task);
@@ -364,12 +399,7 @@
         snapshotList.setText((String) data);
 
         TaskAdapter<ParseHeader> task = new TaskAdapter<>(new ParseHeader(Arrays.asList((String) data), HeapStatsUtils.getReplaceClassName(), true));
-        task.setOnSucceeded(evt -> {
-            startCombo.setItems(FXCollections.observableArrayList(task.getTask().getSnapShotList()));
-            endCombo.setItems(FXCollections.observableArrayList(task.getTask().getSnapShotList()));
-            startCombo.getSelectionModel().selectFirst();
-            endCombo.getSelectionModel().selectLast();
-        });
+        task.setOnSucceeded(evt -> onSnapShotParserSucceeded(task.getTask().getSnapShotList()));
         super.bindTask(task);
 
         Thread parseThread = new Thread(task);
--- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java	Fri Jan 13 21:26:52 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2016 Yasumasa Suenaga
+ * Copyright (C) 2015-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -103,13 +103,13 @@
             LocalDateTime start = threadStatList.get(0).getTime();
             LocalDateTime end = threadStatList.get(threadStatList.size() - 1).getTime();
             long diff = start.until(end, ChronoUnit.MILLIS);
-            LocalDateTime newTime = start.plus((long)((double)diff * newValue), ChronoUnit.MILLIS);
+            LocalDateTime newTime = start.plus((long)(diff * (Math.round(newValue * 100.0d) / 100.0d)), ChronoUnit.MILLIS);
             
             if(target == startTimeLabel){
-                rangeStart.set(newTime);
+                rangeStart.set(newTime.truncatedTo(ChronoUnit.SECONDS));
             }
             else{
-                rangeEnd.set(newTime);
+                rangeEnd.set(newTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS));
             }
             
             target.setText(newTime.format(HeapStatsUtils.getDateTimeFormatter()));
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.css	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.css	Fri Jan 13 21:26:52 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -21,6 +21,13 @@
     Author     : Yasumasa Suenaga
 */
 
+.rect-range-selected {
+    -fx-background-color: lime;
+}
+
+.rect-range-unselected {
+    -fx-background-color: grey;
+}
 
 /* Java CPU */
 #javaCPUChart .series0 {
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.fxml	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.fxml	Fri Jan 13 21:26:52 2017 +0900
@@ -20,30 +20,42 @@
 
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
-<?import javafx.scene.control.ComboBox?>
 <?import javafx.scene.control.Label?>
+<?import javafx.scene.control.SplitPane?>
 <?import javafx.scene.control.Tab?>
 <?import javafx.scene.control.TabPane?>
 <?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.AnchorPane?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
 
-<VBox prefHeight="600.0" prefWidth="800.0" spacing="5.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController">
+<VBox prefHeight="600.0" prefWidth="800.0" spacing="5.0" stylesheets="@/jp/co/ntt/oss/heapstats/plugin/builtin/log/log.css" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController">
     <children>
       <HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0">
          <children>
               <Button mnemonicParsing="false" onAction="#onLogFileClick" text="%button.log" />
-              <TextField fx:id="logFileList" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="23.0" prefWidth="600.0" HBox.hgrow="ALWAYS" />
-              <Button fx:id="okBtn" mnemonicParsing="false" onAction="#onOkClick" text="OK" />
+              <TextField fx:id="logFileList" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" HBox.hgrow="ALWAYS" />
+              <Button fx:id="okBtn" disable="true" mnemonicParsing="false" onAction="#onOkClick" text="OK" />
          </children>
       </HBox>
       <HBox spacing="5.0">
          <children>
-              <Label text="%label.range" />
-              <ComboBox fx:id="startCombo" prefHeight="23.0" prefWidth="176.0" />
+             <Label text="%label.range" />
+             <SplitPane fx:id="rangePane" disable="true" dividerPositions="0.0, 1.0" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" prefHeight="25.0" HBox.hgrow="ALWAYS">
+                 <items>
+                     <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" SplitPane.resizableWithParent="false" />
+                     <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" styleClass="rect-range-selected" SplitPane.resizableWithParent="false" />
+                     <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" SplitPane.resizableWithParent="false" />
+                 </items>
+             </SplitPane>
+         </children>
+      </HBox>
+      <HBox alignment="CENTER" spacing="5.0">
+          <children>
+              <Label fx:id="startTimeLabel" />
               <Label text="-" />
-              <ComboBox fx:id="endCombo" prefHeight="23.0" prefWidth="176.0" />
-         </children>
+              <Label fx:id="endTimeLabel" />
+          </children>
       </HBox>
         <TabPane layoutX="14.0" layoutY="80.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="508.0" prefWidth="773.0" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
             <tabs>
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.css	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.css	Fri Jan 13 21:26:52 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2015 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -21,6 +21,14 @@
     Author     : Yasumasa Suenaga
 */
 
+.rect-range-selected {
+    -fx-background-color: lime;
+}
+
+.rect-range-unselected {
+    -fx-background-color: grey;
+}
+
 /* instance */
 #instanceChart .series0 {
     -fx-stroke: blue;
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.fxml	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.fxml	Fri Jan 13 21:26:52 2017 +0900
@@ -20,31 +20,47 @@
 
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
-<?import javafx.scene.control.ComboBox?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.control.RadioButton?>
+<?import javafx.scene.control.SplitPane?>
 <?import javafx.scene.control.Tab?>
 <?import javafx.scene.control.TabPane?>
 <?import javafx.scene.control.TextField?>
 <?import javafx.scene.control.ToggleGroup?>
+<?import javafx.scene.layout.AnchorPane?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
 
-<VBox prefHeight="600.0" prefWidth="800.0" spacing="5.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController">
+<VBox prefHeight="600.0" prefWidth="800.0" spacing="5.0" stylesheets="@/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/snapshot.css" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController">
     <children>
       <HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0">
          <children>
               <Button mnemonicParsing="false" onAction="#onSnapshotFileClick" text="%button.snapshot" />
-              <TextField fx:id="snapshotList" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="23.0" prefWidth="600.0" HBox.hgrow="ALWAYS" />
-              <Button fx:id="okBtn" mnemonicParsing="false" onAction="#onOkClick" text="OK" />
+              <TextField fx:id="snapshotList" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" HBox.hgrow="ALWAYS" />
+              <Button fx:id="okBtn" disable="true" mnemonicParsing="false" onAction="#onOkClick" text="OK" />
          </children>
       </HBox>
+        <HBox spacing="5.0">
+            <children>
+                <Label text="%label.range" />
+                <SplitPane fx:id="rangePane" disable="true" dividerPositions="0.0, 1.0" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefHeight="25.0" HBox.hgrow="ALWAYS">
+                    <items>
+                        <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" SplitPane.resizableWithParent="false" />
+                        <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" styleClass="rect-range-selected" SplitPane.resizableWithParent="false" />
+                        <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" SplitPane.resizableWithParent="false" />
+                    </items>
+                </SplitPane>
+            </children>
+        </HBox>
+        <HBox alignment="CENTER" spacing="5.0">
+            <children>
+                <Label fx:id="startTimeLabel" />
+                <Label text="-" />
+                <Label fx:id="endTimeLabel" />
+            </children>
+        </HBox>
       <HBox spacing="5.0">
          <children>
-              <Label text="%label.range" />
-              <ComboBox fx:id="startCombo" prefHeight="23.0" prefWidth="176.0" />
-              <Label text="-" />
-              <ComboBox fx:id="endCombo" prefHeight="23.0" prefWidth="176.0" />
               <Label text="%fxml.baseline" />
               <RadioButton mnemonicParsing="false" selected="true" text="%fxml.heapsize">
                   <toggleGroup>
@@ -54,7 +70,7 @@
               <RadioButton fx:id="radioInstance" mnemonicParsing="false" text="%fxml.instance" toggleGroup="$baseline" />
          </children>
       </HBox>
-        <TabPane fx:id="snapshotMain" prefHeight="508.0" prefWidth="773.0" tabClosingPolicy="UNAVAILABLE">
+        <TabPane fx:id="snapshotMain" prefHeight="508.0" prefWidth="773.0" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
             <tabs>
                 <Tab text="%tab.summary">
                     <content>
--- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/threadrecorder.fxml	Sat Jan 07 04:00:57 2017 +0900
+++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/threadrecorder.fxml	Fri Jan 13 21:26:52 2017 +0900
@@ -37,7 +37,7 @@
       <HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="10.0">
          <children>
               <Button mnemonicParsing="false" onAction="#onOpenBtnClick" text="%button.file" />
-              <TextField fx:id="fileNameBox" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
+              <TextField fx:id="fileNameBox" editable="false" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" HBox.hgrow="ALWAYS" />
               <Button fx:id="okBtn" disable="true" mnemonicParsing="false" onAction="#onOkBtnClick" text="OK" />
          </children>
       </HBox>
@@ -46,9 +46,9 @@
               <Label text="%label.timerange" />
               <SplitPane fx:id="rangePane" disable="true" dividerPositions="0.0, 1.0" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" prefHeight="25.0" HBox.hgrow="ALWAYS">
                   <items>
-                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" styleClass="rect-range-unselected" />
-                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="15.0" prefWidth="637.0" styleClass="rect-range-selected" />
-                      <AnchorPane prefHeight="158.0" prefWidth="65.0" styleClass="rect-range-unselected" />
+                      <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" />
+                      <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" styleClass="rect-range-selected" />
+                      <AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="0.0" prefWidth="0.0" styleClass="rect-range-unselected" />
                   </items>
               </SplitPane>
          </children>