1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package ohos.devtools.views.layout.chartview; 17 18 import com.intellij.ui.JBColor; 19 import com.intellij.ui.components.JBPanel; 20 import ohos.devtools.views.charts.utils.ChartUtils; 21 import ohos.devtools.views.layout.chartview.utils.OperationUtils; 22 23 import java.awt.Dimension; 24 import java.awt.Font; 25 import java.awt.Graphics; 26 import java.math.BigDecimal; 27 28 import static ohos.devtools.views.common.ColorConstants.TIMELINE_SCALE; 29 import static ohos.devtools.views.layout.chartview.utils.ChartViewConstants.TIMELINE_FONT_SIZE; 30 import static ohos.devtools.views.layout.chartview.utils.ChartViewConstants.TIMELINE_MARK_COUNTS; 31 32 /** 33 * User-defined timeline component of Profiler 34 * 35 * @since 2021/11/22 36 */ 37 public class ProfilerTimeline extends JBPanel { 38 /** 39 * Offset in point 40 */ 41 private static final int OFFSET_POINT = 5; 42 43 /** 44 * draw line top border 45 */ 46 private static final int TOP_BORDER = 6; 47 48 /** 49 * Offset in seconds 50 */ 51 private static final int OFFSET_SECONDS = 10; 52 53 /** 54 * Unit value of minutes converted to seconds 55 */ 56 private static final int SECONDS_UNIT = 60; 57 58 /** 59 * Conversion rate for time conversion 60 */ 61 private static final int CONVERSION_RATE = 1000; 62 63 /** 64 * The maximum time that can be displayed on the timeline 65 */ 66 private int maxDisplayTime; 67 68 /** 69 * Minimum interval between timescales 70 */ 71 private int minMarkInterval; 72 73 /** 74 * Right coordinates of timeline 75 */ 76 private int right; 77 78 /** 79 * Top coordinates of timeline 80 */ 81 private int top; 82 83 /** 84 * The start time of the timeline when drawing 85 */ 86 private int startTime; 87 88 /** 89 * The end time of the timeline when drawing 90 */ 91 private int endTime; 92 93 /** 94 * Coordinate axis X0 point when drawing timeline 95 * 96 * @see "It is the coordinate axis X0 point used in daily drawing, not the coordinate axis origin of Swing" 97 */ 98 private int x0; 99 100 /** 101 * Coordinate axis Y0 point when drawing timeline 102 * 103 * @see "It is the coordinate axis Y0 point used in daily drawing, not the coordinate axis origin of Swing" 104 */ 105 private int y0; 106 107 /** 108 * The start time offset when the scale is drawn when the timeline is full of panels 109 */ 110 private int offsetTime = 0; 111 112 /** 113 * The x-axis is the coordinate of the starting plot 114 * 115 * @see "The dynamic timeline and chart appear from right to left" 116 */ 117 private int startCoordinate; 118 119 /** 120 * Number of pixels per X-axis time unit 121 */ 122 private BigDecimal pixelPerTime; 123 124 /** 125 * Constructor 126 * 127 * @param width Width of timeline 128 * @param height Height of timeline 129 */ ProfilerTimeline(int width, int height)130 public ProfilerTimeline(int width, int height) { 131 this.setPreferredSize(new Dimension(width, height)); 132 } 133 134 /** 135 * paintComponent 136 * 137 * @param graphics graphics 138 */ 139 @Override paintComponent(Graphics graphics)140 protected void paintComponent(Graphics graphics) { 141 super.paintComponent(graphics); 142 initPoints(); 143 drawAxis(graphics); 144 } 145 146 /** 147 * Initialization of points and scale information 148 */ initPoints()149 private void initPoints() { 150 // Determine the origin of the drawn axis 151 x0 = this.getX(); 152 right = x0 + this.getWidth(); 153 top = this.getY(); 154 y0 = top + this.getHeight() - 1; 155 if (right == 0 || maxDisplayTime == 0) { 156 return; 157 } 158 // Calculate how many pixels a time unit takes 159 pixelPerTime = OperationUtils.divide(right, maxDisplayTime); 160 // If the current time is greater than the maximum time, the offset is calculated 161 if (endTime > maxDisplayTime && minMarkInterval != 0) { 162 startCoordinate = x0; 163 // Determine if there is offset time 164 if (endTime % minMarkInterval == 0) { 165 offsetTime = 0; 166 } else { 167 // If the remainder of current time and minMarkInterval is not 0, 168 // the offset is calculated: minimum interval - current time% minimum interval 169 offsetTime = minMarkInterval - endTime % minMarkInterval; 170 } 171 } else { 172 // If the current time is less than the maximum time, 173 // the timeline needs to be drawn from the middle with an offset of 0 174 offsetTime = 0; 175 startCoordinate = x0 + right - OperationUtils.multiply(pixelPerTime, endTime); 176 } 177 } 178 179 /** 180 * Draw the coordinate axis of the timeline 181 * 182 * @param graphics Graphics 183 */ drawAxis(Graphics graphics)184 private void drawAxis(Graphics graphics) { 185 graphics.setColor(TIMELINE_SCALE); 186 // Draw the vertical line to the left of the timeline 187 graphics.drawLine(x0, top, x0, y0); 188 // Draw a horizontal line at the bottom of the timeline 189 graphics.drawLine(x0, y0, right, y0); 190 // The time line is drawn from offset time (the scale is drawn in fact) 191 for (int drawTime = offsetTime; drawTime <= maxDisplayTime; drawTime += minMarkInterval) { 192 int pointX = startCoordinate + ChartUtils.multiply(pixelPerTime, drawTime); 193 // Calculate the actual time to show 194 int showTime = startTime + drawTime; 195 int result = (showTime / minMarkInterval) % TIMELINE_MARK_COUNTS; 196 // Draw coordinate axis numbers and large scale every minMarkInterval 197 graphics.setColor(TIMELINE_SCALE); 198 if (result == 0) { 199 // Draw a long scale 200 graphics.drawLine(pointX, y0, pointX, top); 201 graphics.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, TIMELINE_FONT_SIZE)); 202 // Time after conversion 203 String str = millisecondToTime(showTime); 204 graphics.setColor(JBColor.foreground()); 205 graphics.drawString(str, pointX + OFFSET_POINT, y0 - OFFSET_SECONDS); 206 } else { 207 graphics.drawLine(pointX, top, pointX, top + TOP_BORDER); 208 } 209 } 210 } 211 212 /** 213 * Draw the coordinate number of the timeline 214 * 215 * @param time time 216 * @return String 217 */ millisecondToTime(int time)218 private String millisecondToTime(int time) { 219 String timeStr; 220 int hour; 221 int minute; 222 int second; 223 int millisecond; 224 if (time <= 0) { 225 return "0"; 226 } else { 227 second = time / CONVERSION_RATE; 228 minute = second / SECONDS_UNIT; 229 millisecond = time % CONVERSION_RATE; 230 if (second < SECONDS_UNIT) { 231 timeStr = secondFormat(second) + "." + millisecondFormat(millisecond); 232 } else if (minute < SECONDS_UNIT) { 233 second = second % SECONDS_UNIT; 234 timeStr = secondFormat(minute) + ":" + secondFormat(second) + "." + millisecondFormat(millisecond); 235 } else { 236 hour = minute / SECONDS_UNIT; 237 minute = minute % SECONDS_UNIT; 238 int num3600 = SECONDS_UNIT * SECONDS_UNIT; 239 second = second - hour * num3600 - minute * SECONDS_UNIT; 240 timeStr = secondFormat(hour) + ":" + secondFormat(minute) + ":" + secondFormat(second) + "." 241 + millisecondFormat(millisecond); 242 } 243 } 244 return timeStr; 245 } 246 247 /** 248 * Format conversion of hour, minute and second 249 * 250 * @param secondTime secondTime 251 * @return String 252 */ secondFormat(int secondTime)253 private String secondFormat(int secondTime) { 254 String formatTime; 255 if (secondTime == 0) { 256 formatTime = "00"; 257 } else if (secondTime > 0 && secondTime < OFFSET_SECONDS) { 258 formatTime = Integer.toString(secondTime); 259 } else { 260 formatTime = "" + secondTime; 261 } 262 return formatTime; 263 } 264 265 /** 266 * Millisecond format conversion 267 * 268 * @param millisecondTime millisecondTime 269 * @return String 270 */ millisecondFormat(int millisecondTime)271 private String millisecondFormat(int millisecondTime) { 272 String formatTime; 273 if (millisecondTime == 0) { 274 formatTime = "000"; 275 } else if (millisecondTime > 0 && millisecondTime < OFFSET_SECONDS) { 276 formatTime = Integer.toString(millisecondTime); 277 } else if (millisecondTime >= OFFSET_SECONDS && millisecondTime < OFFSET_SECONDS * OFFSET_SECONDS) { 278 formatTime = Integer.toString(millisecondTime); 279 } else { 280 formatTime = "" + millisecondTime; 281 } 282 return formatTime; 283 } 284 285 /** 286 * setMaxDisplayTime 287 * 288 * @param maxDisplayTime maxDisplayTime 289 */ setMaxDisplayTime(int maxDisplayTime)290 public void setMaxDisplayTime(int maxDisplayTime) { 291 this.maxDisplayTime = maxDisplayTime; 292 } 293 294 /** 295 * setMinMarkInterval 296 * 297 * @param minMarkInterval minMarkInterval 298 */ setMinMarkInterval(int minMarkInterval)299 public void setMinMarkInterval(int minMarkInterval) { 300 this.minMarkInterval = minMarkInterval; 301 } 302 303 /** 304 * getStartTime 305 * 306 * @return int 307 */ getStartTime()308 public int getStartTime() { 309 return startTime; 310 } 311 312 /** 313 * setStartTime 314 * 315 * @param startTime startTime 316 */ setStartTime(int startTime)317 public void setStartTime(int startTime) { 318 this.startTime = startTime; 319 } 320 321 /** 322 * getEndTime 323 * 324 * @return int 325 */ getEndTime()326 public int getEndTime() { 327 return endTime; 328 } 329 330 /** 331 * setEndTime 332 * 333 * @param endTime endTime 334 */ setEndTime(int endTime)335 public void setEndTime(int endTime) { 336 this.endTime = endTime; 337 } 338 } 339