1/* 2 * RenderProgress.java 3 * Copyright (C) 2005 Amin Ahmad. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at 20 * www.ahmadsoft.org. 21 */ 22package org.ahmadsoft.foprocessor.ui.dialogs; 23 24import java.io.ByteArrayOutputStream; 25import java.io.PrintStream; 26import java.lang.reflect.InvocationTargetException; 27import java.text.SimpleDateFormat; 28import java.util.Date; 29import java.util.List; 30import java.util.concurrent.CountDownLatch; 31import java.util.logging.Handler; 32import java.util.logging.Level; 33import java.util.logging.LogRecord; 34import java.util.logging.Logger; 35 36import org.ahmadsoft.foprocessor.FoProcessorPlugin; 37import org.ahmadsoft.foprocessor.core.FileRenderSpecification; 38import org.ahmadsoft.foprocessor.operations.ConversionOperation; 39import org.eclipse.core.resources.IProject; 40import org.eclipse.core.runtime.CoreException; 41import org.eclipse.core.runtime.IProgressMonitor; 42import org.eclipse.debug.ui.IDebugUIConstants; 43import org.eclipse.debug.ui.console.ConsoleColorProvider; 44import org.eclipse.jface.dialogs.IDialogConstants; 45import org.eclipse.jface.dialogs.TitleAreaDialog; 46import org.eclipse.jface.operation.IRunnableContext; 47import org.eclipse.jface.operation.IRunnableWithProgress; 48import org.eclipse.jface.operation.ModalContext; 49import org.eclipse.jface.resource.JFaceResources; 50import org.eclipse.jface.text.BadLocationException; 51import org.eclipse.jface.text.Document; 52import org.eclipse.jface.text.ITextViewer; 53import org.eclipse.jface.text.TextViewer; 54import org.eclipse.swt.SWT; 55import org.eclipse.swt.custom.StyleRange; 56import org.eclipse.swt.graphics.Color; 57import org.eclipse.swt.graphics.Image; 58import org.eclipse.swt.graphics.Point; 59import org.eclipse.swt.layout.GridData; 60import org.eclipse.swt.layout.GridLayout; 61import org.eclipse.swt.widgets.Button; 62import org.eclipse.swt.widgets.Composite; 63import org.eclipse.swt.widgets.Control; 64import org.eclipse.swt.widgets.Display; 65import org.eclipse.swt.widgets.Label; 66import org.eclipse.swt.widgets.ProgressBar; 67import org.eclipse.swt.widgets.Shell; 68 69/** 70 * @author Amin Ahmad 71 */ 72public class RenderProgress extends TitleAreaDialog implements IRunnableContext, ConversionOperation.Listener { 73 74 private Image image = null; 75 private ITextViewer console; 76 private Document document; 77 78 private ProgressBar progressBar; 79 private Label consoleTitle; 80 81 private ConsoleColorProvider colorProvider = new ConsoleColorProvider(); 82 83 private CountDownLatch createSignal = new CountDownLatch(1); 84 85 private final int RUN_BACKGROUND_ID = 3141; 86 private List<FileRenderSpecification> renderSpecs; 87 88 private volatile boolean cancelled; 89 90 /** 91 * @param parentShell 92 */ 93 public RenderProgress(Shell parentShell, List<FileRenderSpecification> renderSpecs) { 94 super(parentShell); 95 setShellStyle(SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL | SWT.RESIZE); 96 this.renderSpecs = renderSpecs; 97 } 98 99 protected Point getInitialSize() { 100 return getShell().computeSize(460, 500, true); 101 } 102 103 protected Control createContents(Composite parent) { 104 Control result = super.createContents(parent); 105 106 createSignal.countDown(); 107 108 return result; 109 } 110 111 protected void createButtonsForButtonBar(Composite parent) { 112// Button btnBackground = createButton(parent, RUN_BACKGROUND_ID, "Run in &Background", 113// false); 114// btnBackground.setEnabled(false); 115 super.createButtonsForButtonBar(parent); 116 } 117 118 protected Control createDialogArea(Composite parent) { 119 Composite compositeParent = (Composite)super.createDialogArea(parent); 120 121 // Setup the title area 122 // 123 setTitleImage(getImage()); 124 setTitle("Rendering"); 125 setMessage("Rendering documents."); 126 127 Composite pageContainer = new Composite(compositeParent, SWT.NONE); 128 GridData gd = new GridData(GridData.FILL_BOTH); 129 pageContainer.setLayoutData(gd); 130 pageContainer.setFont(parent.getFont()); 131 132 // Document 133 // 134 document = new Document(); 135 136 //layout for page 137 // 138 GridLayout layout = new GridLayout(); 139 layout.numColumns = 1; 140 141 pageContainer.setLayout(layout); 142 143 progressCaption = new Label(pageContainer, SWT.NONE); 144 progressCaption.setText("Progress:"); 145 progressCaption.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 146 147 progressBar = new ProgressBar(pageContainer, SWT.SMOOTH | SWT.HORIZONTAL); 148 gd = new GridData(GridData.FILL_HORIZONTAL); 149 gd.heightHint = 10; 150 progressBar.setLayoutData(gd); 151 152 consoleTitle = new Label(pageContainer, SWT.NONE); 153 consoleTitle.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 154 155 console = new TextViewer(pageContainer, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); 156 console.getTextWidget().setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT)); 157 console.getTextWidget().setLayoutData(new GridData(GridData.FILL_BOTH)); 158 console.setDocument(document); 159 160 // stream.setColor(fColorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 161 162// btnAlwaysBackground = new Button(pageContainer, SWT.CHECK); 163// btnAlwaysBackground.setText("Always render in the background."); 164 165 btnBuildAutomatically = new Button(pageContainer, SWT.CHECK); 166 btnBuildAutomatically.setText("Integrate rendering for this document into the build cycle."); 167 168 boolean buildSelVal = true; 169 for (FileRenderSpecification spec: renderSpecs) { 170 try { 171 if (!FoProcessorPlugin.getDefault().isAutoBuild(spec.getInputFile())) { 172 buildSelVal = false; 173 break; 174 } 175 } catch (CoreException e) { 176 buildSelVal = false; 177 break; 178 } 179 } 180 181 btnBuildAutomatically.setSelection(buildSelVal); 182 183 // Build the separator line 184 // 185 Label separator= new Label(compositeParent, SWT.HORIZONTAL | SWT.SEPARATOR); 186 separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 187 188 getShell().setText("Apache FOP 0.95"); // TODO update from version to version 189 190 return compositeParent; 191 } 192 193 public Image getImage() { 194 if (image == null) { 195 image = FoProcessorPlugin.getDefault().getImageDescriptor("full/wizban/RenderProgress.gif").createImage(); 196 } 197 return image; 198 } 199 200 public void setConsoleTitle() { 201 SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss"); 202 consoleTitle.setText("XSL-FO Rendering [Powered by Apache FOP] (" +df.format(new Date())+ ")"); 203 } 204 205 /** 206 * Renders the document. Should not be run on the UI thread. 207 * 208 * @param mimeType 209 * @param extension 210 * @param resources 211 */ 212 public void beginRendering() { 213 214 try { 215 createSignal.await(); 216 } catch (InterruptedException e) { 217 error(null, e); 218 } 219 220 // Setup the FOP Logger 221 // 222 223 Handler lh = new LogHandler(); 224 lh.setLevel(Level.ALL); 225 Logger.getLogger("org.apache.fop").setLevel(Level.INFO); 226 Logger.getLogger("org.apache.fop.area.AreaTreeHandler").setLevel(Level.FINE); 227 Logger.getLogger("org.apache.fop.fo.FOTreeBuilder").setLevel(Level.FINE); 228 Logger.getLogger("org.apache.fop").addHandler(lh); 229 230 231 // Set the titles and the button states 232 // 233 setConsoleTitle(); 234 235 Button okButton = getButton(IDialogConstants.OK_ID); 236 if (okButton != null) { 237 okButton.setEnabled(false); 238 } 239 240 Button cnButton = getButton(IDialogConstants.CANCEL_ID); 241 if (cnButton != null) { 242 cnButton.setEnabled(true); 243 } 244 245 Button bkgButton = getButton(RUN_BACKGROUND_ID); 246 if (bkgButton != null) { 247 bkgButton.setEnabled(true); 248 } 249 250 conversionOp = new ConversionOperation(renderSpecs, null, RenderProgress.this); 251 IRunnableWithProgress runnable = new IRunnableWithProgress() { 252 public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { 253 conversionOp.setProgressMonitor(monitor); 254 if (!cancelled) { 255 conversionOp.run(); 256 } 257 } 258 }; 259 260 try { 261 run(true, true, runnable); 262 } catch (Exception e) { 263 error(null, e); 264 } 265 } 266 267 268 /** 269 * @inheritDoc 270 */ 271 public boolean close() { 272 getImage().dispose(); 273 return super.close(); 274 } 275 /** 276 * @inheritDoc 277 */ 278 public void cancelPressed() { 279 cancelled = true; 280 if (conversionOp != null) { 281 conversionOp.setInterrupted(); 282 } 283 super.cancelPressed(); 284 } 285 286 /** 287 * @inheritDoc 288 */ 289 public void okPressed() { 290 // Process builder. 291 // 292 boolean buildValue = btnBuildAutomatically.getSelection(); 293 294 for (FileRenderSpecification spec: renderSpecs) { 295 IProject project = spec.getInputFile().getProject(); 296 297 try { 298 if (buildValue == true) { 299 FoProcessorPlugin.getDefault().addFopNature(project); 300 } 301 FoProcessorPlugin.getDefault().setAutoBuild(spec.getInputFile(), buildValue, spec); 302 } catch (CoreException e) { 303 e.printStackTrace(); 304 } 305 } 306 307 super.okPressed(); 308 } 309 310 // IRunnableContext 311 // 312 313 public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException { 314 // Create the Progress Monitor 315 // 316 ProgressMonitor monitor = new ProgressMonitor(); 317 318 ModalContext.run(runnable, fork, monitor, getShell().getDisplay()); 319 } 320 321 // Logging Methods -------------------------------------------------------- 322 323 private static final String nl = System.getProperty("line.separator"); 324 private Label progressCaption; 325 private Button btnBuildAutomatically; 326 private ConversionOperation conversionOp; 327 328 private String toString(Throwable t) { 329 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 330 PrintStream ps = new PrintStream(bos); 331 t.printStackTrace(ps); 332 ps.close(); 333 334 return new String(bos.toByteArray()); 335 } 336 337 private void append(final String s, final Color color) { 338 if (s == null) { 339 return; 340 } 341 Display.getDefault().asyncExec( 342 new Runnable() { 343 public void run() { 344 try { 345 StyleRange styleRange = new StyleRange(); 346 styleRange.start = document.getLength(); 347 styleRange.length = s.length() + nl.length(); 348 styleRange.foreground = color; 349 350 document.replace(document.getLength(), 0, s); 351 document.replace(document.getLength(), 0, nl); 352 353 if (console.getTextWidget() != null) { // amazingly, this happens... 354 console.getTextWidget().setStyleRange(styleRange); 355 } 356 } catch (BadLocationException e) { 357 e.printStackTrace(); 358 } 359 } 360 } 361 ); 362 } 363 364 /** 365 * @inheritdoc 366 */ 367 public void error(String arg0) { 368 append(arg0, colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 369 } 370 371 /** 372 * @inheritdoc 373 */ 374 public void error(String arg0, Throwable arg1) { 375 append(arg0, colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 376 append(toString(arg1), colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 377 378 } 379 380 /** 381 * @inheritdoc 382 */ 383 public void fatalError(String arg0) { 384 append(arg0, colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 385 } 386 387 /** 388 * @inheritdoc 389 */ 390 public void fatalError(String arg0, Throwable arg1) { 391 append(arg0, colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 392 append(toString(arg1), colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM)); 393 } 394 395 public class LogHandler extends Handler { 396 397 @Override 398 public void publish(LogRecord record) { 399 Color color = null; 400 if (record.getLevel().intValue() <= Level.INFO.intValue()) { 401 color = colorProvider.getColor(IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM); 402 } else { 403 color = colorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM); 404 } 405 if (record.getLevel().intValue() < Level.INFO.intValue()) 406 append(" " + record.getMessage(), color); 407 else 408 append(record.getMessage(), color); 409 if (record.getThrown() != null) { 410 append(RenderProgress.this.toString(record.getThrown()), color); 411 } 412 } 413 414 @Override 415 public void flush() { 416 // TODO Auto-generated method stub 417 418 } 419 420 @Override 421 public void close() throws SecurityException { 422 // TODO Auto-generated method stub 423 424 } 425 426 } 427 428 // Progress Monitor ------------------------------------------------------- 429 430 public class ProgressMonitor implements IProgressMonitor { 431 432 private String name; 433 private boolean canceled; 434 435 /** 436 * @inheritdoc 437 */ 438 public void beginTask(final String name, final int totalWork) { 439 Display.getDefault().asyncExec( 440 new Runnable() { 441 public void run() { 442 progressBar.setMinimum(0); 443 progressBar.setSelection(0); 444 progressBar.setMaximum(totalWork); 445 446 progressCaption.setText("Progress: " + name); 447 ProgressMonitor.this.name = name; 448 } 449 } 450 ); 451 } 452 453 /** 454 * @inheritdoc 455 */ 456 public void done() { 457 Display.getDefault().asyncExec( 458 new Runnable() { 459 public void run() { 460 progressBar.setSelection(progressBar.getMaximum()); 461 progressCaption.setText("Progress: Done"); 462 } 463 } 464 ); 465 } 466 467 /** 468 * @inheritdoc 469 */ 470 public void internalWorked(double work) { 471 worked((int) Math.round(work)); 472 } 473 474 /** 475 * @inheritdoc 476 */ 477 public boolean isCanceled() { 478 return canceled; 479 } 480 481 /** 482 * @inheritdoc 483 */ 484 public void setCanceled(boolean value) { 485 this.canceled = value; 486 if (value) { 487 Display.getDefault().asyncExec( 488 new Runnable() { 489 public void run() { 490 progressCaption.setText("Progress: " + ProgressMonitor.this.name + "... Requesting cancellation"); 491 } 492 } 493 ); 494 } 495 } 496 497 /** 498 * @inheritdoc 499 */ 500 public void setTaskName(String name) { 501 this.name = name; 502 } 503 504 /** 505 * @inheritdoc 506 */ 507 public void subTask(final String name) { 508 Display.getDefault().asyncExec( 509 new Runnable() { 510 public void run() { 511 progressCaption.setText("Progress: " + ProgressMonitor.this.name + "... " + name); 512 } 513 } 514 ); 515 } 516 517 /** 518 * @inheritdoc 519 */ 520 public void worked(final int work) { 521 Display.getDefault().asyncExec( 522 new Runnable() { 523 public void run() { 524 progressBar.setSelection(progressBar.getSelection() + work); 525 } 526 } 527 ); 528 } 529 530 } 531 532 public void onFinish() { 533 Display.getDefault().asyncExec( 534 new Runnable() { 535 public void run() { 536 Button okButton = getButton(IDialogConstants.OK_ID); 537 if (okButton != null) { 538 okButton.setEnabled(true); 539 okButton.setFocus(); 540 } 541 542 Button cnButton = getButton(IDialogConstants.CANCEL_ID); 543 if (cnButton != null) { 544 cnButton.setEnabled(false); 545 } 546 547 Button bkgButton = getButton(RUN_BACKGROUND_ID); 548 if (bkgButton != null) { 549 bkgButton.setEnabled(false); 550 } 551 } 552 } 553 ); 554 } 555 556 public void onError(String msg, Throwable throwable) { 557 error(msg, throwable); 558 } 559} 560