001package com.ganteater.ae.desktop.view; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.Font; 007import java.awt.Toolkit; 008import java.awt.datatransfer.Clipboard; 009import java.awt.datatransfer.StringSelection; 010import java.awt.event.ActionEvent; 011import java.awt.event.ActionListener; 012import java.awt.event.KeyAdapter; 013import java.awt.event.KeyEvent; 014import java.awt.event.MouseAdapter; 015import java.awt.event.MouseEvent; 016import java.io.IOException; 017import java.io.PrintWriter; 018import java.io.StringWriter; 019import java.text.SimpleDateFormat; 020import java.util.Date; 021import java.util.HashMap; 022import java.util.Map; 023 024import javax.swing.JButton; 025import javax.swing.JCheckBox; 026import javax.swing.JComboBox; 027import javax.swing.JLabel; 028import javax.swing.JMenuItem; 029import javax.swing.JPanel; 030import javax.swing.JPopupMenu; 031import javax.swing.JScrollBar; 032import javax.swing.JScrollPane; 033import javax.swing.JTable; 034import javax.swing.JTextArea; 035import javax.swing.JTextField; 036import javax.swing.ListSelectionModel; 037import javax.swing.UIDefaults; 038import javax.swing.table.AbstractTableModel; 039import javax.swing.table.TableCellRenderer; 040import javax.swing.table.TableColumnModel; 041 042import org.apache.commons.lang.ObjectUtils; 043import org.apache.commons.lang.StringUtils; 044 045import com.ganteater.ae.AELogRecord; 046import com.ganteater.ae.AEWorkspace; 047import com.ganteater.ae.desktop.editor.TaskEditor; 048import com.ganteater.ae.desktop.ui.AEFrame; 049import com.ganteater.ae.desktop.ui.LogFrame; 050import com.ganteater.ae.desktop.ui.TextPrompt; 051import com.ganteater.ae.desktop.util.UIUtils; 052 053public class ListLogPresenter extends LogPresenter implements ActionListener { 054 055 private static final long serialVersionUID = 1L; 056 057 private static final Font LOG_FONT = new Font("Monospaced", Font.PLAIN, 11); 058 059 private LogList logList; 060 061 private JComboBox<String> levelBox = new JComboBox<String>(new String[] { "Debug", "Info", "Warning", "Error" }); 062 private JCheckBox autoformatBtn = new JCheckBox("Auto format"); 063 private JTextField find = new JTextField(8); 064 065 private JTable fLogTextArea = new LogTable(); 066 067 private JCheckBox fLogToEnd = new JCheckBox("Auto scrolling"); 068 private JCheckBox fDeleteOldRec = new JCheckBox("Limit 50 records"); 069 070 private JScrollPane fLogScrollPanel = new JScrollPane(); 071 072 private TaskEditor fTaskEditor; 073 074 private JLabel messageBar = new JLabel(); 075 076 private boolean mainLog; 077 078 public ListLogPresenter(TaskEditor aTaskEditor, String aName) { 079 this(aTaskEditor, aName, false); 080 } 081 082 public ListLogPresenter(TaskEditor aTaskEditor, String aName, boolean mainLog) { 083 super(aName, aTaskEditor.getConfigNode()); 084 this.mainLog = mainLog; 085 fTaskEditor = aTaskEditor; 086 createPanel(); 087 } 088 089 private void copyToClipboard() { 090 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 091 int minSelectionIndex = selectionModel.getMinSelectionIndex(); 092 int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); 093 094 StringBuilder msgBuilder = new StringBuilder(); 095 for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) { 096 097 boolean selectedIndex = selectionModel.isSelectedIndex(i); 098 099 if (selectedIndex) { 100 if (i != minSelectionIndex) { 101 msgBuilder.append("\n---------------------\n"); 102 } 103 104 LogRecord text = logList.get(i); 105 TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, getName()); 106 logPanel.info(text, fTaskEditor.getTaskProcessor()); 107 boolean selected = autoformatBtn.isSelected(); 108 if (selected) { 109 logPanel.format(); 110 } 111 112 String msg = ((JTextArea) logPanel.getLogTextArea()).getText(); 113 msgBuilder.append(msg); 114 } 115 } 116 117 StringSelection stringSelection = new StringSelection(msgBuilder.toString()); 118 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 119 clipboard.setContents(stringSelection, null); 120 } 121 122 public void actionPerformed(ActionEvent e) { 123 if (e != null && e.getSource() == fLogToEnd) { 124 AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.LogToEnd", 125 fLogToEnd.isSelected() ? "true" : "false"); 126 return; 127 } 128 if (e != null && e.getSource() == fDeleteOldRec) { 129 AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.DeleteOldRec", 130 fDeleteOldRec.isSelected() ? "true" : "false"); 131 return; 132 } 133 } 134 135 @Override 136 public Object info(Object o) { 137 appendText(2, o); 138 return super.info(o); 139 } 140 141 @Override 142 public Object debug(Object o) { 143 if (o instanceof Throwable) { 144 StringWriter theStringWriter = new StringWriter(); 145 ((Throwable) o).printStackTrace(new PrintWriter(theStringWriter)); 146 appendText(3, theStringWriter.toString()); 147 return o; 148 149 } else { 150 appendText(3, o); 151 return super.debug(o); 152 } 153 } 154 155 @Override 156 public Object debug(Object o, Throwable aThrowable) { 157 appendText(3, o); 158 159 StringWriter theStringWriter = new StringWriter(); 160 aThrowable.printStackTrace(new PrintWriter(theStringWriter)); 161 appendText(3, theStringWriter.toString()); 162 163 return super.error(o, aThrowable); 164 } 165 166 @Override 167 public Object error(Object o) { 168 appendText(0, o); 169 return super.error(o); 170 } 171 172 @Override 173 public Object error(Object o, Throwable aThrowable) { 174 StringWriter theStringWriter = new StringWriter(); 175 aThrowable.printStackTrace(new PrintWriter(theStringWriter)); 176 appendText(0, ObjectUtils.toString(o) + "\n" + theStringWriter.toString()); 177 return super.error(o, aThrowable); 178 } 179 180 @Override 181 public Object warn(Object o) { 182 appendText(1, o); 183 return super.error(o); 184 } 185 186 void appendText(int logLevel, Object o) { 187 logList.add(new LogRecord(logLevel, o), fDeleteOldRec.isSelected()); 188 189 int level = 3 - levelBox.getSelectedIndex(); 190 if (logLevel <= level) { 191 fLogTextArea.revalidate(); 192 fLogScrollPanel.repaint(); 193 } 194 195 if (fLogToEnd.isSelected()) { 196 JScrollBar verticalScrollBar = fLogScrollPanel.getVerticalScrollBar(); 197 verticalScrollBar.setValue(verticalScrollBar.getMaximum()); 198 } 199 } 200 201 public void warn(Object o, Throwable aThrowable) { 202 appendText(1, o); 203 StringWriter theStringWriter = new StringWriter(); 204 aThrowable.printStackTrace(new PrintWriter(theStringWriter)); 205 appendText(1, theStringWriter.toString()); 206 super.error(o, aThrowable); 207 } 208 209 public void createPanel() { 210 String defaultUserConfiguration = AEWorkspace.getInstance().getDefaultUserConfiguration(".defaultLogLevel", 211 "2"); 212 int defLevel = Integer.parseInt(defaultUserConfiguration); 213 logList = new LogList(defLevel); 214 215 logList.setLevel(defLevel); 216 levelBox.setSelectedIndex(3 - defLevel); 217 levelBox.setFont(LOG_FONT); 218 219 fLogTextArea.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 220 fLogTextArea.setCellSelectionEnabled(true); 221 222 fLogTextArea.addKeyListener(new KeyAdapter() { 223 @Override 224 public void keyPressed(KeyEvent e) { 225 if (e.isControlDown() && e.isShiftDown() && e.getKeyCode() == KeyEvent.VK_C) { 226 copyToClipboard(); 227 } 228 } 229 230 }); 231 232 fLogTextArea.addMouseListener(new MouseAdapter() { 233 234 public void mouseClicked(MouseEvent e) { 235 if (e.getButton() == 1) { 236 237 if (e.getClickCount() == 2) { 238 LogRecord logRecord = selectAndGetRecord(e); 239 if (!e.isControlDown()) { 240 openLogRecord(logRecord); 241 242 } else { 243 openIn(logRecord); 244 } 245 } 246 247 } else if (e.getButton() == 3) { 248 LogRecord logRecord = selectAndGetRecord(e); 249 250 final JPopupMenu popup = new JPopupMenu(); 251 JMenuItem jMenuItem = new JMenuItem("Open"); 252 jMenuItem.addActionListener(event -> openLogRecord(logRecord)); 253 popup.add(jMenuItem); 254 jMenuItem = new JMenuItem("System TextEditor"); 255 jMenuItem.addActionListener(event -> openIn(logRecord)); 256 popup.add(jMenuItem); 257 jMenuItem = new JMenuItem("Copy"); 258 jMenuItem.addActionListener(event -> copyToClipboard()); 259 popup.add(jMenuItem); 260 jMenuItem = new JMenuItem("Save"); 261 jMenuItem.addActionListener(event -> { 262 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 263 int minSelectionIndex = selectionModel.getMinSelectionIndex(); 264 int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); 265 266 for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) { 267 268 boolean selectedIndex = selectionModel.isSelectedIndex(i); 269 270 if (selectedIndex) { 271 LogRecord rec = logList.get(i); 272 TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, rec.getSource()); 273 274 logPanel.info(rec, fTaskEditor.getTaskProcessor()); 275 boolean selected = autoformatBtn.isSelected(); 276 if (selected) { 277 logPanel.format(); 278 } 279 logPanel.saveToFile(); 280 } 281 } 282 }); 283 popup.add(jMenuItem); 284 popup.show(e.getComponent(), e.getX(), e.getY()); 285 } 286 } 287 288 private LogRecord selectAndGetRecord(MouseEvent e) { 289 int r = fLogTextArea.rowAtPoint(e.getPoint()); 290 if (r >= 0 && r < fLogTextArea.getRowCount()) { 291 fLogTextArea.setRowSelectionInterval(r, r); 292 } else { 293 fLogTextArea.clearSelection(); 294 } 295 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 296 int anchorSelectionIndex = selectionModel.getAnchorSelectionIndex(); 297 return logList.get(anchorSelectionIndex); 298 } 299 300 private void openLogRecord(LogRecord logRecord) { 301 String source = logRecord.getSource(); 302 source = StringUtils.defaultString(source); 303 TextLogPresenter logPanel = new TextLogPresenter(fTaskEditor, source); 304 LogFrame logFrame = new LogFrame(logPanel); 305 logFrame.setVisible(true); 306 logFrame.setAlwaysOnTop(true); 307 logFrame.setAlwaysOnTop(false); 308 309 logFrame.showText(logRecord, fTaskEditor.getTaskProcessor(), autoformatBtn.isSelected()); 310 } 311 312 private void openIn(LogRecord text) { 313 try { 314 TextLogPresenter.openFile(text, text.getType()); 315 } catch (IOException e1) { 316 e1.printStackTrace(); 317 } 318 } 319 }); 320 321 TableColumnModel columnModel = fLogTextArea.getColumnModel(); 322 columnModel.getColumn(0).setMinWidth(0); 323 columnModel.getColumn(0).setPreferredWidth(8); 324 columnModel.getColumn(0).setMaxWidth(100); 325 columnModel.getColumn(0).setHeaderValue("Time"); 326 columnModel.getColumn(1).setHeaderValue("Message"); 327 columnModel.getColumn(2).setHeaderValue("Size, B"); 328 columnModel.getColumn(2).setMinWidth(0); 329 columnModel.getColumn(2).setMaxWidth(150); 330 columnModel.getColumn(2).setPreferredWidth(8); 331 332 fLogScrollPanel.getVerticalScrollBar().setDoubleBuffered(true); 333 334 add(fLogScrollPanel, BorderLayout.CENTER); 335 336 JPanel theFindPanel = new JPanel(); 337 338 theFindPanel.add(find); 339 new TextPrompt("Search", find); 340 341 find.addKeyListener(new KeyAdapter() { 342 @Override 343 public void keyPressed(KeyEvent e) { 344 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 345 find.setText(""); 346 } 347 fLogTextArea.repaint(); 348 } 349 }); 350 JLabel comp = new JLabel("Level:"); 351 comp.setFont(LOG_FONT); 352 theFindPanel.add(comp); 353 levelBox.addActionListener(e -> { 354 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 355 selectionModel.clearSelection(); 356 int logLevel = 3 - levelBox.getSelectedIndex(); 357 AEWorkspace.getInstance().setDefaultUserConfiguration(".defaultLogLevel", Integer.toString(logLevel)); 358 logList.setLevel(logLevel); 359 fLogTextArea.revalidate(); 360 fLogScrollPanel.revalidate(); 361 }); 362 theFindPanel.add(levelBox); 363 364 JButton theClearButton = new JButton(AEFrame.getIcon("clean.png")); 365 theClearButton.setFont(LOG_FONT); 366 theClearButton.addActionListener(e -> { 367 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 368 selectionModel.clearSelection(); 369 logList.clear(); 370 fLogTextArea.revalidate(); 371 fLogScrollPanel.revalidate(); 372 }); 373 374 theFindPanel.add(theClearButton); 375 376 if (!mainLog) { 377 JButton theCloseButton = new JButton(AEFrame.getIcon("close.png")); 378 theCloseButton.setFont(LOG_FONT); 379 theCloseButton.addActionListener(e -> fTaskEditor.removeActivePresentationPanel()); 380 381 theFindPanel.add(theCloseButton); 382 } 383 384 JPanel theLogControlPanel = new JPanel(new BorderLayout()); 385 theLogControlPanel.add(theFindPanel, BorderLayout.EAST); 386 JPanel theLogLevelPanel = new JPanel(); 387 388 theLogControlPanel.add(theLogLevelPanel, BorderLayout.WEST); 389 390 add(theLogControlPanel, BorderLayout.NORTH); 391 392 JPanel panel = new JPanel(new BorderLayout()); 393 JPanel panel4 = new JPanel(); 394 395 panel4.add(fLogToEnd); 396 panel4.add(fDeleteOldRec); 397 398 autoformatBtn.setSelected( 399 "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.AutoFormat", "false"))); 400 autoformatBtn.addChangeListener(e -> AEWorkspace.getInstance().setDefaultUserConfiguration(".Edit.AutoFormat", 401 autoformatBtn.isSelected() ? "true" : "false")); 402 403 panel4.add(autoformatBtn); 404 405 panel4.add(this.messageBar); 406 panel.add(panel4, BorderLayout.WEST); 407 add(panel, BorderLayout.SOUTH); 408 fLogToEnd.setSelected( 409 "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.LogToEnd", "true"))); 410 fLogToEnd.addActionListener(this); 411 412 fDeleteOldRec.setSelected( 413 "true".equals(AEWorkspace.getInstance().getDefaultUserConfiguration(".Edit.DeleteOldRec", "true"))); 414 fDeleteOldRec.addActionListener(this); 415 416 fLogTextArea.setFont(LOG_FONT); 417 fLogScrollPanel.getViewport().add(fLogTextArea); 418 } 419 420 public ListLogPresenter copyAndClean() { 421 ListLogPresenter listLogPresenter = new ListLogPresenter(fTaskEditor, getName()); 422 listLogPresenter.setLogList(logList); 423 String defaultUserConfiguration = AEWorkspace.getInstance().getDefaultUserConfiguration(".defaultLogLevel", 424 "2"); 425 int defLevel = Integer.parseInt(defaultUserConfiguration); 426 logList = new LogList(defLevel); 427 fLogTextArea.revalidate(); 428 fLogScrollPanel.repaint(); 429 430 return listLogPresenter; 431 } 432 433 private void setLogList(LogList logList) { 434 this.logList = logList; 435 fLogToEnd.setVisible(false); 436 fDeleteOldRec.setVisible(false); 437 } 438 439 private final class LogTable extends JTable { 440 private static final long serialVersionUID = 1L; 441 442 private LogTable() { 443 super(new LogTableModel()); 444 } 445 446 @Override 447 public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { 448 Component c = super.prepareRenderer(renderer, row, column); 449 450 UIDefaults defaults = javax.swing.UIManager.getDefaults(); 451 Color background = defaults.getColor("List.background"); 452 Color foreground = defaults.getColor("List.foreground"); 453 454 LogRecord logRecord = logList.get(row); 455 int level = logRecord.getLogLevel(); 456 457 switch (column) { 458 case 0: 459 long time = logRecord.getTime(); 460 double a = time / 500.0; 461 double b = (Math.sin(a) + 1); 462 float r = 1 - (float) b / 2; 463 background = new Color(r, r, r); 464 foreground = UIUtils.getContrastColor(background); 465 break; 466 467 case 2: 468 String text = logRecord.toString(); 469 if (text != null) { 470 a = (double) (text.length()) / 100; 471 if (a < 1) { 472 a = 1; 473 } 474 b = Math.log(a) * 8; 475 b = (100 - b / 2) / 100; 476 background = new Color((float) b, (float) b, (float) b); 477 } else { 478 background = Color.YELLOW; 479 } 480 foreground = UIUtils.getContrastColor(background); 481 break; 482 } 483 484 if (column == 1) { 485 switch (level) { 486 case 0: 487 foreground = tuneColorByBackground(Color.RED); 488 break; 489 case 1: 490 foreground = tuneColorByBackground(Color.BLUE); 491 break; 492 case 2: 493 foreground = tuneColorByBackground(Color.GREEN); 494 break; 495 case 3: 496 break; 497 default: 498 } 499 500 String text = find.getText(); 501 if (StringUtils.isNotBlank(text)) { 502 String message = ObjectUtils.toString(logRecord.getMessage()); 503 if (logRecord.getSource() != null) { 504 message = logRecord.getSource() + " = " + message; 505 } 506 int indexOf = StringUtils.indexOfIgnoreCase(message, text); 507 if (indexOf >= 0) { 508 background = Color.yellow; 509 foreground = UIUtils.getContrastColor(background); 510 } 511 } 512 } 513 514 ListSelectionModel selectionModel = fLogTextArea.getSelectionModel(); 515 516 if (selectionModel.isSelectedIndex(row) && column == 1) { 517 c.setBackground(foreground); 518 if (background == Color.yellow) { 519 c.setForeground(background); 520 } else { 521 c.setForeground(UIUtils.getContrastColor(foreground)); 522 } 523 } else { 524 c.setBackground(background); 525 c.setForeground(foreground); 526 } 527 528 return c; 529 } 530 531 private Color tuneColorByBackground(Color fgc) { 532 UIDefaults defaults = javax.swing.UIManager.getDefaults(); 533 Color background = defaults.getColor("List.background"); 534 535 int alpha = (int) (0.2126 * background.getRed() + 0.7152 * background.getGreen() 536 + 0.0722 * background.getBlue()); 537 538 if (alpha < 60) { 539 int colorLevel = 140; 540 fgc = new Color(fgc.getRed() == 0 ? colorLevel : fgc.getRed(), 541 fgc.getGreen() == 0 ? colorLevel : fgc.getGreen(), 542 fgc.getBlue() == 0 ? colorLevel : fgc.getBlue()); 543 } 544 545 if (alpha > 200) { 546 fgc = fgc.darker().darker(); 547 } 548 return fgc; 549 } 550 } 551 552 private final class LogTableModel extends AbstractTableModel { 553 private static final long serialVersionUID = 1L; 554 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH.mm.ss,SSS"); 555 556 public int getColumnCount() { 557 return 3; 558 } 559 560 public int getRowCount() { 561 return logList.getSize(); 562 } 563 564 public Object getValueAt(int rowIndex, int columnIndex) { 565 566 String text = logList.get(rowIndex).toString(); 567 568 switch (columnIndex) { 569 case 0: 570 return simpleDateFormat.format(logList.get(rowIndex).time); 571 case 1: 572 if (logList.get(rowIndex).getSource() != null) { 573 if (text != null) { 574 return logList.get(rowIndex).getSource() + " = " + text; 575 } else { 576 return logList.get(rowIndex).getSource() + " is not defined."; 577 } 578 } else { 579 return text; 580 } 581 case 2: 582 return text == null ? 0 : text.length(); 583 } 584 return null; 585 } 586 } 587 588 public static class LogRecord { 589 590 private Object message; 591 592 private int logLevel; 593 594 private Date time; 595 596 private String type; 597 598 private String source; 599 600 public LogRecord(int logLevel, Object o) { 601 if (o instanceof Map) { 602 Map map = (Map) o; 603 message = new HashMap(); 604 ((Map) message).putAll(map); 605 606 } else if (o instanceof AELogRecord) { 607 type = ((AELogRecord) o).getType(); 608 message = ((AELogRecord) o).getMessage(); 609 610 if (message instanceof Map) { 611 Map map = (Map) message; 612 message = new HashMap(); 613 ((Map) message).putAll(map); 614 } 615 616 source = ((AELogRecord) o).getVarName(); 617 } else { 618 message = o; 619 } 620 this.logLevel = logLevel; 621 this.time = new Date(); 622 } 623 624 public Object getMessage() { 625 return message; 626 } 627 628 public long getTime() { 629 return time.getTime(); 630 } 631 632 public int getLogLevel() { 633 return logLevel; 634 } 635 636 public String getType() { 637 return type; 638 } 639 640 @Override 641 public String toString() { 642 if (message instanceof byte[]) { 643 return new String((byte[]) message); 644 } 645 return ObjectUtils.toString(message, null); 646 } 647 648 public String getSource() { 649 return source; 650 } 651 652 public void setSource(String source) { 653 this.source = source; 654 } 655 656 public String getText() { 657 return ObjectUtils.toString(message); 658 } 659 660 } 661 662 @Override 663 public boolean isFilterEnabled() { 664 return log.isFilterEnabled(); 665 } 666 667 @Override 668 public String filter(Object message) { 669 return log.filter(message); 670 } 671 672}