1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "prtsetup.hxx"
21 #include <svdata.hxx>
22 #include <strings.hrc>
23 
24 #include <officecfg/Office/Common.hxx>
25 
26 using namespace psp;
27 
insertAllPPDValues(weld::ComboBox & rBox,const PPDParser * pParser,const PPDKey * pKey)28 void RTSDialog::insertAllPPDValues(weld::ComboBox& rBox, const PPDParser* pParser, const PPDKey* pKey )
29 {
30     if( ! pKey || ! pParser )
31         return;
32 
33     const PPDValue* pValue = nullptr;
34     OUString aOptionText;
35 
36     for (int i = 0; i < pKey->countValues(); ++i)
37     {
38         pValue = pKey->getValue( i );
39         if (pValue->m_bCustomOption)
40             continue;
41         aOptionText = pParser->translateOption( pKey->getKey(), pValue->m_aOption) ;
42 
43         OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pValue)));
44         int nCurrentPos = rBox.find_id(sId);
45         if( m_aJobData.m_aContext.checkConstraints( pKey, pValue ) )
46         {
47             if (nCurrentPos == -1)
48                 rBox.append(sId, aOptionText);
49         }
50         else
51         {
52             if (nCurrentPos != -1)
53                 rBox.remove(nCurrentPos);
54         }
55     }
56     pValue = m_aJobData.m_aContext.getValue( pKey );
57     if (pValue && !pValue->m_bCustomOption)
58     {
59         OUString sId(OUString::number(reinterpret_cast<sal_IntPtr>(pValue)));
60         int nPos = rBox.find_id(sId);
61         if (nPos != -1)
62             rBox.set_active(nPos);
63     }
64 }
65 
66 /*
67  * RTSDialog
68  */
69 
RTSDialog(const PrinterInfo & rJobData,weld::Window * pParent)70 RTSDialog::RTSDialog(const PrinterInfo& rJobData, weld::Window* pParent)
71     : GenericDialogController(pParent, "vcl/ui/printerpropertiesdialog.ui", "PrinterPropertiesDialog")
72     , m_aJobData(rJobData)
73     , m_bDataModified(false)
74     , m_xTabControl(m_xBuilder->weld_notebook("tabcontrol"))
75     , m_xOKButton(m_xBuilder->weld_button("ok"))
76     , m_xCancelButton(m_xBuilder->weld_button("cancel"))
77     , m_xPaperPage(new RTSPaperPage(m_xTabControl->get_page("paper"), this))
78     , m_xDevicePage(new RTSDevicePage(m_xTabControl->get_page("device"), this))
79 {
80     OUString aTitle(m_xDialog->get_title());
81     m_xDialog->set_title(aTitle.replaceAll("%s", m_aJobData.m_aPrinterName));
82 
83     m_xTabControl->connect_enter_page( LINK( this, RTSDialog, ActivatePage ) );
84     m_xOKButton->connect_clicked( LINK( this, RTSDialog, ClickButton ) );
85     m_xCancelButton->connect_clicked( LINK( this, RTSDialog, ClickButton ) );
86     ActivatePage(m_xTabControl->get_current_page_ident());
87 }
88 
~RTSDialog()89 RTSDialog::~RTSDialog()
90 {
91 }
92 
IMPL_LINK(RTSDialog,ActivatePage,const OString &,rPage,void)93 IMPL_LINK(RTSDialog, ActivatePage, const OString&, rPage, void)
94 {
95     if (rPage == "paper")
96         m_xPaperPage->update();
97 }
98 
IMPL_LINK(RTSDialog,ClickButton,weld::Button &,rButton,void)99 IMPL_LINK( RTSDialog, ClickButton, weld::Button&, rButton, void )
100 {
101     if (&rButton == m_xOKButton.get())
102     {
103         // refresh the changed values
104         if (m_xPaperPage)
105         {
106             // orientation
107             m_aJobData.m_eOrientation = m_xPaperPage->getOrientation() == 0 ?
108                 orientation::Portrait : orientation::Landscape;
109             // assume use of paper size from printer setup if the user
110             // got here via File > Printer Settings ...
111             if ( m_aJobData.meSetupMode == PrinterSetupMode::DocumentGlobal )
112                m_aJobData.m_bPapersizeFromSetup = true;
113         }
114         if( m_xDevicePage )
115         {
116             m_aJobData.m_nColorDepth    = m_xDevicePage->getDepth();
117             m_aJobData.m_nColorDevice   = m_xDevicePage->getColorDevice();
118             m_aJobData.m_nPSLevel       = m_xDevicePage->getLevel();
119             m_aJobData.m_nPDFDevice     = m_xDevicePage->getPDFDevice();
120         }
121         m_xDialog->response(RET_OK);
122     }
123     else if (&rButton == m_xCancelButton.get())
124         m_xDialog->response(RET_CANCEL);
125 }
126 
127 /*
128  * RTSPaperPage
129  */
130 
RTSPaperPage(weld::Widget * pPage,RTSDialog * pDialog)131 RTSPaperPage::RTSPaperPage(weld::Widget* pPage, RTSDialog* pDialog)
132     : m_xBuilder(Application::CreateBuilder(pPage, "vcl/ui/printerpaperpage.ui"))
133     , m_pParent(pDialog)
134     , m_xContainer(m_xBuilder->weld_widget("PrinterPaperPage"))
135     , m_xCbFromSetup(m_xBuilder->weld_check_button("papersizefromsetup"))
136     , m_xPaperText(m_xBuilder->weld_label("paperft"))
137     , m_xPaperBox(m_xBuilder->weld_combo_box("paperlb"))
138     , m_xOrientText(m_xBuilder->weld_label("orientft"))
139     , m_xOrientBox(m_xBuilder->weld_combo_box("orientlb"))
140     , m_xDuplexText(m_xBuilder->weld_label("duplexft"))
141     , m_xDuplexBox(m_xBuilder->weld_combo_box("duplexlb"))
142     , m_xSlotText(m_xBuilder->weld_label("slotft"))
143     , m_xSlotBox(m_xBuilder->weld_combo_box("slotlb"))
144 {
145     //PrinterPaperPage
146     m_xPaperBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
147     m_xOrientBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
148     m_xDuplexBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
149     m_xSlotBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
150     m_xCbFromSetup->connect_toggled( LINK( this, RTSPaperPage, CheckBoxHdl ) );
151 
152     update();
153 }
154 
~RTSPaperPage()155 RTSPaperPage::~RTSPaperPage()
156 {
157 }
158 
update()159 void RTSPaperPage::update()
160 {
161     const PPDKey* pKey      = nullptr;
162 
163     // orientation
164     m_xOrientBox->set_active(m_pParent->m_aJobData.m_eOrientation == orientation::Portrait ? 0 : 1);
165 
166     // duplex
167     if( m_pParent->m_aJobData.m_pParser &&
168         (pKey = m_pParent->m_aJobData.m_pParser->getKey( "Duplex" )) )
169     {
170         m_pParent->insertAllPPDValues( *m_xDuplexBox, m_pParent->m_aJobData.m_pParser, pKey );
171     }
172     else
173     {
174         m_xDuplexText->set_sensitive( false );
175         m_xDuplexBox->set_sensitive( false );
176     }
177 
178     // paper
179     if( m_pParent->m_aJobData.m_pParser &&
180         (pKey = m_pParent->m_aJobData.m_pParser->getKey( "PageSize" )) )
181     {
182         m_pParent->insertAllPPDValues( *m_xPaperBox, m_pParent->m_aJobData.m_pParser, pKey );
183     }
184     else
185     {
186         m_xPaperText->set_sensitive( false );
187         m_xPaperBox->set_sensitive( false );
188     }
189 
190     // input slots
191     if( m_pParent->m_aJobData.m_pParser &&
192         (pKey = m_pParent->m_aJobData.m_pParser->getKey( "InputSlot" )) )
193     {
194         m_pParent->insertAllPPDValues( *m_xSlotBox, m_pParent->m_aJobData.m_pParser, pKey );
195     }
196     else
197     {
198         m_xSlotText->set_sensitive( false );
199         m_xSlotBox->set_sensitive( false );
200     }
201 
202     if ( m_pParent->m_aJobData.meSetupMode == PrinterSetupMode::SingleJob )
203     {
204         m_xCbFromSetup->show();
205 
206         if ( m_pParent->m_aJobData.m_bPapersizeFromSetup )
207             m_xCbFromSetup->set_active(m_pParent->m_aJobData.m_bPapersizeFromSetup);
208         // disable those, unless user wants to use papersize from printer prefs
209         // as they have no influence on what's going to be printed anyway
210         else
211         {
212             m_xPaperText->set_sensitive( false );
213             m_xPaperBox->set_sensitive( false );
214             m_xOrientText->set_sensitive( false );
215             m_xOrientBox->set_sensitive( false );
216         }
217     }
218 }
219 
IMPL_LINK(RTSPaperPage,SelectHdl,weld::ComboBox &,rBox,void)220 IMPL_LINK( RTSPaperPage, SelectHdl, weld::ComboBox&, rBox, void )
221 {
222     const PPDKey* pKey = nullptr;
223     if( &rBox == m_xPaperBox.get() )
224     {
225         if( m_pParent->m_aJobData.m_pParser )
226             pKey = m_pParent->m_aJobData.m_pParser->getKey( "PageSize" );
227     }
228     else if( &rBox == m_xDuplexBox.get() )
229     {
230         if( m_pParent->m_aJobData.m_pParser )
231             pKey = m_pParent->m_aJobData.m_pParser->getKey( "Duplex" );
232     }
233     else if( &rBox == m_xSlotBox.get() )
234     {
235         if( m_pParent->m_aJobData.m_pParser )
236             pKey = m_pParent->m_aJobData.m_pParser->getKey( "InputSlot" );
237     }
238     else if( &rBox == m_xOrientBox.get() )
239     {
240         m_pParent->m_aJobData.m_eOrientation = m_xOrientBox->get_active() == 0 ? orientation::Portrait : orientation::Landscape;
241     }
242     if( pKey )
243     {
244         PPDValue* pValue = reinterpret_cast<PPDValue*>(rBox.get_active_id().toInt64());
245         m_pParent->m_aJobData.m_aContext.setValue( pKey, pValue );
246         update();
247     }
248 
249     m_pParent->SetDataModified( true );
250 }
251 
252 IMPL_LINK( RTSPaperPage, CheckBoxHdl, weld::ToggleButton&, /*cBox*/, void )
253 {
254     bool bFromSetup = m_xCbFromSetup->get_active();
255     m_pParent->m_aJobData.m_bPapersizeFromSetup = bFromSetup;
256     m_xPaperText->set_sensitive(bFromSetup);
257     m_xPaperBox->set_sensitive(bFromSetup);
258     m_xOrientText->set_sensitive(bFromSetup);
259     m_xOrientBox->set_sensitive(bFromSetup);
260     m_pParent->SetDataModified(true);
261 }
262 /*
263  * RTSDevicePage
264  */
265 
RTSDevicePage(weld::Widget * pPage,RTSDialog * pParent)266 RTSDevicePage::RTSDevicePage(weld::Widget* pPage, RTSDialog* pParent)
267     : m_xBuilder(Application::CreateBuilder(pPage, "vcl/ui/printerdevicepage.ui"))
268     , m_pCustomValue(nullptr)
269     , m_pParent(pParent)
270     , m_xContainer(m_xBuilder->weld_widget("PrinterDevicePage"))
271     , m_xPPDKeyBox(m_xBuilder->weld_tree_view("options"))
272     , m_xPPDValueBox(m_xBuilder->weld_tree_view("values"))
273     , m_xCustomEdit(m_xBuilder->weld_entry("custom"))
274     , m_xLevelBox(m_xBuilder->weld_combo_box("level"))
275     , m_xSpaceBox(m_xBuilder->weld_combo_box("colorspace"))
276     , m_xDepthBox(m_xBuilder->weld_combo_box("colordepth"))
277 {
278     m_aReselectCustomIdle.SetInvokeHandler(LINK(this, RTSDevicePage, ImplHandleReselectHdl));
279     m_aReselectCustomIdle.SetDebugName("RTSDevicePage m_aReselectCustomIdle");
280 
281     m_xPPDKeyBox->set_size_request(m_xPPDKeyBox->get_approximate_digit_width() * 32,
282                                    m_xPPDKeyBox->get_height_rows(12));
283 
284     m_xCustomEdit->connect_changed(LINK(this, RTSDevicePage, ModifyHdl));
285 
286     m_xPPDKeyBox->connect_changed( LINK( this, RTSDevicePage, SelectHdl ) );
287     m_xPPDValueBox->connect_changed( LINK( this, RTSDevicePage, SelectHdl ) );
288 
289     switch( m_pParent->m_aJobData.m_nColorDevice )
290     {
291         case 0:
292             m_xSpaceBox->set_active(0);
293             break;
294         case 1:
295             m_xSpaceBox->set_active(1);
296             break;
297         case -1:
298             m_xSpaceBox->set_active(2);
299             break;
300     }
301 
302     sal_Int32 nLevelEntryData = 0; //automatic
303     if( m_pParent->m_aJobData.m_nPDFDevice == 2 ) //explicit PDF
304         nLevelEntryData = 10;
305     else if (m_pParent->m_aJobData.m_nPSLevel > 0) //explicit PS Level
306         nLevelEntryData = m_pParent->m_aJobData.m_nPSLevel+1;
307     else if (m_pParent->m_aJobData.m_nPDFDevice == 1) //automatically PDF
308         nLevelEntryData = 0;
309     else if (m_pParent->m_aJobData.m_nPDFDevice == -1) //explicitly PS from driver
310         nLevelEntryData = 1;
311 
312     bool bAutoIsPDF = officecfg::Office::Common::Print::Option::Printer::PDFAsStandardPrintJobFormat::get();
313 
314     assert(nLevelEntryData != 0
315             || "Generic Printer" == m_pParent->m_aJobData.m_aPrinterName
316             || int(bAutoIsPDF) == m_pParent->m_aJobData.m_nPDFDevice);
317 
318     OUString sStr = m_xLevelBox->get_text(0);
319     OUString sId = m_xLevelBox->get_id(0);
320     m_xLevelBox->insert(0, sStr.replaceAll("%s", bAutoIsPDF ? m_xLevelBox->get_text(5) : m_xLevelBox->get_text(1)), &sId, nullptr, nullptr);
321     m_xLevelBox->remove(1);
322 
323     for (int i = 0; i < m_xLevelBox->get_count(); ++i)
324     {
325         if (m_xLevelBox->get_id(i).toInt32() == nLevelEntryData)
326         {
327             m_xLevelBox->set_active(i);
328             break;
329         }
330     }
331 
332     if (m_pParent->m_aJobData.m_nColorDepth == 8)
333         m_xDepthBox->set_active(0);
334     else if (m_pParent->m_aJobData.m_nColorDepth == 24)
335         m_xDepthBox->set_active(1);
336 
337     // fill ppd boxes
338     if( m_pParent->m_aJobData.m_pParser )
339     {
340         for( int i = 0; i < m_pParent->m_aJobData.m_pParser->getKeys(); i++ )
341         {
342             const PPDKey* pKey = m_pParent->m_aJobData.m_pParser->getKey( i );
343 
344             // skip options already shown somewhere else
345             // also skip options from the "InstallableOptions" PPD group
346             // Options in that group define hardware features that are not
347             // job-specific and should better be handled in the system-wide
348             // printer configuration. Keyword is defined in PPD specification
349             // (version 4.3), section 5.4.
350             if( pKey->isUIKey()                   &&
351                 pKey->getKey() != "PageSize"      &&
352                 pKey->getKey() != "InputSlot"     &&
353                 pKey->getKey() != "PageRegion"    &&
354                 pKey->getKey() != "Duplex"        &&
355                 pKey->getGroup() != "InstallableOptions")
356             {
357                 OUString aEntry( m_pParent->m_aJobData.m_pParser->translateKey( pKey->getKey() ) );
358                 m_xPPDKeyBox->append(OUString::number(reinterpret_cast<sal_Int64>(pKey)), aEntry);
359             }
360         }
361     }
362 }
363 
~RTSDevicePage()364 RTSDevicePage::~RTSDevicePage()
365 {
366 }
367 
getDepth() const368 sal_uLong RTSDevicePage::getDepth() const
369 {
370     sal_uInt16 nSelectPos = m_xDepthBox->get_active();
371     if (nSelectPos == 0)
372         return 8;
373     else
374         return 24;
375 }
376 
getColorDevice() const377 sal_uLong RTSDevicePage::getColorDevice() const
378 {
379     sal_uInt16 nSelectPos = m_xSpaceBox->get_active();
380     switch (nSelectPos)
381     {
382         case 0:
383             return 0;
384         case 1:
385             return 1;
386         case 2:
387             return -1;
388     }
389     return 0;
390 }
391 
getLevel() const392 sal_uLong RTSDevicePage::getLevel() const
393 {
394     auto nLevel = m_xLevelBox->get_active_id().toInt32();
395     if (nLevel == 0)
396         return 0;   //automatic
397     return nLevel < 10 ? nLevel-1 : 0;
398 }
399 
getPDFDevice() const400 sal_uLong RTSDevicePage::getPDFDevice() const
401 {
402     auto nLevel = m_xLevelBox->get_active_id().toInt32();
403     if (nLevel > 9)
404         return 2;   //explicitly PDF
405     else if (nLevel == 0)
406         return 0;   //automatic
407     return -1;      //explicitly PS
408 }
409 
IMPL_LINK(RTSDevicePage,ModifyHdl,weld::Entry &,rEdit,void)410 IMPL_LINK(RTSDevicePage, ModifyHdl, weld::Entry&, rEdit, void)
411 {
412     if (m_pCustomValue)
413     {
414         // tdf#123734 Custom PPD option values are a CUPS extension to PPDs and the user-set value
415         // needs to be prefixed with "Custom." in order to be processed properly
416         m_pCustomValue->m_aCustomOption = "Custom." + rEdit.get_text();
417     }
418 }
419 
IMPL_LINK(RTSDevicePage,SelectHdl,weld::TreeView &,rBox,void)420 IMPL_LINK( RTSDevicePage, SelectHdl, weld::TreeView&, rBox, void )
421 {
422     if (&rBox == m_xPPDKeyBox.get())
423     {
424         const PPDKey* pKey = reinterpret_cast<PPDKey*>(m_xPPDKeyBox->get_selected_id().toInt64());
425         FillValueBox( pKey );
426     }
427     else if (&rBox == m_xPPDValueBox.get())
428     {
429         const PPDKey* pKey = reinterpret_cast<PPDKey*>(m_xPPDKeyBox->get_selected_id().toInt64());
430         const PPDValue* pValue = reinterpret_cast<PPDValue*>(m_xPPDValueBox->get_selected_id().toInt64());
431         if (pKey && pValue)
432         {
433             m_pParent->m_aJobData.m_aContext.setValue( pKey, pValue );
434             ValueBoxChanged(pKey);
435         }
436     }
437     m_pParent->SetDataModified( true );
438 }
439 
FillValueBox(const PPDKey * pKey)440 void RTSDevicePage::FillValueBox( const PPDKey* pKey )
441 {
442     m_xPPDValueBox->clear();
443     m_xCustomEdit->hide();
444 
445     if( ! pKey )
446         return;
447 
448     const PPDValue* pValue = nullptr;
449     for( int i = 0; i < pKey->countValues(); i++ )
450     {
451         pValue = pKey->getValue( i );
452         if( m_pParent->m_aJobData.m_aContext.checkConstraints( pKey, pValue ) &&
453             m_pParent->m_aJobData.m_pParser )
454         {
455             OUString aEntry;
456             if (pValue->m_bCustomOption)
457                 aEntry = VclResId(SV_PRINT_CUSTOM_TXT);
458             else
459                 aEntry = m_pParent->m_aJobData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption);
460             m_xPPDValueBox->append(OUString::number(reinterpret_cast<sal_Int64>(pValue)), aEntry);
461         }
462     }
463     pValue = m_pParent->m_aJobData.m_aContext.getValue( pKey );
464     m_xPPDValueBox->select_id(OUString::number(reinterpret_cast<sal_Int64>(pValue)));
465 
466     ValueBoxChanged(pKey);
467 }
468 
IMPL_LINK_NOARG(RTSDevicePage,ImplHandleReselectHdl,Timer *,void)469 IMPL_LINK_NOARG(RTSDevicePage, ImplHandleReselectHdl, Timer*, void)
470 {
471     //in case selected entry is now not visible select it again to scroll it into view
472     m_xPPDValueBox->select(m_xPPDValueBox->get_selected_index());
473 }
474 
ValueBoxChanged(const PPDKey * pKey)475 void RTSDevicePage::ValueBoxChanged( const PPDKey* pKey )
476 {
477     const PPDValue* pValue = m_pParent->m_aJobData.m_aContext.getValue(pKey);
478     if (pValue->m_bCustomOption)
479     {
480         m_pCustomValue = pValue;
481         m_pParent->m_aJobData.m_aContext.setValue(pKey, pValue);
482         // don't show the "Custom." prefix in the UI, s.a. comment in ModifyHdl
483         m_xCustomEdit->set_text(m_pCustomValue->m_aCustomOption.replaceFirst("Custom.", ""));
484         m_xCustomEdit->show();
485         m_aReselectCustomIdle.Start();
486     }
487     else
488         m_xCustomEdit->hide();
489 }
490 
SetupPrinterDriver(weld::Window * pParent,::psp::PrinterInfo & rJobData)491 int SetupPrinterDriver(weld::Window* pParent, ::psp::PrinterInfo& rJobData)
492 {
493     int nRet = 0;
494     RTSDialog aDialog(rJobData, pParent);
495 
496     // return 0 if cancel was pressed or if the data
497     // weren't modified, 1 otherwise
498     if (aDialog.run() != RET_CANCEL)
499     {
500         rJobData = aDialog.getSetup();
501         nRet = aDialog.GetDataModified() ? 1 : 0;
502     }
503 
504     return nRet;
505 }
506 
507 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
508