1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.printing.PrintDialog; 14 15 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.SWTException; 19 import org.eclipse.swt.internal.LONG; 20 import org.eclipse.swt.internal.gtk.OS; 21 import org.eclipse.swt.widgets.Dialog; 22 import org.eclipse.swt.widgets.Display; 23 import org.eclipse.swt.widgets.Shell; 24 import org.eclipse.swt.widgets.Widget; 25 import org.eclipse.swt.printing.Printer; 26 import org.eclipse.swt.printing.PrinterData; 27 import java.lang.all; 28 29 version(Tango){ 30 import tango.util.Convert; 31 } else { // Phobos 32 import std.conv; 33 } 34 35 /** 36 * Instances of this class allow the user to select 37 * a printer and various print-related parameters 38 * prior to starting a print job. 39 * <p> 40 * IMPORTANT: This class is intended to be subclassed <em>only</em> 41 * within the SWT implementation. 42 * </p> 43 * 44 * @see <a href="http://www.eclipse.org/swt/snippets/#printing">Printing snippets</a> 45 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> 46 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 47 */ 48 public class PrintDialog : Dialog { 49 PrinterData printerData; 50 int scope_ = PrinterData.ALL_PAGES; 51 int startPage = 1, endPage = 1; 52 bool printToFile = false; 53 54 GtkPrintUnixDialog* handle; 55 int index; 56 char [] settingsData; 57 58 static const String GET_MODAL_DIALOG = "org.eclipse.swt.internal.gtk.getModalDialog"; 59 static const String SET_MODAL_DIALOG = "org.eclipse.swt.internal.gtk.setModalDialog"; 60 static const String ADD_IDLE_PROC_KEY = "org.eclipse.swt.internal.gtk.addIdleProc"; 61 static const String REMOVE_IDLE_PROC_KEY = "org.eclipse.swt.internal.gtk.removeIdleProc"; 62 static const String GET_EMISSION_PROC_KEY = "org.eclipse.swt.internal.gtk.getEmissionProc"; 63 /** 64 * Constructs a new instance of this class given only its parent. 65 * 66 * @param parent a composite control which will be the parent of the new instance (cannot be null) 67 * 68 * @exception IllegalArgumentException <ul> 69 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 70 * </ul> 71 * @exception SWTException <ul> 72 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 73 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 74 * </ul> 75 * 76 * @see SWT 77 * @see Widget#checkSubclass 78 * @see Widget#getStyle 79 */ 80 public this (Shell parent) { 81 this (parent, SWT.PRIMARY_MODAL); 82 } 83 84 /** 85 * Constructs a new instance of this class given its parent 86 * and a style value describing its behavior and appearance. 87 * <p> 88 * The style value is either one of the style constants defined in 89 * class <code>SWT</code> which is applicable to instances of this 90 * class, or must be built by <em>bitwise OR</em>'ing together 91 * (that is, using the <code>int</code> "|" operator) two or more 92 * of those <code>SWT</code> style constants. The class description 93 * lists the style constants that are applicable to the class. 94 * Style bits are also inherited from superclasses. 95 * </p> 96 * 97 * @param parent a composite control which will be the parent of the new instance (cannot be null) 98 * @param style the style of control to construct 99 * 100 * @exception IllegalArgumentException <ul> 101 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 102 * </ul> 103 * @exception SWTException <ul> 104 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 105 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 106 * </ul> 107 * 108 * @see SWT 109 * @see Widget#checkSubclass 110 * @see Widget#getStyle 111 */ 112 public this (Shell parent, int style) { 113 super (parent, parent is null? style : checkStyleBit (parent, style)); 114 checkSubclass (); 115 } 116 117 /** 118 * Sets the printer data that will be used when the dialog 119 * is opened. 120 * 121 * @param data the data that will be used when the dialog is opened 122 * 123 * @since 3.4 124 */ 125 public void setPrinterData(PrinterData data) { 126 this.printerData = data; 127 } 128 129 /** 130 * Returns the printer data that will be used when the dialog 131 * is opened. 132 * 133 * @return the data that will be used when the dialog is opened 134 * 135 * @since 3.4 136 */ 137 public PrinterData getPrinterData() { 138 return printerData; 139 } 140 141 static int checkBits (int style, int int0, int int1, int int2, int int3, int int4, int int5) { 142 int mask = int0 | int1 | int2 | int3 | int4 | int5; 143 if ((style & mask) is 0) style |= int0; 144 if ((style & int0) !is 0) style = (style & ~mask) | int0; 145 if ((style & int1) !is 0) style = (style & ~mask) | int1; 146 if ((style & int2) !is 0) style = (style & ~mask) | int2; 147 if ((style & int3) !is 0) style = (style & ~mask) | int3; 148 if ((style & int4) !is 0) style = (style & ~mask) | int4; 149 if ((style & int5) !is 0) style = (style & ~mask) | int5; 150 return style; 151 } 152 153 static int checkStyleBit (Shell parent, int style) { 154 style &= ~SWT.MIRRORED; 155 if ((style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT)) is 0) { 156 if (parent !is null) { 157 if ((parent.getStyle () & SWT.LEFT_TO_RIGHT) !is 0) style |= SWT.LEFT_TO_RIGHT; 158 if ((parent.getStyle () & SWT.RIGHT_TO_LEFT) !is 0) style |= SWT.RIGHT_TO_LEFT; 159 } 160 } 161 return checkBits (style, SWT.LEFT_TO_RIGHT, SWT.RIGHT_TO_LEFT, 0, 0, 0, 0); 162 } 163 164 /** 165 * Returns the print job scope that the user selected 166 * before pressing OK in the dialog. This will be one 167 * of the following values: 168 * <dl> 169 * <dt><code>PrinterData.ALL_PAGES</code></dt> 170 * <dd>Print all pages in the current document</dd> 171 * <dt><code>PrinterData.PAGE_RANGE</code></dt> 172 * <dd>Print the range of pages specified by startPage and endPage</dd> 173 * <dt><code>PrinterData.SELECTION</code></dt> 174 * <dd>Print the current selection</dd> 175 * </dl> 176 * 177 * @return the scope setting that the user selected 178 */ 179 public int getScope() { 180 return scope_; 181 } 182 183 /** 184 * Sets the scope of the print job. The user will see this 185 * setting when the dialog is opened. This can have one of 186 * the following values: 187 * <dl> 188 * <dt><code>PrinterData.ALL_PAGES</code></dt> 189 * <dd>Print all pages in the current document</dd> 190 * <dt><code>PrinterData.PAGE_RANGE</code></dt> 191 * <dd>Print the range of pages specified by startPage and endPage</dd> 192 * <dt><code>PrinterData.SELECTION</code></dt> 193 * <dd>Print the current selection</dd> 194 * </dl> 195 * 196 * @param scope the scope setting when the dialog is opened 197 */ 198 public void setScope(int scope_) { 199 this.scope_ = scope_; 200 } 201 202 /** 203 * Returns the start page setting that the user selected 204 * before pressing OK in the dialog. 205 * <p> 206 * This value can be from 1 to the maximum number of pages for the platform. 207 * Note that it is only valid if the scope is <code>PrinterData.PAGE_RANGE</code>. 208 * </p> 209 * 210 * @return the start page setting that the user selected 211 */ 212 public int getStartPage() { 213 return startPage; 214 } 215 216 /** 217 * Sets the start page that the user will see when the dialog 218 * is opened. 219 * <p> 220 * This value can be from 1 to the maximum number of pages for the platform. 221 * Note that it is only valid if the scope is <code>PrinterData.PAGE_RANGE</code>. 222 * </p> 223 * 224 * @param startPage the startPage setting when the dialog is opened 225 */ 226 public void setStartPage(int startPage) { 227 this.startPage = startPage; 228 } 229 230 /** 231 * Returns the end page setting that the user selected 232 * before pressing OK in the dialog. 233 * <p> 234 * This value can be from 1 to the maximum number of pages for the platform. 235 * Note that it is only valid if the scope is <code>PrinterData.PAGE_RANGE</code>. 236 * </p> 237 * 238 * @return the end page setting that the user selected 239 */ 240 public int getEndPage() { 241 return endPage; 242 } 243 244 /** 245 * Sets the end page that the user will see when the dialog 246 * is opened. 247 * <p> 248 * This value can be from 1 to the maximum number of pages for the platform. 249 * Note that it is only valid if the scope is <code>PrinterData.PAGE_RANGE</code>. 250 * </p> 251 * 252 * @param endPage the end page setting when the dialog is opened 253 */ 254 public void setEndPage(int endPage) { 255 this.endPage = endPage; 256 } 257 258 /** 259 * Returns the 'Print to file' setting that the user selected 260 * before pressing OK in the dialog. 261 * 262 * @return the 'Print to file' setting that the user selected 263 */ 264 public bool getPrintToFile() { 265 return printToFile; 266 } 267 268 /** 269 * Sets the 'Print to file' setting that the user will see 270 * when the dialog is opened. 271 * 272 * @param printToFile the 'Print to file' setting when the dialog is opened 273 */ 274 public void setPrintToFile(bool printToFile) { 275 this.printToFile = printToFile; 276 } 277 278 /** 279 * Makes the receiver visible and brings it to the front 280 * of the display. 281 * 282 * @return a printer data object describing the desired print job parameters 283 * 284 * @exception SWTException <ul> 285 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 286 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 287 * </ul> 288 */ 289 public PrinterData open() { 290 if (OS.GTK_VERSION < OS.buildVERSION (2, 10, 0)) { 291 return Printer.getDefaultPrinterData(); 292 } else { 293 char* titleBytes = toStringz( getText() ); 294 auto topHandle = getParent().handle; 295 while (topHandle !is null && !OS.GTK_IS_WINDOW(topHandle)) { 296 topHandle = OS.gtk_widget_get_parent(topHandle); 297 } 298 handle = cast(GtkPrintUnixDialog*)OS.gtk_print_unix_dialog_new(titleBytes, cast(GtkWindow*)topHandle); 299 300 //TODO: Not currently implemented. May need new API. For now, disable 'Current' in the dialog. (see gtk bug 344519) 301 OS.gtk_print_unix_dialog_set_current_page(handle, -1); 302 303 OS.gtk_print_unix_dialog_set_manual_capabilities(handle, 304 OS.GTK_PRINT_CAPABILITY_COLLATE | OS.GTK_PRINT_CAPABILITY_COPIES | OS.GTK_PRINT_CAPABILITY_PAGE_SET); 305 306 /* Set state into print dialog settings. */ 307 auto settings = OS.gtk_print_settings_new(); 308 auto page_setup = OS.gtk_page_setup_new(); 309 310 if (printerData !is null) { 311 if (printerData.otherData !is null) { 312 Printer.restore(printerData.otherData, settings, page_setup); 313 } 314 /* Set values of settings from PrinterData. */ 315 Printer.setScope(settings, printerData.scope_, printerData.startPage, printerData.endPage); 316 //TODO: Should we look at printToFile, or driver/name for "Print to File", or both? (see gtk bug 345590) 317 if (printerData.printToFile) { 318 char* buffer = toStringz(printerData.fileName); 319 OS.gtk_print_settings_set(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr, buffer); 320 } 321 if (printerData.driver.equals("GtkPrintBackendFile") && printerData.name.equals("Print to File")) { //$NON-NLS-1$ //$NON-NLS-2$ 322 char* buffer = toStringz(printerData.fileName); 323 OS.gtk_print_settings_set(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr, buffer); 324 } 325 OS.gtk_print_settings_set_n_copies(settings, printerData.copyCount); 326 OS.gtk_print_settings_set_collate(settings, printerData.collate); 327 } 328 329 Printer.setScope(settings, scope_, startPage, endPage); 330 331 if (printToFile) { 332 char* buffer = toStringz( "Print to File" ); //$NON-NLS-1$ 333 OS.gtk_print_settings_set_printer(settings, buffer); 334 } 335 OS.gtk_print_unix_dialog_set_settings(handle, settings); 336 OS.gtk_print_unix_dialog_set_page_setup(handle, page_setup); 337 OS.g_object_unref(settings); 338 OS.g_object_unref(page_setup); 339 340 PrinterData data = null; 341 //TODO: Handle 'Print Preview' (GTK_RESPONSE_APPLY). 342 Display display = getParent() !is null ? getParent().getDisplay (): Display.getCurrent (); 343 344 int signalId = 0; 345 ptrdiff_t hookId = 0; 346 if ((getStyle () & SWT.RIGHT_TO_LEFT) !is 0) { 347 signalId = OS.g_signal_lookup (OS.map.ptr, OS.GTK_TYPE_WIDGET()); 348 hookId = OS.g_signal_add_emission_hook (signalId, 0, cast(GSignalEmissionHook)cast(void*)(cast(LONG) display.getData (GET_EMISSION_PROC_KEY)).intValue, handle, null); 349 } 350 display.setData (ADD_IDLE_PROC_KEY, null); 351 Object oldModal = null; 352 if (OS.gtk_window_get_modal (handle)) { 353 oldModal = display.getData (GET_MODAL_DIALOG); 354 display.setData (SET_MODAL_DIALOG, this); 355 } 356 ptrdiff_t response = OS.gtk_dialog_run (handle); 357 if (OS.gtk_window_get_modal (handle)) { 358 display.setData (SET_MODAL_DIALOG, oldModal); 359 } 360 if ((getStyle () & SWT.RIGHT_TO_LEFT) !is 0) { 361 OS.g_signal_remove_emission_hook (signalId, hookId); 362 } 363 if (response is OS.GTK_RESPONSE_OK) { 364 auto printer = OS.gtk_print_unix_dialog_get_selected_printer(handle); 365 if (printer !is null) { 366 367 /* Get state from print dialog. */ 368 settings = OS.gtk_print_unix_dialog_get_settings(handle); // must unref 369 page_setup = OS.gtk_print_unix_dialog_get_page_setup(handle); // do not unref 370 data = Printer.printerDataFromGtkPrinter(printer); 371 ptrdiff_t print_pages = OS.gtk_print_settings_get_print_pages(settings); 372 switch (print_pages) { 373 case OS.GTK_PRINT_PAGES_ALL: 374 scope_ = PrinterData.ALL_PAGES; 375 break; 376 case OS.GTK_PRINT_PAGES_RANGES: 377 scope_ = PrinterData.PAGE_RANGE; 378 int num_ranges; 379 auto page_ranges = OS.gtk_print_settings_get_page_ranges(settings, &num_ranges); 380 ptrdiff_t length = num_ranges; 381 int min = int.max, max = 0; 382 for (int i = 0; i < length; i++) { 383 min = Math.min(min, page_ranges[i].start + 1); 384 max = Math.max(max, page_ranges[i].end + 1); 385 } 386 OS.g_free(page_ranges); 387 startPage = min is int.max ? 1 : min; 388 endPage = max is 0 ? 1 : max; 389 break; 390 case OS.GTK_PRINT_PAGES_CURRENT: 391 //TODO: Disabled in dialog (see above). This code will not run. (see gtk bug 344519) 392 scope_ = PrinterData.SELECTION; 393 startPage = endPage = OS.gtk_print_unix_dialog_get_current_page(handle); 394 break; 395 default: 396 } 397 398 printToFile = ( data.name.equals("Print to File")); //$NON-NLS-1$ 399 if (printToFile) { 400 auto address = OS.gtk_print_settings_get(settings, OS.GTK_PRINT_SETTINGS_OUTPUT_URI.ptr); 401 data.fileName = fromStringz( address)._idup(); 402 } 403 404 data.scope_ = scope_; 405 data.startPage = startPage; 406 data.endPage = endPage; 407 data.printToFile = printToFile; 408 data.copyCount = OS.gtk_print_settings_get_n_copies(settings); 409 data.collate = cast(bool)OS.gtk_print_settings_get_collate(settings); 410 411 /* Save other print_settings data as key/value pairs in otherData. */ 412 index = 0; 413 settingsData = new char[1024]; 414 settingsData[] = '\0'; 415 OS.gtk_print_settings_foreach (settings, &GtkPrintSettingsFunc, cast(void*)this ); 416 index++; // extra null terminator after print_settings and before page_setup 417 418 /* Save page_setup data as key/value pairs in otherData. 419 * Note that page_setup properties must be stored and restored in the same order. 420 */ 421 store("orientation", OS.gtk_page_setup_get_orientation(page_setup)); //$NON-NLS-1$ 422 store("top_margin", OS.gtk_page_setup_get_top_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 423 store("bottom_margin", OS.gtk_page_setup_get_bottom_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 424 store("left_margin", OS.gtk_page_setup_get_left_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 425 store("right_margin", OS.gtk_page_setup_get_right_margin(page_setup, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 426 auto paper_size = OS.gtk_page_setup_get_paper_size(page_setup); //$NON-NLS-1$ 427 storeBytes("paper_size_name", OS.gtk_paper_size_get_name(paper_size)); //$NON-NLS-1$ 428 storeBytes("paper_size_display_name", OS.gtk_paper_size_get_display_name(paper_size)); //$NON-NLS-1$ 429 storeBytes("paper_size_ppd_name", OS.gtk_paper_size_get_ppd_name(paper_size)); //$NON-NLS-1$ 430 store("paper_size_width", OS.gtk_paper_size_get_width(paper_size, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 431 store("paper_size_height", OS.gtk_paper_size_get_height(paper_size, OS.GTK_UNIT_MM)); //$NON-NLS-1$ 432 store("paper_size_is_custom", OS.gtk_paper_size_is_custom(paper_size)); //$NON-NLS-1$ 433 data.otherData = settingsData; 434 OS.g_object_unref(settings); 435 } 436 } 437 display.setData (REMOVE_IDLE_PROC_KEY, null); 438 OS.gtk_widget_destroy (handle); 439 return data; 440 } 441 } 442 443 private static extern(C) void GtkPrintSettingsFunc (char* key, char* value, void* data) { 444 (cast(PrintDialog)data).GtkPrintSettingsMeth(key, value ); 445 } 446 447 void GtkPrintSettingsMeth (char* key, char* value) { 448 store( fromStringz(key)._idup(), fromStringz(value)._idup() ); 449 } 450 451 void store(String key, int value) { 452 store(key, to!(String)(value)); 453 } 454 455 void store(String key, double value) { 456 store(key, to!(String)(value)); 457 } 458 459 void store(String key, bool value) { 460 store(key, to!(String)(value)); 461 } 462 463 void storeBytes(String key, char* value) { 464 store(key, fromStringz(value)._idup() ); 465 } 466 467 void store(String key, String value) { 468 ptrdiff_t length = key.length + 1 + value.length + 1; 469 if (index + length + 1 > settingsData.length) { 470 char [] newData = new char[settingsData.length + Math.max(length + 1, 1024)]; 471 newData[] = '\0'; 472 System.arraycopy (settingsData, 0, newData, 0, settingsData.length); 473 settingsData = newData; 474 } 475 System.arraycopy (key, 0, settingsData, index, key.length); 476 index += key.length + 1; // null terminated 477 System.arraycopy (value, 0, settingsData, index, value.length); 478 index += value.length + 1; // null terminated 479 } 480 }