Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h" | 5 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h" |
| 6 | 6 |
| 7 #include <ctype.h> | 7 #include <ctype.h> |
| 8 | 8 |
| 9 #include <map> | |
| 9 #include <string> | 10 #include <string> |
| 10 | 11 |
| 11 #include "base/base64.h" | 12 #include "base/base64.h" |
| 12 #include "base/bind.h" | 13 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" |
| 14 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 15 #include "base/i18n/file_util_icu.h" | 16 #include "base/i18n/file_util_icu.h" |
| 16 #include "base/i18n/number_formatting.h" | 17 #include "base/i18n/number_formatting.h" |
| 17 #include "base/json/json_reader.h" | 18 #include "base/json/json_reader.h" |
| 18 #include "base/lazy_instance.h" | 19 #include "base/lazy_instance.h" |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 57 #include "content/public/browser/navigation_controller.h" | 58 #include "content/public/browser/navigation_controller.h" |
| 58 #include "content/public/browser/navigation_entry.h" | 59 #include "content/public/browser/navigation_entry.h" |
| 59 #include "content/public/browser/render_view_host.h" | 60 #include "content/public/browser/render_view_host.h" |
| 60 #include "content/public/browser/web_contents.h" | 61 #include "content/public/browser/web_contents.h" |
| 61 #include "content/public/browser/web_ui.h" | 62 #include "content/public/browser/web_ui.h" |
| 62 #include "google_apis/gaia/oauth2_token_service.h" | 63 #include "google_apis/gaia/oauth2_token_service.h" |
| 63 #include "printing/backend/print_backend.h" | 64 #include "printing/backend/print_backend.h" |
| 64 #include "printing/backend/print_backend_consts.h" | 65 #include "printing/backend/print_backend_consts.h" |
| 65 #include "printing/metafile.h" | 66 #include "printing/metafile.h" |
| 66 #include "printing/metafile_impl.h" | 67 #include "printing/metafile_impl.h" |
| 68 #include "printing/page_range.h" | |
| 67 #include "printing/pdf_render_settings.h" | 69 #include "printing/pdf_render_settings.h" |
| 68 #include "printing/print_settings.h" | 70 #include "printing/print_settings.h" |
| 71 #include "printing/printing_context.h" | |
| 69 #include "printing/units.h" | 72 #include "printing/units.h" |
| 70 #include "third_party/icu/source/i18n/unicode/ulocdata.h" | 73 #include "third_party/icu/source/i18n/unicode/ulocdata.h" |
| 71 | 74 |
| 72 #if defined(OS_CHROMEOS) | 75 #if defined(OS_CHROMEOS) |
| 73 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" | 76 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" |
| 74 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h " | 77 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h " |
| 75 #endif | 78 #endif |
| 76 | 79 |
| 77 #if defined(ENABLE_SERVICE_DISCOVERY) | 80 #if defined(ENABLE_SERVICE_DISCOVERY) |
| 78 #include "chrome/browser/local_discovery/privet_constants.h" | 81 #include "chrome/browser/local_discovery/privet_constants.h" |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 269 | 272 |
| 270 std::string GetDefaultPrinterOnFileThread( | 273 std::string GetDefaultPrinterOnFileThread( |
| 271 scoped_refptr<printing::PrintBackend> print_backend) { | 274 scoped_refptr<printing::PrintBackend> print_backend) { |
| 272 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 275 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 273 | 276 |
| 274 std::string default_printer = print_backend->GetDefaultPrinterName(); | 277 std::string default_printer = print_backend->GetDefaultPrinterName(); |
| 275 VLOG(1) << "Default Printer: " << default_printer; | 278 VLOG(1) << "Default Printer: " << default_printer; |
| 276 return default_printer; | 279 return default_printer; |
| 277 } | 280 } |
| 278 | 281 |
| 282 gfx::Size GetDefaultPdfMediaSizeMicrons() { | |
| 283 scoped_ptr<printing::PrintingContext> printing_context( | |
| 284 printing::PrintingContext::Create( | |
| 285 g_browser_process->GetApplicationLocale())); | |
| 286 scoped_ptr<base::DictionaryValue> pdf_settings(new base::DictionaryValue); | |
| 287 pdf_settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); | |
| 288 pdf_settings->SetBoolean(printing::kSettingShouldPrintBackgrounds, false); | |
| 289 pdf_settings->SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); | |
| 290 pdf_settings->SetInteger(printing::kSettingMarginsType, printing::NO_MARGINS); | |
| 291 pdf_settings->SetBoolean(printing::kSettingCollate, true); | |
| 292 pdf_settings->SetInteger(printing::kSettingCopies, 1); | |
| 293 pdf_settings->SetInteger(printing::kSettingColor, printing::COLOR); | |
| 294 pdf_settings->SetInteger(printing::kSettingDuplexMode, printing::SIMPLEX); | |
| 295 pdf_settings->SetBoolean(printing::kSettingLandscape, false); | |
|
Vitaly Buka (NO REVIEWS)
2014/05/20 23:53:02
move to context
Aleksey Shlyapnikov
2014/05/22 00:08:14
Done.
| |
| 296 pdf_settings->SetString(printing::kSettingDeviceName, ""); | |
| 297 pdf_settings->SetBoolean(printing::kSettingPrintToPDF, true); | |
| 298 pdf_settings->SetBoolean(printing::kSettingCloudPrintDialog, false); | |
| 299 pdf_settings->SetBoolean(printing::kSettingPrintWithPrivet, false); | |
| 300 | |
| 301 printing::PrintingContext::Result result = | |
| 302 printing_context->UpdatePrintSettings(*pdf_settings, | |
| 303 printing::PageRanges()); | |
| 304 if (printing::PrintingContext::OK != result || | |
| 305 printing_context->settings().device_units_per_inch() <= 0) { | |
| 306 return gfx::Size(); | |
| 307 } | |
| 308 gfx::Size pdf_media_size = printing_context->GetPdfPaperSizeDeviceUnits(); | |
| 309 float deviceMicronsPerDeviceUnit = | |
| 310 (printing::kHundrethsMMPerInch * 10.0f) / | |
| 311 printing_context->settings().device_units_per_inch(); | |
| 312 return gfx::Size(pdf_media_size.width() * deviceMicronsPerDeviceUnit, | |
| 313 pdf_media_size.height() * deviceMicronsPerDeviceUnit); | |
| 314 } | |
| 315 | |
| 316 typedef base::Callback<void(const base::DictionaryValue*)> | |
| 317 GetPdfCapabilitiesCallback; | |
| 318 | |
| 319 const struct { | |
| 320 int width_microns; | |
| 321 int height_microns; | |
| 322 const char* name; | |
| 323 } PDF_MEDIA_SIZE[] = { | |
| 324 { | |
| 325 209900, /*x*/ 297000, | |
| 326 "ISO_A4" | |
| 327 }, | |
| 328 { | |
| 329 297000, /*x*/ 420000, | |
| 330 "ISO_A3" | |
| 331 }, | |
| 332 { | |
| 333 215900, /*x*/ 279400, | |
| 334 "NA_LETTER" | |
| 335 }, | |
| 336 { | |
| 337 215900, /*x*/ 355600, | |
| 338 "NA_LEGAL" | |
| 339 }, | |
| 340 { | |
| 341 279400, /*x*/ 431800, | |
| 342 "NA_LEDGER" | |
| 343 } | |
| 344 }; | |
| 345 | |
| 346 void GetPdfCapabilitiesOnFileThread( | |
| 347 scoped_refptr<printing::PrintBackend> print_backend, | |
| 348 const GetPdfCapabilitiesCallback& capabilities_cb) { | |
| 349 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 350 | |
| 351 gfx::Size default_media_size = GetDefaultPdfMediaSizeMicrons(); | |
| 352 | |
| 353 // Create a list of media. | |
| 354 scoped_ptr<base::ListValue> media_size_option(new base::ListValue); | |
| 355 | |
| 356 base::DictionaryValue* default_media_item = 0; | |
| 357 int max_diff = default_media_size.width() + default_media_size.height(); | |
| 358 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(PDF_MEDIA_SIZE); ++i) { | |
| 359 scoped_ptr<base::DictionaryValue> media_item(new base::DictionaryValue); | |
| 360 media_item->SetInteger("width_microns", PDF_MEDIA_SIZE[i].width_microns); | |
| 361 media_item->SetInteger("height_microns", PDF_MEDIA_SIZE[i].height_microns); | |
| 362 media_item->SetString("name", PDF_MEDIA_SIZE[i].name); | |
| 363 int diff = | |
| 364 abs(default_media_size.width() - PDF_MEDIA_SIZE[i].width_microns) + | |
| 365 abs(default_media_size.height() - PDF_MEDIA_SIZE[i].height_microns); | |
| 366 if (max_diff >= diff) { | |
|
Vitaly Buka (NO REVIEWS)
2014/05/20 23:53:02
TEST(PrinterDescriptionTest, CddSetAll)
Aleksey Shlyapnikov
2014/05/22 00:08:14
Done.
| |
| 367 max_diff = diff; | |
| 368 default_media_item = media_item.get(); | |
| 369 } | |
| 370 media_size_option->Append(media_item.release()); | |
| 371 } | |
| 372 if (!default_media_item) { | |
| 373 media_size_option->GetDictionary(0, &default_media_item); | |
| 374 } | |
| 375 default_media_item->SetBoolean("is_default", true); | |
| 376 | |
| 377 scoped_ptr<base::DictionaryValue> media_size(new base::DictionaryValue); | |
| 378 media_size->Set("option", media_size_option.release()); | |
| 379 | |
| 380 scoped_ptr<base::DictionaryValue> printer(new base::DictionaryValue); | |
| 381 printer->Set("media_size", media_size.release()); | |
| 382 | |
| 383 scoped_ptr<base::DictionaryValue> capabilities(new base::DictionaryValue); | |
| 384 capabilities->Set("printer", printer.release()); | |
| 385 | |
| 386 BrowserThread::PostTask( | |
| 387 BrowserThread::UI, FROM_HERE, | |
| 388 base::Bind(capabilities_cb, base::Owned(capabilities.release()))); | |
| 389 } | |
| 390 | |
| 279 void EnumeratePrintersOnFileThread( | 391 void EnumeratePrintersOnFileThread( |
| 280 scoped_refptr<printing::PrintBackend> print_backend, | 392 scoped_refptr<printing::PrintBackend> print_backend, |
| 281 base::ListValue* printers) { | 393 base::ListValue* printers) { |
| 282 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 394 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 283 | 395 |
| 284 VLOG(1) << "Enumerate printers start"; | 396 VLOG(1) << "Enumerate printers start"; |
| 285 printing::PrinterList printer_list; | 397 printing::PrinterList printer_list; |
| 286 print_backend->EnumeratePrinters(&printer_list); | 398 print_backend->EnumeratePrinters(&printer_list); |
| 287 | 399 |
| 288 for (printing::PrinterList::iterator it = printer_list.begin(); | 400 for (printing::PrinterList::iterator it = printer_list.begin(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 typedef base::Callback<void(const base::DictionaryValue*)> | 438 typedef base::Callback<void(const base::DictionaryValue*)> |
| 327 GetPrinterCapabilitiesSuccessCallback; | 439 GetPrinterCapabilitiesSuccessCallback; |
| 328 typedef base::Callback<void(const std::string&)> | 440 typedef base::Callback<void(const std::string&)> |
| 329 GetPrinterCapabilitiesFailureCallback; | 441 GetPrinterCapabilitiesFailureCallback; |
| 330 | 442 |
| 331 void GetPrinterCapabilitiesOnFileThread( | 443 void GetPrinterCapabilitiesOnFileThread( |
| 332 scoped_refptr<printing::PrintBackend> print_backend, | 444 scoped_refptr<printing::PrintBackend> print_backend, |
| 333 const std::string& printer_name, | 445 const std::string& printer_name, |
| 334 const GetPrinterCapabilitiesSuccessCallback& success_cb, | 446 const GetPrinterCapabilitiesSuccessCallback& success_cb, |
| 335 const GetPrinterCapabilitiesFailureCallback& failure_cb) { | 447 const GetPrinterCapabilitiesFailureCallback& failure_cb) { |
| 336 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 448 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
|
Vitaly Buka (NO REVIEWS)
2014/05/20 23:53:02
calculate here?
Aleksey Shlyapnikov
2014/05/22 00:08:14
Done.
| |
| 337 DCHECK(!printer_name.empty()); | 449 DCHECK(!printer_name.empty()); |
| 338 | 450 |
| 339 VLOG(1) << "Get printer capabilities start for " << printer_name; | 451 VLOG(1) << "Get printer capabilities start for " << printer_name; |
| 340 crash_keys::ScopedPrinterInfo crash_key( | 452 crash_keys::ScopedPrinterInfo crash_key( |
| 341 print_backend->GetPrinterDriverInfo(printer_name)); | 453 print_backend->GetPrinterDriverInfo(printer_name)); |
| 342 | 454 |
| 343 if (!print_backend->IsValidPrinter(printer_name)) { | 455 if (!print_backend->IsValidPrinter(printer_name)) { |
| 344 // TODO(gene): Notify explicitly if printer is not valid, instead of | 456 // TODO(gene): Notify explicitly if printer is not valid, instead of |
| 345 // failed to get capabilities. | 457 // failed to get capabilities. |
| 346 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 458 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 526 base::Unretained(this))); | 638 base::Unretained(this))); |
| 527 web_ui()->RegisterMessageCallback("cancelPendingPrintRequest", | 639 web_ui()->RegisterMessageCallback("cancelPendingPrintRequest", |
| 528 base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest, | 640 base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest, |
| 529 base::Unretained(this))); | 641 base::Unretained(this))); |
| 530 web_ui()->RegisterMessageCallback("saveAppState", | 642 web_ui()->RegisterMessageCallback("saveAppState", |
| 531 base::Bind(&PrintPreviewHandler::HandleSaveAppState, | 643 base::Bind(&PrintPreviewHandler::HandleSaveAppState, |
| 532 base::Unretained(this))); | 644 base::Unretained(this))); |
| 533 web_ui()->RegisterMessageCallback("getInitialSettings", | 645 web_ui()->RegisterMessageCallback("getInitialSettings", |
| 534 base::Bind(&PrintPreviewHandler::HandleGetInitialSettings, | 646 base::Bind(&PrintPreviewHandler::HandleGetInitialSettings, |
| 535 base::Unretained(this))); | 647 base::Unretained(this))); |
| 648 web_ui()->RegisterMessageCallback("getPdfCapabilities", | |
| 649 base::Bind(&PrintPreviewHandler::HandleGetPdfCapabilities, | |
| 650 base::Unretained(this))); | |
| 536 web_ui()->RegisterMessageCallback("reportUiEvent", | 651 web_ui()->RegisterMessageCallback("reportUiEvent", |
| 537 base::Bind(&PrintPreviewHandler::HandleReportUiEvent, | 652 base::Bind(&PrintPreviewHandler::HandleReportUiEvent, |
| 538 base::Unretained(this))); | 653 base::Unretained(this))); |
| 539 web_ui()->RegisterMessageCallback("printWithCloudPrintDialog", | 654 web_ui()->RegisterMessageCallback("printWithCloudPrintDialog", |
| 540 base::Bind(&PrintPreviewHandler::HandlePrintWithCloudPrintDialog, | 655 base::Bind(&PrintPreviewHandler::HandlePrintWithCloudPrintDialog, |
| 541 base::Unretained(this))); | 656 base::Unretained(this))); |
| 542 web_ui()->RegisterMessageCallback("forceOpenNewTab", | 657 web_ui()->RegisterMessageCallback("forceOpenNewTab", |
| 543 base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab, | 658 base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab, |
| 544 base::Unretained(this))); | 659 base::Unretained(this))); |
| 545 web_ui()->RegisterMessageCallback("getPrivetPrinters", | 660 web_ui()->RegisterMessageCallback("getPrivetPrinters", |
| (...skipping 493 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1039 const base::ListValue* /*args*/) { | 1154 const base::ListValue* /*args*/) { |
| 1040 // Send before SendInitialSettings to allow cloud printer auto select. | 1155 // Send before SendInitialSettings to allow cloud printer auto select. |
| 1041 SendCloudPrintEnabled(); | 1156 SendCloudPrintEnabled(); |
| 1042 BrowserThread::PostTaskAndReplyWithResult( | 1157 BrowserThread::PostTaskAndReplyWithResult( |
| 1043 BrowserThread::FILE, FROM_HERE, | 1158 BrowserThread::FILE, FROM_HERE, |
| 1044 base::Bind(&GetDefaultPrinterOnFileThread, print_backend_), | 1159 base::Bind(&GetDefaultPrinterOnFileThread, print_backend_), |
| 1045 base::Bind(&PrintPreviewHandler::SendInitialSettings, | 1160 base::Bind(&PrintPreviewHandler::SendInitialSettings, |
| 1046 weak_factory_.GetWeakPtr())); | 1161 weak_factory_.GetWeakPtr())); |
| 1047 } | 1162 } |
| 1048 | 1163 |
| 1164 void PrintPreviewHandler::HandleGetPdfCapabilities( | |
| 1165 const base::ListValue* /*args*/) { | |
| 1166 GetPdfCapabilitiesCallback capabilities_cb = | |
| 1167 base::Bind(&PrintPreviewHandler::SendPdfCapabilities, | |
| 1168 weak_factory_.GetWeakPtr()); | |
| 1169 BrowserThread::PostTask( | |
| 1170 BrowserThread::FILE, FROM_HERE, | |
| 1171 base::Bind(&GetPdfCapabilitiesOnFileThread, | |
| 1172 print_backend_, capabilities_cb)); | |
| 1173 } | |
| 1174 | |
| 1049 void PrintPreviewHandler::HandleReportUiEvent(const base::ListValue* args) { | 1175 void PrintPreviewHandler::HandleReportUiEvent(const base::ListValue* args) { |
| 1050 int event_group, event_number; | 1176 int event_group, event_number; |
| 1051 if (!args->GetInteger(0, &event_group) || !args->GetInteger(1, &event_number)) | 1177 if (!args->GetInteger(0, &event_group) || !args->GetInteger(1, &event_number)) |
| 1052 return; | 1178 return; |
| 1053 | 1179 |
| 1054 enum UiBucketGroups ui_bucket_group = | 1180 enum UiBucketGroups ui_bucket_group = |
| 1055 static_cast<enum UiBucketGroups>(event_group); | 1181 static_cast<enum UiBucketGroups>(event_group); |
| 1056 if (ui_bucket_group >= UI_BUCKET_GROUP_BOUNDARY) | 1182 if (ui_bucket_group >= UI_BUCKET_GROUP_BOUNDARY) |
| 1057 return; | 1183 return; |
| 1058 | 1184 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1121 // metro. | 1247 // metro. |
| 1122 bool is_ash = (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH); | 1248 bool is_ash = (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH); |
| 1123 initial_settings.SetBoolean(kHidePrintWithSystemDialogLink, is_ash); | 1249 initial_settings.SetBoolean(kHidePrintWithSystemDialogLink, is_ash); |
| 1124 #endif | 1250 #endif |
| 1125 | 1251 |
| 1126 if (print_preview_ui->source_is_modifiable()) | 1252 if (print_preview_ui->source_is_modifiable()) |
| 1127 GetNumberFormatAndMeasurementSystem(&initial_settings); | 1253 GetNumberFormatAndMeasurementSystem(&initial_settings); |
| 1128 web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings); | 1254 web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings); |
| 1129 } | 1255 } |
| 1130 | 1256 |
| 1257 void PrintPreviewHandler::SendPdfCapabilities( | |
| 1258 const base::DictionaryValue* capabilities) { | |
| 1259 web_ui()->CallJavascriptFunction("setPdfCapabilities", *capabilities); | |
| 1260 } | |
| 1261 | |
| 1131 void PrintPreviewHandler::ClosePreviewDialog() { | 1262 void PrintPreviewHandler::ClosePreviewDialog() { |
| 1132 PrintPreviewUI* print_preview_ui = | 1263 PrintPreviewUI* print_preview_ui = |
| 1133 static_cast<PrintPreviewUI*>(web_ui()->GetController()); | 1264 static_cast<PrintPreviewUI*>(web_ui()->GetController()); |
| 1134 print_preview_ui->OnClosePrintPreviewDialog(); | 1265 print_preview_ui->OnClosePrintPreviewDialog(); |
| 1135 } | 1266 } |
| 1136 | 1267 |
| 1137 void PrintPreviewHandler::SendAccessToken(const std::string& type, | 1268 void PrintPreviewHandler::SendAccessToken(const std::string& type, |
| 1138 const std::string& access_token) { | 1269 const std::string& access_token) { |
| 1139 VLOG(1) << "Get getAccessToken finished"; | 1270 VLOG(1) << "Get getAccessToken finished"; |
| 1140 web_ui()->CallJavascriptFunction("onDidGetAccessToken", | 1271 web_ui()->CallJavascriptFunction("onDidGetAccessToken", |
| (...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1566 printer_value->SetString("name", description.name); | 1697 printer_value->SetString("name", description.name); |
| 1567 printer_value->SetBoolean("hasLocalPrinting", has_local_printing); | 1698 printer_value->SetBoolean("hasLocalPrinting", has_local_printing); |
| 1568 printer_value->SetBoolean( | 1699 printer_value->SetBoolean( |
| 1569 "isUnregistered", | 1700 "isUnregistered", |
| 1570 description.id.empty() && | 1701 description.id.empty() && |
| 1571 command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)); | 1702 command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)); |
| 1572 printer_value->SetString("cloudID", description.id); | 1703 printer_value->SetString("cloudID", description.id); |
| 1573 } | 1704 } |
| 1574 | 1705 |
| 1575 #endif | 1706 #endif |
| OLD | NEW |