3131import net .miginfocom .swing .MigLayout ;
3232import org .scijava .Context ;
3333import org .scijava .event .EventHandler ;
34+ import org .scijava .log .Logger ;
3435import org .scijava .task .Task ;
3536import org .scijava .task .event .TaskEvent ;
37+ import org .scijava .ui .swing .console .LoggingPanel ;
3638
3739import javax .swing .JComponent ;
3840import javax .swing .JFrame ;
5860import java .awt .Shape ;
5961import java .awt .event .MouseAdapter ;
6062import java .awt .event .MouseEvent ;
63+ import java .awt .event .WindowAdapter ;
64+ import java .awt .event .WindowEvent ;
6165import java .awt .geom .Arc2D ;
6266import java .awt .geom .Area ;
6367import java .awt .geom .Ellipse2D ;
9599 * Second column:
96100 * </p>
97101 * <ul>
102+ * <li>a log icon, clickable and which opens a window showing the output
103+ * logged to the displayed task's {@link Task#log() logger} from then
104+ * on</li>
105+ * </ul>
106+ * <p>
107+ * Third column:
108+ * </p>
109+ * <ul>
98110 * <li>a stop icon, clickable and which calls {@link Task#cancel(String)} for
99111 * the displayed task</li>
100112 * </ul>
109121
110122public class SwingTaskMonitorComponent {
111123
124+ /** Column of the {@link #taskTable} with the log icon. */
125+ private static final int LOG_COLUMN = 1 ;
126+
127+ /** Column of the {@link #taskTable} with the stop icon. */
128+ private static final int STOP_COLUMN = 2 ;
129+
130+ private final Context context ;
131+
132+ /** Open log windows, by task. Accessed from the event dispatch thread only. */
133+ private final Map <Task , JFrame > logFrames = new HashMap <>();
134+
112135 /**
113136 * Progress bar showing the global progression ( = progression of all tasks ).
114137 * Clicking it toggles taskFrame visibility.
@@ -169,6 +192,7 @@ public SwingTaskMonitorComponent(final Context context,
169192 int size ,
170193 boolean undecorated ) {
171194 context .inject (this ); // register event handler (this#onEvent)
195+ this .context = context ;
172196
173197 this .sizeGlobalProgressBar = size ;
174198 this .confirmBeforeCancel = confirmBeforeCancel ;
@@ -210,8 +234,9 @@ public void mouseClicked(MouseEvent e) {
210234 taskTable .setTableHeader (null ); // no header
211235 taskTable .setRowMargin (2 );
212236 taskTable .setDefaultRenderer (Task .class , new TaskRenderer (false ));
213- // restrict size of second column to the size of the stop icon
214- taskTable .getColumnModel ().getColumn (1 ).setMaxWidth (30 );
237+ // restrict size of icon columns to the size of the icons
238+ taskTable .getColumnModel ().getColumn (LOG_COLUMN ).setMaxWidth (30 );
239+ taskTable .getColumnModel ().getColumn (STOP_COLUMN ).setMaxWidth (30 );
215240
216241 // Scroll pane containing the JTable -> necessary when many tasks are displayed
217242 JScrollPane scrollPane = new JScrollPane (taskTable );
@@ -222,13 +247,17 @@ public void mouseClicked(MouseEvent e) {
222247 scrollPane .setColumnHeaderView (null );
223248 taskFrame .pack ();
224249
225- // enable canceling when the user clicks on the second column (of index 1)
250+ // show the log or cancel when the user clicks on the corresponding icon
226251 taskTable .addMouseListener (new java .awt .event .MouseAdapter () {
227252 @ Override
228253 public void mouseClicked (java .awt .event .MouseEvent evt ) {
229254 int row = taskTable .rowAtPoint (evt .getPoint ());
230255 int col = taskTable .columnAtPoint (evt .getPoint ());
231- if (row >= 0 && col == 1 ) {
256+ if (row >= 0 && col == LOG_COLUMN ) {
257+ Task selectedTask = taskTableModel .getTask (row );
258+ if (selectedTask != null ) showLog (selectedTask );
259+ }
260+ if (row >= 0 && col == STOP_COLUMN ) {
232261 Task selectedTask = taskTableModel .getTask (row );
233262 if (selectedTask !=null ) {
234263 if (SwingTaskMonitorComponent .this .confirmBeforeCancel ) {
@@ -274,6 +303,37 @@ private void onEvent(final TaskEvent evt) {
274303 globalProgressBar .setValue ((int )(globalProgression *100 ));
275304 }
276305
306+ /**
307+ * Shows a window with the output logged to the given task's logger. The
308+ * window shows output from the moment it is first opened, and remains
309+ * available after the task finishes, until closed.
310+ */
311+ private void showLog (final Task task ) {
312+ JFrame frame = logFrames .get (task );
313+ if (frame == null ) {
314+ final LoggingPanel panel = new LoggingPanel (context );
315+ panel .setSourcesPanelVisible (false );
316+ final Logger logger = task .log ();
317+ logger .addLogListener (panel );
318+ final JFrame logFrame = new JFrame ("Log: " + task .getName ());
319+ logFrame .setDefaultCloseOperation (JFrame .DISPOSE_ON_CLOSE );
320+ logFrame .add (panel );
321+ logFrame .setSize (640 , 400 );
322+ logFrame .setLocationRelativeTo (taskFrame );
323+ logFrame .addWindowListener (new WindowAdapter () {
324+ @ Override
325+ public void windowClosed (WindowEvent e ) {
326+ logger .removeLogListener (panel );
327+ logFrames .remove (task );
328+ }
329+ });
330+ logFrames .put (task , logFrame );
331+ frame = logFrame ;
332+ }
333+ frame .setVisible (true );
334+ frame .toFront ();
335+ }
336+
277337 /**
278338 * User confirmation required when canceling a task by clicking the task table.
279339 */
@@ -388,25 +448,22 @@ public synchronized int getRowCount() {
388448
389449 @ Override
390450 public int getColumnCount () {
391- return 2 ;
451+ return 3 ;
392452 }
393453
394454 @ Override
395455 public String getColumnName (int columnIndex ) {
396456 switch (columnIndex ) {
397457 case 0 : return "Task" ;
398- case 1 : return "Stop" ;
458+ case LOG_COLUMN : return "Log" ;
459+ case STOP_COLUMN : return "Stop" ;
399460 }
400461 return null ;
401462 }
402463
403464 @ Override
404465 public Class <?> getColumnClass (int columnIndex ) {
405- switch (columnIndex ) {
406- case 0 : return Task .class ;
407- case 1 : return Task .class ;
408- }
409- return null ;
466+ return columnIndex >= 0 && columnIndex < getColumnCount () ? Task .class : null ;
410467 }
411468
412469 @ Override
@@ -416,12 +473,8 @@ public boolean isCellEditable(int rowIndex, int columnIndex) {
416473
417474 @ Override
418475 public Object getValueAt (int rowIndex , int columnIndex ) {
419- if (rowIndex <monitoredTasks .size ()) {
420- Task task = monitoredTasks .get (rowIndex );
421- switch (columnIndex ) {
422- case 0 : return task ;
423- case 1 : return task ;
424- }
476+ if (rowIndex <monitoredTasks .size () && getColumnClass (columnIndex ) != null ) {
477+ return monitoredTasks .get (rowIndex );
425478 }
426479 return null ;
427480 }
@@ -488,6 +541,7 @@ class TaskRenderer implements TableCellRenderer {
488541 JLabel labelTop = new JLabel (); // top label : task name and status
489542 JProgressBar progressBar = new JProgressBar (); // standard linear progress bar
490543 JLabel labelBottom = new JLabel (); // bottom label : task completion, and optionally time left
544+ JLabel showLog ; // log icon
491545 JLabel cancelTask ; // cancel icon
492546
493547 public TaskRenderer (boolean isBordered ) {
@@ -501,6 +555,9 @@ public TaskRenderer(boolean isBordered) {
501555 cell .add (labelTop ,"height ::14, span" );
502556 cell .add (progressBar ,"height ::3, span" );
503557 cell .add (labelBottom , "height ::14" );
558+ showLog = new JLabel ("\u2261 " , SwingConstants .CENTER );
559+ showLog .setOpaque (true );
560+ showLog .setToolTipText ("Show log" );
504561 cancelTask = new JLabel ("\u2715 " , SwingConstants .CENTER );
505562 cancelTask .setOpaque (true );
506563 }
@@ -515,10 +572,15 @@ public Component getTableCellRendererComponent(
515572 cell .setForeground (table .getForeground ());
516573 labelTop .setForeground (table .getForeground ());
517574 labelBottom .setForeground (table .getForeground ());
575+ showLog .setBackground (table .getBackground ());
576+ showLog .setForeground (table .getForeground ());
518577 cancelTask .setBackground (table .getBackground ());
519578 cancelTask .setForeground (table .getForeground ());
520- if (column ==1 ) {
521- return cancelTask ; // second column : stop icon
579+ if (column == LOG_COLUMN ) {
580+ return showLog ;
581+ }
582+ if (column == STOP_COLUMN ) {
583+ return cancelTask ;
522584 }
523585 // first column : task information
524586 Task task = (Task ) tk ;
0 commit comments