OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/libgtkui/print_dialog_gtk2.h" | |
6 | |
7 #include <gtk/gtkunixprint.h> | |
8 | |
9 #include <algorithm> | |
10 #include <cmath> | |
11 #include <string> | |
12 #include <vector> | |
13 | |
14 #include "base/bind.h" | |
15 #include "base/files/file_util.h" | |
16 #include "base/files/file_util_proxy.h" | |
17 #include "base/lazy_instance.h" | |
18 #include "base/logging.h" | |
19 #include "base/macros.h" | |
20 #include "base/strings/utf_string_conversions.h" | |
21 #include "base/values.h" | |
22 #include "chrome/browser/ui/libgtkui/gtk2_util.h" | |
23 #include "chrome/browser/ui/libgtkui/printing_gtk2_util.h" | |
24 #include "printing/metafile.h" | |
25 #include "printing/print_job_constants.h" | |
26 #include "printing/print_settings.h" | |
27 #include "ui/aura/window.h" | |
28 #include "ui/events/platform/x11/x11_event_source.h" | |
29 | |
30 using content::BrowserThread; | |
31 using printing::PageRanges; | |
32 using printing::PrintSettings; | |
33 | |
34 namespace { | |
35 | |
36 #if defined(USE_CUPS) | |
37 // CUPS Duplex attribute and values. | |
38 const char kCUPSDuplex[] = "cups-Duplex"; | |
39 const char kDuplexNone[] = "None"; | |
40 const char kDuplexTumble[] = "DuplexTumble"; | |
41 const char kDuplexNoTumble[] = "DuplexNoTumble"; | |
42 #endif | |
43 | |
44 int kPaperSizeTresholdMicrons = 100; | |
45 int kMicronsInMm = 1000; | |
46 | |
47 // Checks whether gtk_paper_size can be used to represent user selected media. | |
48 // In fuzzy match mode checks that paper sizes are "close enough" (less than | |
49 // 1mm difference). In the exact mode, looks for the paper with the same PPD | |
50 // name and "close enough" size. | |
51 bool PaperSizeMatch(GtkPaperSize* gtk_paper_size, | |
52 const PrintSettings::RequestedMedia& media, | |
53 bool fuzzy_match) { | |
54 if (!gtk_paper_size) { | |
55 return false; | |
56 } | |
57 gfx::Size paper_size_microns( | |
58 static_cast<int>(gtk_paper_size_get_width(gtk_paper_size, GTK_UNIT_MM) * | |
59 kMicronsInMm + 0.5), | |
60 static_cast<int>(gtk_paper_size_get_height(gtk_paper_size, GTK_UNIT_MM) * | |
61 kMicronsInMm + 0.5)); | |
62 int diff = std::max( | |
63 std::abs(paper_size_microns.width() - media.size_microns.width()), | |
64 std::abs(paper_size_microns.height() - media.size_microns.height())); | |
65 if (fuzzy_match) { | |
66 return diff <= kPaperSizeTresholdMicrons; | |
67 } | |
68 return !media.vendor_id.empty() && | |
69 media.vendor_id == gtk_paper_size_get_ppd_name(gtk_paper_size) && | |
70 diff <= kPaperSizeTresholdMicrons; | |
71 } | |
72 | |
73 // Looks up a paper size matching (in terms of PaperSizeMatch) the user selected | |
74 // media in the paper size list reported by GTK. Returns NULL if there's no | |
75 // match found. | |
76 GtkPaperSize* FindPaperSizeMatch(GList* gtk_paper_sizes, | |
77 const PrintSettings::RequestedMedia& media) { | |
78 GtkPaperSize* first_fuzzy_match = NULL; | |
79 for (GList* p = gtk_paper_sizes; p && p->data; p = g_list_next(p)) { | |
80 GtkPaperSize* gtk_paper_size = static_cast<GtkPaperSize*>(p->data); | |
81 if (PaperSizeMatch(gtk_paper_size, media, false)) { | |
82 return gtk_paper_size; | |
83 } | |
84 if (!first_fuzzy_match && PaperSizeMatch(gtk_paper_size, media, true)) { | |
85 first_fuzzy_match = gtk_paper_size; | |
86 } | |
87 } | |
88 return first_fuzzy_match; | |
89 } | |
90 | |
91 class StickyPrintSettingGtk { | |
92 public: | |
93 StickyPrintSettingGtk() : last_used_settings_(gtk_print_settings_new()) { | |
94 } | |
95 ~StickyPrintSettingGtk() { | |
96 NOTREACHED(); // Intended to be used with a Leaky LazyInstance. | |
97 } | |
98 | |
99 GtkPrintSettings* settings() { | |
100 return last_used_settings_; | |
101 } | |
102 | |
103 void SetLastUsedSettings(GtkPrintSettings* settings) { | |
104 DCHECK(last_used_settings_); | |
105 g_object_unref(last_used_settings_); | |
106 last_used_settings_ = gtk_print_settings_copy(settings); | |
107 } | |
108 | |
109 private: | |
110 GtkPrintSettings* last_used_settings_; | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(StickyPrintSettingGtk); | |
113 }; | |
114 | |
115 base::LazyInstance<StickyPrintSettingGtk>::Leaky g_last_used_settings = | |
116 LAZY_INSTANCE_INITIALIZER; | |
117 | |
118 // Helper class to track GTK printers. | |
119 class GtkPrinterList { | |
120 public: | |
121 GtkPrinterList() : default_printer_(NULL) { | |
122 gtk_enumerate_printers(SetPrinter, this, NULL, TRUE); | |
123 } | |
124 | |
125 ~GtkPrinterList() { | |
126 for (std::vector<GtkPrinter*>::iterator it = printers_.begin(); | |
127 it < printers_.end(); ++it) { | |
128 g_object_unref(*it); | |
129 } | |
130 } | |
131 | |
132 // Can return NULL if there's no default printer. E.g. Printer on a laptop | |
133 // is "home_printer", but the laptop is at work. | |
134 GtkPrinter* default_printer() { | |
135 return default_printer_; | |
136 } | |
137 | |
138 // Can return NULL if the printer cannot be found due to: | |
139 // - Printer list out of sync with printer dialog UI. | |
140 // - Querying for non-existant printers like 'Print to PDF'. | |
141 GtkPrinter* GetPrinterWithName(const std::string& name) { | |
142 if (name.empty()) | |
143 return NULL; | |
144 | |
145 for (std::vector<GtkPrinter*>::iterator it = printers_.begin(); | |
146 it < printers_.end(); ++it) { | |
147 if (gtk_printer_get_name(*it) == name) { | |
148 return *it; | |
149 } | |
150 } | |
151 | |
152 return NULL; | |
153 } | |
154 | |
155 private: | |
156 // Callback function used by gtk_enumerate_printers() to get all printer. | |
157 static gboolean SetPrinter(GtkPrinter* printer, gpointer data) { | |
158 GtkPrinterList* printer_list = reinterpret_cast<GtkPrinterList*>(data); | |
159 if (gtk_printer_is_default(printer)) | |
160 printer_list->default_printer_ = printer; | |
161 | |
162 g_object_ref(printer); | |
163 printer_list->printers_.push_back(printer); | |
164 | |
165 return FALSE; | |
166 } | |
167 | |
168 std::vector<GtkPrinter*> printers_; | |
169 GtkPrinter* default_printer_; | |
170 }; | |
171 | |
172 } // namespace | |
173 | |
174 // static | |
175 printing::PrintDialogGtkInterface* PrintDialogGtk2::CreatePrintDialog( | |
176 PrintingContextLinux* context) { | |
177 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
178 return new PrintDialogGtk2(context); | |
179 } | |
180 | |
181 PrintDialogGtk2::PrintDialogGtk2(PrintingContextLinux* context) | |
182 : context_(context), | |
183 dialog_(NULL), | |
184 gtk_settings_(NULL), | |
185 page_setup_(NULL), | |
186 printer_(NULL) { | |
187 } | |
188 | |
189 PrintDialogGtk2::~PrintDialogGtk2() { | |
190 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
191 | |
192 if (dialog_) { | |
193 aura::Window* parent = libgtkui::GetAuraTransientParent(dialog_); | |
194 if (parent) { | |
195 parent->RemoveObserver(this); | |
196 libgtkui::ClearAuraTransientParent(dialog_); | |
197 } | |
198 gtk_widget_destroy(dialog_); | |
199 dialog_ = NULL; | |
200 } | |
201 if (gtk_settings_) { | |
202 g_object_unref(gtk_settings_); | |
203 gtk_settings_ = NULL; | |
204 } | |
205 if (page_setup_) { | |
206 g_object_unref(page_setup_); | |
207 page_setup_ = NULL; | |
208 } | |
209 if (printer_) { | |
210 g_object_unref(printer_); | |
211 printer_ = NULL; | |
212 } | |
213 } | |
214 | |
215 void PrintDialogGtk2::UseDefaultSettings() { | |
216 DCHECK(!page_setup_); | |
217 DCHECK(!printer_); | |
218 | |
219 // |gtk_settings_| is a new copy. | |
220 gtk_settings_ = | |
221 gtk_print_settings_copy(g_last_used_settings.Get().settings()); | |
222 page_setup_ = gtk_page_setup_new(); | |
223 | |
224 PrintSettings settings; | |
225 InitPrintSettings(&settings); | |
226 } | |
227 | |
228 bool PrintDialogGtk2::UpdateSettings(printing::PrintSettings* settings) { | |
229 if (!gtk_settings_) { | |
230 gtk_settings_ = | |
231 gtk_print_settings_copy(g_last_used_settings.Get().settings()); | |
232 } | |
233 | |
234 std::unique_ptr<GtkPrinterList> printer_list(new GtkPrinterList); | |
235 printer_ = printer_list->GetPrinterWithName( | |
236 base::UTF16ToUTF8(settings->device_name())); | |
237 if (printer_) { | |
238 g_object_ref(printer_); | |
239 gtk_print_settings_set_printer(gtk_settings_, | |
240 gtk_printer_get_name(printer_)); | |
241 if (!page_setup_) { | |
242 page_setup_ = gtk_printer_get_default_page_size(printer_); | |
243 } | |
244 } | |
245 | |
246 gtk_print_settings_set_n_copies(gtk_settings_, settings->copies()); | |
247 gtk_print_settings_set_collate(gtk_settings_, settings->collate()); | |
248 | |
249 #if defined(USE_CUPS) | |
250 std::string color_value; | |
251 std::string color_setting_name; | |
252 printing::GetColorModelForMode(settings->color(), &color_setting_name, | |
253 &color_value); | |
254 gtk_print_settings_set(gtk_settings_, color_setting_name.c_str(), | |
255 color_value.c_str()); | |
256 | |
257 if (settings->duplex_mode() != printing::UNKNOWN_DUPLEX_MODE) { | |
258 const char* cups_duplex_mode = NULL; | |
259 switch (settings->duplex_mode()) { | |
260 case printing::LONG_EDGE: | |
261 cups_duplex_mode = kDuplexNoTumble; | |
262 break; | |
263 case printing::SHORT_EDGE: | |
264 cups_duplex_mode = kDuplexTumble; | |
265 break; | |
266 case printing::SIMPLEX: | |
267 cups_duplex_mode = kDuplexNone; | |
268 break; | |
269 default: // UNKNOWN_DUPLEX_MODE | |
270 NOTREACHED(); | |
271 break; | |
272 } | |
273 gtk_print_settings_set(gtk_settings_, kCUPSDuplex, cups_duplex_mode); | |
274 } | |
275 #endif | |
276 if (!page_setup_) | |
277 page_setup_ = gtk_page_setup_new(); | |
278 | |
279 if (page_setup_ && !settings->requested_media().IsDefault()) { | |
280 const PrintSettings::RequestedMedia& requested_media = | |
281 settings->requested_media(); | |
282 GtkPaperSize* gtk_current_paper_size = | |
283 gtk_page_setup_get_paper_size(page_setup_); | |
284 if (!PaperSizeMatch(gtk_current_paper_size, requested_media, | |
285 true /*fuzzy_match*/)) { | |
286 GList* gtk_paper_sizes = | |
287 gtk_paper_size_get_paper_sizes(false /*include_custom*/); | |
288 if (gtk_paper_sizes) { | |
289 GtkPaperSize* matching_gtk_paper_size = | |
290 FindPaperSizeMatch(gtk_paper_sizes, requested_media); | |
291 if (matching_gtk_paper_size) { | |
292 VLOG(1) << "Using listed paper size"; | |
293 gtk_page_setup_set_paper_size(page_setup_, matching_gtk_paper_size); | |
294 } else { | |
295 VLOG(1) << "Using custom paper size"; | |
296 GtkPaperSize* custom_size = gtk_paper_size_new_custom( | |
297 requested_media.vendor_id.c_str(), | |
298 requested_media.vendor_id.c_str(), | |
299 requested_media.size_microns.width() / kMicronsInMm, | |
300 requested_media.size_microns.height() / kMicronsInMm, | |
301 GTK_UNIT_MM); | |
302 gtk_page_setup_set_paper_size(page_setup_, custom_size); | |
303 gtk_paper_size_free(custom_size); | |
304 } | |
305 #if GTK_CHECK_VERSION(2,28,0) | |
306 g_list_free_full(gtk_paper_sizes, | |
307 reinterpret_cast<GDestroyNotify>(gtk_paper_size_free)); | |
308 #else | |
309 g_list_foreach(gtk_paper_sizes, | |
310 reinterpret_cast<GFunc>(gtk_paper_size_free), NULL); | |
311 g_list_free(gtk_paper_sizes); | |
312 #endif | |
313 } | |
314 } else { | |
315 VLOG(1) << "Using default paper size"; | |
316 } | |
317 } | |
318 | |
319 gtk_print_settings_set_orientation( | |
320 gtk_settings_, | |
321 settings->landscape() ? GTK_PAGE_ORIENTATION_LANDSCAPE : | |
322 GTK_PAGE_ORIENTATION_PORTRAIT); | |
323 | |
324 InitPrintSettings(settings); | |
325 return true; | |
326 } | |
327 | |
328 void PrintDialogGtk2::ShowDialog( | |
329 gfx::NativeView parent_view, | |
330 bool has_selection, | |
331 const PrintingContextLinux::PrintSettingsCallback& callback) { | |
332 callback_ = callback; | |
333 DCHECK(!callback_.is_null()); | |
334 | |
335 dialog_ = gtk_print_unix_dialog_new(NULL, NULL); | |
336 libgtkui::SetGtkTransientForAura(dialog_, parent_view); | |
337 if (parent_view) | |
338 parent_view->AddObserver(this); | |
339 g_signal_connect(dialog_, "delete-event", | |
340 G_CALLBACK(gtk_widget_hide_on_delete), NULL); | |
341 | |
342 // Handle the case when the existing |gtk_settings_| has "selection" selected | |
343 // as the page range, but |has_selection| is false. | |
344 if (!has_selection) { | |
345 GtkPrintPages range = gtk_print_settings_get_print_pages(gtk_settings_); | |
346 if (range == GTK_PRINT_PAGES_SELECTION) | |
347 gtk_print_settings_set_print_pages(gtk_settings_, GTK_PRINT_PAGES_ALL); | |
348 } | |
349 | |
350 // Set modal so user cannot focus the same tab and press print again. | |
351 gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); | |
352 | |
353 // Since we only generate PDF, only show printers that support PDF. | |
354 // TODO(thestig) Add more capabilities to support? | |
355 GtkPrintCapabilities cap = static_cast<GtkPrintCapabilities>( | |
356 GTK_PRINT_CAPABILITY_GENERATE_PDF | | |
357 GTK_PRINT_CAPABILITY_PAGE_SET | | |
358 GTK_PRINT_CAPABILITY_COPIES | | |
359 GTK_PRINT_CAPABILITY_COLLATE | | |
360 GTK_PRINT_CAPABILITY_REVERSE); | |
361 gtk_print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(dialog_), | |
362 cap); | |
363 gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_), | |
364 TRUE); | |
365 gtk_print_unix_dialog_set_support_selection(GTK_PRINT_UNIX_DIALOG(dialog_), | |
366 TRUE); | |
367 gtk_print_unix_dialog_set_has_selection(GTK_PRINT_UNIX_DIALOG(dialog_), | |
368 has_selection); | |
369 gtk_print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(dialog_), | |
370 gtk_settings_); | |
371 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); | |
372 gtk_widget_show(dialog_); | |
373 | |
374 // We need to call gtk_window_present after making the widgets visible to make | |
375 // sure window gets correctly raised and gets focus. | |
376 gtk_window_present_with_time( | |
377 GTK_WINDOW(dialog_), ui::X11EventSource::GetInstance()->GetTimestamp()); | |
378 } | |
379 | |
380 void PrintDialogGtk2::PrintDocument(const printing::MetafilePlayer& metafile, | |
381 const base::string16& document_name) { | |
382 // This runs on the print worker thread, does not block the UI thread. | |
383 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
384 | |
385 // The document printing tasks can outlive the PrintingContext that created | |
386 // this dialog. | |
387 AddRef(); | |
388 | |
389 bool success = base::CreateTemporaryFile(&path_to_pdf_); | |
390 | |
391 if (success) { | |
392 base::File file; | |
393 file.Initialize(path_to_pdf_, | |
394 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | |
395 success = metafile.SaveTo(&file); | |
396 file.Close(); | |
397 if (!success) | |
398 base::DeleteFile(path_to_pdf_, false); | |
399 } | |
400 | |
401 if (!success) { | |
402 LOG(ERROR) << "Saving metafile failed"; | |
403 // Matches AddRef() above. | |
404 Release(); | |
405 return; | |
406 } | |
407 | |
408 // No errors, continue printing. | |
409 BrowserThread::PostTask( | |
410 BrowserThread::UI, | |
411 FROM_HERE, | |
412 base::Bind(&PrintDialogGtk2::SendDocumentToPrinter, this, document_name)); | |
413 } | |
414 | |
415 void PrintDialogGtk2::AddRefToDialog() { | |
416 AddRef(); | |
417 } | |
418 | |
419 void PrintDialogGtk2::ReleaseDialog() { | |
420 Release(); | |
421 } | |
422 | |
423 void PrintDialogGtk2::OnResponse(GtkWidget* dialog, int response_id) { | |
424 int num_matched_handlers = g_signal_handlers_disconnect_by_func( | |
425 dialog_, reinterpret_cast<gpointer>(&OnResponseThunk), this); | |
426 CHECK_EQ(1, num_matched_handlers); | |
427 | |
428 gtk_widget_hide(dialog_); | |
429 | |
430 switch (response_id) { | |
431 case GTK_RESPONSE_OK: { | |
432 if (gtk_settings_) | |
433 g_object_unref(gtk_settings_); | |
434 gtk_settings_ = gtk_print_unix_dialog_get_settings( | |
435 GTK_PRINT_UNIX_DIALOG(dialog_)); | |
436 | |
437 if (printer_) | |
438 g_object_unref(printer_); | |
439 printer_ = gtk_print_unix_dialog_get_selected_printer( | |
440 GTK_PRINT_UNIX_DIALOG(dialog_)); | |
441 g_object_ref(printer_); | |
442 | |
443 if (page_setup_) | |
444 g_object_unref(page_setup_); | |
445 page_setup_ = gtk_print_unix_dialog_get_page_setup( | |
446 GTK_PRINT_UNIX_DIALOG(dialog_)); | |
447 g_object_ref(page_setup_); | |
448 | |
449 // Handle page ranges. | |
450 PageRanges ranges_vector; | |
451 gint num_ranges; | |
452 bool print_selection_only = false; | |
453 switch (gtk_print_settings_get_print_pages(gtk_settings_)) { | |
454 case GTK_PRINT_PAGES_RANGES: { | |
455 GtkPageRange* gtk_range = | |
456 gtk_print_settings_get_page_ranges(gtk_settings_, &num_ranges); | |
457 if (gtk_range) { | |
458 for (int i = 0; i < num_ranges; ++i) { | |
459 printing::PageRange range; | |
460 range.from = gtk_range[i].start; | |
461 range.to = gtk_range[i].end; | |
462 ranges_vector.push_back(range); | |
463 } | |
464 g_free(gtk_range); | |
465 } | |
466 break; | |
467 } | |
468 case GTK_PRINT_PAGES_SELECTION: | |
469 print_selection_only = true; | |
470 break; | |
471 case GTK_PRINT_PAGES_ALL: | |
472 // Leave |ranges_vector| empty to indicate print all pages. | |
473 break; | |
474 case GTK_PRINT_PAGES_CURRENT: | |
475 default: | |
476 NOTREACHED(); | |
477 break; | |
478 } | |
479 | |
480 PrintSettings settings; | |
481 settings.set_ranges(ranges_vector); | |
482 settings.set_selection_only(print_selection_only); | |
483 InitPrintSettingsGtk(gtk_settings_, page_setup_, &settings); | |
484 context_->InitWithSettings(settings); | |
485 callback_.Run(PrintingContextLinux::OK); | |
486 callback_.Reset(); | |
487 return; | |
488 } | |
489 case GTK_RESPONSE_DELETE_EVENT: // Fall through. | |
490 case GTK_RESPONSE_CANCEL: { | |
491 callback_.Run(PrintingContextLinux::CANCEL); | |
492 callback_.Reset(); | |
493 return; | |
494 } | |
495 case GTK_RESPONSE_APPLY: | |
496 default: { | |
497 NOTREACHED(); | |
498 } | |
499 } | |
500 } | |
501 | |
502 | |
503 | |
504 static void OnJobCompletedThunk(GtkPrintJob* print_job, | |
505 gpointer user_data, | |
506 #if GTK_MAJOR_VERSION == 2 | |
507 GError* error | |
508 #else | |
509 const GError* error | |
510 #endif | |
511 ) { | |
512 static_cast<PrintDialogGtk2*>(user_data)->OnJobCompleted(print_job, error); | |
513 } | |
514 void PrintDialogGtk2::SendDocumentToPrinter( | |
515 const base::string16& document_name) { | |
516 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
517 | |
518 // If |printer_| is NULL then somehow the GTK printer list changed out under | |
519 // us. In which case, just bail out. | |
520 if (!printer_) { | |
521 // Matches AddRef() in PrintDocument(); | |
522 Release(); | |
523 return; | |
524 } | |
525 | |
526 // Save the settings for next time. | |
527 g_last_used_settings.Get().SetLastUsedSettings(gtk_settings_); | |
528 | |
529 GtkPrintJob* print_job = gtk_print_job_new( | |
530 base::UTF16ToUTF8(document_name).c_str(), | |
531 printer_, | |
532 gtk_settings_, | |
533 page_setup_); | |
534 gtk_print_job_set_source_file(print_job, path_to_pdf_.value().c_str(), NULL); | |
535 gtk_print_job_send(print_job, OnJobCompletedThunk, this, NULL); | |
536 } | |
537 | |
538 void PrintDialogGtk2::OnJobCompleted(GtkPrintJob* print_job, | |
539 const GError* error) { | |
540 if (error) | |
541 LOG(ERROR) << "Printing failed: " << error->message; | |
542 if (print_job) | |
543 g_object_unref(print_job); | |
544 base::FileUtilProxy::DeleteFile( | |
545 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(), | |
546 path_to_pdf_, false, base::FileUtilProxy::StatusCallback()); | |
547 // Printing finished. Matches AddRef() in PrintDocument(); | |
548 Release(); | |
549 } | |
550 | |
551 void PrintDialogGtk2::InitPrintSettings(PrintSettings* settings) { | |
552 InitPrintSettingsGtk(gtk_settings_, page_setup_, settings); | |
553 context_->InitWithSettings(*settings); | |
554 } | |
555 | |
556 void PrintDialogGtk2::OnWindowDestroying(aura::Window* window) { | |
557 DCHECK_EQ(libgtkui::GetAuraTransientParent(dialog_), window); | |
558 | |
559 libgtkui::ClearAuraTransientParent(dialog_); | |
560 window->RemoveObserver(this); | |
561 if (!callback_.is_null()) { | |
562 callback_.Run(PrintingContextLinux::CANCEL); | |
563 callback_.Reset(); | |
564 } | |
565 } | |
OLD | NEW |