File indexing completed on 2025-03-09 05:06:33
0001 /* 0002 SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com> 0003 0004 The save PPD snippet is from CUPS 0005 SPDX-FileCopyrightText: 2007-2009 Apple Inc. 0006 SPDX-FileCopyrightText: 1997-2007 Easy Software Products. 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 This is converted from LGPL 2 in accordance with section 3 0010 See https://www.cups.org/doc/license.html 0011 */ 0012 0013 #include "PrinterOptions.h" 0014 0015 #include "ui_PrinterOptions.h" 0016 0017 #include "Debug.h" 0018 0019 #include <KCupsRequest.h> 0020 #include <NoSelectionRectDelegate.h> 0021 0022 #include <QButtonGroup> 0023 #include <QComboBox> 0024 #include <QFormLayout> 0025 #include <QGroupBox> 0026 #include <QListView> 0027 #include <QPointer> 0028 #include <QRadioButton> 0029 #include <QStandardItemModel> 0030 #include <QTextCodec> 0031 0032 #include <ctype.h> 0033 0034 #define DEFAULT_CHOICE "defaultChoice" 0035 0036 PrinterOptions::PrinterOptions(const QString &destName, bool isClass, bool isRemote, QWidget *parent) 0037 : PrinterPage(parent) 0038 , ui(new Ui::PrinterOptions) 0039 , m_destName(destName) 0040 , m_isClass(isClass) 0041 , m_isRemote(isRemote) 0042 { 0043 ui->setupUi(this); 0044 connect(ui->autoConfigurePB, &QPushButton::clicked, this, &PrinterOptions::autoConfigureClicked); 0045 0046 reloadPPD(); 0047 } 0048 0049 void PrinterOptions::autoConfigureClicked() 0050 { 0051 QPointer<KCupsRequest> request = new KCupsRequest; 0052 request->printCommand(m_destName, QLatin1String("AutoConfigure"), i18n("Set Default Options")); 0053 request->waitTillFinished(); 0054 if (request) { 0055 request->deleteLater(); 0056 } 0057 } 0058 0059 void PrinterOptions::reloadPPD() 0060 { 0061 // The caller "owns" the file that is created and must unlink the returned filename. 0062 if (!m_filename.isEmpty()) { 0063 unlink(qUtf8Printable(m_filename)); 0064 } 0065 0066 // remove all the options 0067 while (ui->verticalLayout->count()) { 0068 qCDebug(PM_CONFIGURE_PRINTER) << "removing" << ui->verticalLayout->count(); 0069 QLayoutItem *item = ui->verticalLayout->itemAt(0); 0070 ui->verticalLayout->removeItem(item); 0071 if (item->widget()) { 0072 item->widget()->deleteLater(); 0073 delete item; 0074 } else if (item->layout()) { 0075 qCDebug(PM_CONFIGURE_PRINTER) << "removing layout" << ui->verticalLayout->count(); 0076 0077 // item->layout()->deleteLater(); 0078 } else if (item->spacerItem()) { 0079 delete item->spacerItem(); 0080 } 0081 } 0082 m_changes = 0; 0083 m_customValues.clear(); 0084 Q_EMIT changed(false); 0085 0086 QPointer<KCupsRequest> request = new KCupsRequest; 0087 request->getPrinterPPD(m_destName); 0088 request->waitTillFinished(); 0089 if (!request) { 0090 return; 0091 } 0092 m_filename = request->printerPPD(); 0093 m_ppd = ppdOpenFile(qUtf8Printable(m_filename)); 0094 request->deleteLater(); 0095 if (m_ppd == nullptr) { 0096 qCWarning(PM_CONFIGURE_PRINTER) << "Could not open ppd file:" << m_filename << request->errorMsg(); 0097 m_filename.clear(); 0098 return; 0099 } 0100 0101 ppdLocalize(m_ppd); 0102 // select the default options on the ppd file 0103 ppdMarkDefaults(m_ppd); 0104 0105 // TODO try to use QTextCodec aliases 0106 const char *lang_encoding; 0107 lang_encoding = m_ppd->lang_encoding; 0108 if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin1")) { 0109 m_codec = QTextCodec::codecForName("ISO-8859-1"); 0110 } else if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin2")) { 0111 m_codec = QTextCodec::codecForName("ISO-8859-2"); 0112 } else if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin5")) { 0113 m_codec = QTextCodec::codecForName("ISO-8859-5"); 0114 } else if (lang_encoding && !strcasecmp(lang_encoding, "JIS83-RKSJ")) { 0115 m_codec = QTextCodec::codecForName("SHIFT-JIS"); 0116 } else if (lang_encoding && !strcasecmp(lang_encoding, "MacStandard")) { 0117 m_codec = QTextCodec::codecForName("MACINTOSH"); 0118 } else if (lang_encoding && !strcasecmp(lang_encoding, "WindowsANSI")) { 0119 m_codec = QTextCodec::codecForName("WINDOWS-1252"); 0120 } else { 0121 // Guess 0122 m_codec = QTextCodec::codecForName(lang_encoding); 0123 } 0124 if (m_codec == nullptr) { 0125 m_codec = QTextCodec::codecForName("UTF-8"); 0126 } 0127 0128 if (m_ppd->manufacturer) { 0129 m_make = m_codec->toUnicode(m_ppd->manufacturer); 0130 } 0131 0132 if (m_ppd->nickname) { 0133 m_makeAndModel = m_codec->toUnicode(m_ppd->nickname); 0134 } 0135 0136 ui->autoConfigurePB->hide(); 0137 ppd_attr_t *ppdattr; 0138 if (m_ppd->num_filters == 0 0139 || ((ppdattr = ppdFindAttr(m_ppd, "cupsCommands", nullptr)) != nullptr && ppdattr->value && strstr(ppdattr->value, "AutoConfigure"))) { 0140 ui->autoConfigurePB->show(); 0141 } else { 0142 for (int i = 0; i < m_ppd->num_filters; i++) { 0143 if (!strncmp(m_ppd->filters[i], "application/vnd.cups-postscript", 31)) { 0144 ui->autoConfigurePB->show(); 0145 break; 0146 } 0147 } 0148 } 0149 0150 createGroups(); 0151 } 0152 0153 void PrinterOptions::createGroups() 0154 { 0155 int i; 0156 ppd_group_t *group; 0157 // Iterate over the groups 0158 for (i = 0, group = m_ppd->groups; i < m_ppd->num_groups; i++, group++) { 0159 // The name of the group 0160 QString name = m_codec->toUnicode(group->name); 0161 0162 // The human name of the group 0163 QString text = m_codec->toUnicode(group->text); 0164 0165 // The group box were the options will be laid out 0166 auto groupBox = new QGroupBox(text, ui->scrollArea); 0167 0168 // Create the form layout to put options in 0169 auto gFormLayout = new QFormLayout(groupBox); 0170 gFormLayout->setFormAlignment(Qt::AlignCenter); 0171 groupBox->setLayout(gFormLayout); 0172 ui->verticalLayout->addWidget(groupBox); 0173 0174 int j; 0175 ppd_option_t *option; 0176 // Iterate over the options in the group 0177 for (j = 0, option = group->options; j < group->num_options; j++, option++) { 0178 QString oKeyword = m_codec->toUnicode(option->keyword); 0179 QString oText = m_codec->toUnicode(option->text); 0180 QString oDefChoice = m_codec->toUnicode(option->defchoice); 0181 // The python system-config-printer skips this one 0182 // which has the same data as "PageSize", let's hope 0183 // they did the right thing 0184 if (oKeyword == QLatin1String("PageRegion")) { 0185 continue; 0186 } 0187 0188 QWidget *optionW = nullptr; 0189 switch (option->ui) { 0190 case PPD_UI_BOOLEAN: 0191 optionW = pickBoolean(option, oKeyword, ui->scrollAreaWidgetContents); 0192 break; 0193 case PPD_UI_PICKMANY: 0194 optionW = pickMany(option, oKeyword, ui->scrollAreaWidgetContents); 0195 break; 0196 case PPD_UI_PICKONE: 0197 optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents); 0198 break; 0199 default: 0200 qCWarning(PM_CONFIGURE_PRINTER) << "Option type not recognized: " << option->ui; 0201 // let's use the most common 0202 optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents); 0203 break; 0204 } 0205 0206 if (optionW) { 0207 // insert the option widget 0208 gFormLayout->addRow(oText, optionW); 0209 } 0210 } 0211 } 0212 ui->verticalLayout->addStretch(); 0213 } 0214 0215 QWidget *PrinterOptions::pickBoolean(ppd_option_t *option, const QString &keyword, QWidget *parent) const 0216 { 0217 Q_UNUSED(keyword) 0218 auto widget = new QWidget(parent); 0219 auto layout = new QHBoxLayout(widget); 0220 auto radioGroup = new QButtonGroup(widget); 0221 widget->setLayout(layout); 0222 0223 int i; 0224 ppd_choice_t *choice; 0225 QString defChoice = m_codec->toUnicode(option->defchoice); 0226 // Iterate over the choices in the option 0227 for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { 0228 const QString choiceName = m_codec->toUnicode(choice->choice); 0229 const QString cText = m_codec->toUnicode(choice->text); 0230 0231 auto button = new QRadioButton(cText, widget); 0232 button->setChecked(defChoice == choiceName); 0233 button->setProperty("choice", choiceName); 0234 // if we are in looking at a remote printer we can't save it 0235 button->setEnabled(!m_isRemote); 0236 layout->addWidget(button); 0237 radioGroup->addButton(button); 0238 } 0239 0240 // store the default choice 0241 radioGroup->setProperty(DEFAULT_CHOICE, defChoice); 0242 radioGroup->setProperty("Keyword", keyword); 0243 connect(radioGroup, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked), this, &PrinterOptions::radioBtClicked); 0244 return widget; 0245 } 0246 0247 void PrinterOptions::radioBtClicked(QAbstractButton *button) 0248 { 0249 QObject *radioGroup = sender(); 0250 bool isDifferent = radioGroup->property(DEFAULT_CHOICE).toString() != button->property("choice"); 0251 0252 if (isDifferent != radioGroup->property("different").toBool()) { 0253 // it's different from the last time so add or remove changes 0254 isDifferent ? m_changes++ : m_changes--; 0255 0256 radioGroup->setProperty("different", isDifferent); 0257 Q_EMIT changed(m_changes); 0258 } 0259 0260 QString keyword = radioGroup->property("Keyword").toString(); 0261 QString choice = button->property("choice").toString(); 0262 radioGroup->setProperty("currentChoice", choice); 0263 0264 // TODO warning about conflicts 0265 // ppdMarkOption(m_ppd, 0266 // m_codec->fromUnicode(keyword), 0267 // m_codec->fromUnicode(choice)); 0268 // store the new value 0269 if (isDifferent) { 0270 m_customValues[keyword] = radioGroup; 0271 } else { 0272 m_customValues.remove(keyword); 0273 } 0274 } 0275 0276 QWidget *PrinterOptions::pickMany(ppd_option_t *option, const QString &keyword, QWidget *parent) const 0277 { 0278 Q_UNUSED(keyword) 0279 auto listView = new QListView(parent); 0280 auto model = new QStandardItemModel(listView); 0281 listView->setModel(model); 0282 listView->setItemDelegate(new NoSelectionRectDelegate(listView)); 0283 0284 int i; 0285 ppd_choice_t *choice; 0286 const QString oDefChoice = m_codec->toUnicode(option->defchoice); 0287 // Iterate over the choices in the option 0288 for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { 0289 const QString cName = m_codec->toUnicode(choice->choice); 0290 const QString cText = m_codec->toUnicode(choice->text); 0291 0292 auto item = new QStandardItem(cText); 0293 item->setData(cName); 0294 item->setCheckable(true); 0295 item->setEditable(false); 0296 // TODO there is only ONE default choice, what about the other 0297 // Items selected?! 0298 item->setCheckState(oDefChoice == cName ? Qt::Checked : Qt::Unchecked); 0299 model->appendRow(item); 0300 } 0301 // if we are in looking at a remote printer we can't save it 0302 listView->setEnabled(!m_isRemote); 0303 return qobject_cast<QWidget *>(listView); 0304 } 0305 0306 QWidget *PrinterOptions::pickOne(ppd_option_t *option, const QString &keyword, QWidget *parent) const 0307 { 0308 int i; 0309 ppd_choice_t *choice; 0310 const QString defChoice = m_codec->toUnicode(option->defchoice); 0311 auto comboBox = new QComboBox(parent); 0312 // Iterate over the choices in the option 0313 for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { 0314 const QString cName = m_codec->toUnicode(choice->choice); 0315 const QString cText = m_codec->toUnicode(choice->text); 0316 0317 comboBox->addItem(cText, cName); 0318 } 0319 // store the default choice 0320 comboBox->setProperty(DEFAULT_CHOICE, defChoice); 0321 comboBox->setProperty("Keyword", keyword); 0322 comboBox->setCurrentIndex(comboBox->findData(defChoice)); 0323 // connect the signal AFTER setCurrentIndex is called 0324 connect(comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &PrinterOptions::currentIndexChangedCB); 0325 // if we are in looking at a remote printer we can't save it 0326 comboBox->setEnabled(!m_isRemote); 0327 return qobject_cast<QWidget *>(comboBox); 0328 } 0329 0330 void PrinterOptions::currentIndexChangedCB(int index) 0331 { 0332 auto comboBox = qobject_cast<QComboBox *>(sender()); 0333 bool isDifferent = comboBox->property(DEFAULT_CHOICE).toString() != comboBox->itemData(index); 0334 0335 if (isDifferent != comboBox->property("different").toBool()) { 0336 // it's different from the last time so add or remove changes 0337 isDifferent ? m_changes++ : m_changes--; 0338 0339 comboBox->setProperty("different", isDifferent); 0340 Q_EMIT changed(m_changes); 0341 } 0342 0343 QString keyword = comboBox->property("Keyword").toString(); 0344 QString value = comboBox->itemData(index).toString(); 0345 comboBox->setProperty("currentChoice", value); 0346 0347 // TODO warning about conflicts 0348 // ppdMarkOption(m_ppd, 0349 // m_codec->fromUnicode(keyword), 0350 // m_codec->fromUnicode(value)); 0351 // store the new value 0352 if (isDifferent) { 0353 m_customValues[keyword] = qobject_cast<QObject *>(comboBox); 0354 } else { 0355 m_customValues.remove(keyword); 0356 } 0357 } 0358 0359 PrinterOptions::~PrinterOptions() 0360 { 0361 if (m_ppd != nullptr) { 0362 ppdClose(m_ppd); 0363 } 0364 0365 if (!m_filename.isEmpty()) { 0366 unlink(qUtf8Printable(m_filename)); 0367 } 0368 0369 delete ui; 0370 } 0371 0372 const char * /* O - Value of variable */ 0373 PrinterOptions::getVariable(const char *name) const /* I - Name of variable */ 0374 { 0375 const QString keyword = m_codec->toUnicode(name); 0376 auto it = m_customValues.constFind(keyword); 0377 if (it != m_customValues.constEnd()) { 0378 const QString value = it.value()->property("currentChoice").toString(); 0379 return m_codec->fromUnicode(value).constData(); 0380 } else { 0381 return nullptr; 0382 } 0383 } 0384 0385 /* 0386 * 'get_points()' - Get a value in points. 0387 */ 0388 double /* O - Number in points */ 0389 PrinterOptions::get_points(double number, /* I - Original number */ 0390 const char *uval) /* I - Units */ 0391 { 0392 if (!strcmp(uval, "mm")) /* Millimeters */ 0393 return (number * 72.0 / 25.4); 0394 else if (!strcmp(uval, "cm")) /* Centimeters */ 0395 return (number * 72.0 / 2.54); 0396 else if (!strcmp(uval, "in")) /* Inches */ 0397 return (number * 72.0); 0398 else if (!strcmp(uval, "ft")) /* Feet */ 0399 return (number * 72.0 * 12.0); 0400 else if (!strcmp(uval, "m")) /* Meters */ 0401 return (number * 72.0 / 0.0254); 0402 else /* Points */ 0403 return (number); 0404 } 0405 0406 /* 0407 * 'get_option_value()' - Return the value of an option. 0408 * 0409 * This function also handles generation of custom option values. 0410 */ 0411 0412 char * /* O - Value string or nullptr on error */ 0413 PrinterOptions::get_option_value(ppd_file_t *ppd, /* I - PPD file */ 0414 const char *name, /* I - Option name */ 0415 char *buffer, /* I - String buffer */ 0416 size_t bufsize) const /* I - Size of buffer */ 0417 { 0418 char *bufptr, /* Pointer into buffer */ 0419 *bufend; /* End of buffer */ 0420 ppd_coption_t *coption; /* Custom option */ 0421 ppd_cparam_t *cparam; /* Current custom parameter */ 0422 char keyword[256]; /* Parameter name */ 0423 const char *val, /* Parameter value */ 0424 *uval; /* Units value */ 0425 long integer; /* Integer value */ 0426 double number, /* Number value */ 0427 number_points; /* Number in points */ 0428 0429 /* 0430 * See if we have a custom option choice... 0431 */ 0432 0433 if ((val = getVariable(name)) == nullptr) { 0434 /* 0435 * Option not found! 0436 */ 0437 0438 return (nullptr); 0439 } else if (strcasecmp(val, "Custom") || (coption = ppdFindCustomOption(ppd, name)) == nullptr) { 0440 /* 0441 * Not a custom choice... 0442 */ 0443 0444 qstrncpy(buffer, val, bufsize); 0445 return (buffer); 0446 } 0447 0448 /* 0449 * OK, we have a custom option choice, format it... 0450 */ 0451 0452 *buffer = '\0'; 0453 0454 if (!strcmp(coption->keyword, "PageSize")) { 0455 const char *lval; /* Length string value */ 0456 double width, /* Width value */ 0457 width_points, /* Width in points */ 0458 length, /* Length value */ 0459 length_points; /* Length in points */ 0460 0461 val = getVariable("PageSize.Width"); 0462 lval = getVariable("PageSize.Height"); 0463 uval = getVariable("PageSize.Units"); 0464 0465 if (!val || !lval || !uval || (width = strtod(val, nullptr)) == 0.0 || (length = strtod(lval, nullptr)) == 0.0 0466 || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) { 0467 return (nullptr); 0468 } 0469 0470 width_points = get_points(width, uval); 0471 length_points = get_points(length, uval); 0472 0473 if (width_points < ppd->custom_min[0] || width_points > ppd->custom_max[0] || length_points < ppd->custom_min[1] 0474 || length_points > ppd->custom_max[1]) { 0475 return (nullptr); 0476 } 0477 0478 snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval); 0479 } else if (cupsArrayCount(coption->params) == 1) { 0480 cparam = ppdFirstCustomParam(coption); 0481 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); 0482 0483 if ((val = getVariable(keyword)) == nullptr) 0484 return (nullptr); 0485 0486 switch (cparam->type) { 0487 case PPD_CUSTOM_CURVE: 0488 case PPD_CUSTOM_INVCURVE: 0489 case PPD_CUSTOM_REAL: 0490 if ((number = strtod(val, nullptr)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real) 0491 return (nullptr); 0492 0493 snprintf(buffer, bufsize, "Custom.%g", number); 0494 break; 0495 case PPD_CUSTOM_INT: 0496 if (!*val || (integer = strtol(val, nullptr, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int 0497 || integer > cparam->maximum.custom_int) 0498 return (nullptr); 0499 0500 snprintf(buffer, bufsize, "Custom.%ld", integer); 0501 break; 0502 case PPD_CUSTOM_POINTS: 0503 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); 0504 0505 if ((number = strtod(val, nullptr)) == 0.0 || (uval = getVariable(keyword)) == nullptr 0506 || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) 0507 return (nullptr); 0508 0509 number_points = get_points(number, uval); 0510 if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points) 0511 return (nullptr); 0512 0513 snprintf(buffer, bufsize, "Custom.%g%s", number, uval); 0514 break; 0515 case PPD_CUSTOM_PASSCODE: 0516 for (uval = val; *uval; ++uval) { 0517 if (!isdigit(*uval & 255)) { 0518 return (nullptr); 0519 } 0520 } 0521 case PPD_CUSTOM_PASSWORD: 0522 case PPD_CUSTOM_STRING: 0523 integer = (long)strlen(val); 0524 if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) { 0525 return (nullptr); 0526 } 0527 0528 snprintf(buffer, bufsize, "Custom.%s", val); 0529 break; 0530 #if (CUPS_VERSION_MAJOR >= 3) || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR >= 3) \ 0531 || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR == 2 && CUPS_VERSION_PATCH >= 12) 0532 case PPD_CUSTOM_UNKNOWN: 0533 #endif 0534 default: 0535 break; 0536 } 0537 } else { 0538 const char *prefix = "{"; /* Prefix string */ 0539 0540 bufptr = buffer; 0541 bufend = buffer + bufsize; 0542 0543 for (cparam = ppdFirstCustomParam(coption); cparam; cparam = ppdNextCustomParam(coption)) { 0544 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); 0545 0546 if ((val = getVariable(keyword)) == nullptr) { 0547 return (nullptr); 0548 } 0549 0550 snprintf(bufptr, bufend - bufptr, "%s%s=", prefix, cparam->name); 0551 bufptr += strlen(bufptr); 0552 prefix = " "; 0553 0554 switch (cparam->type) { 0555 case PPD_CUSTOM_CURVE: 0556 case PPD_CUSTOM_INVCURVE: 0557 case PPD_CUSTOM_REAL: 0558 if ((number = strtod(val, nullptr)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real) 0559 return (nullptr); 0560 0561 snprintf(bufptr, bufend - bufptr, "%g", number); 0562 break; 0563 case PPD_CUSTOM_INT: 0564 if (!*val || (integer = strtol(val, nullptr, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int 0565 || integer > cparam->maximum.custom_int) { 0566 return (nullptr); 0567 } 0568 0569 snprintf(bufptr, bufend - bufptr, "%ld", integer); 0570 break; 0571 case PPD_CUSTOM_POINTS: 0572 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); 0573 0574 if ((number = strtod(val, nullptr)) == 0.0 || (uval = getVariable(keyword)) == nullptr 0575 || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) { 0576 return (nullptr); 0577 } 0578 0579 number_points = get_points(number, uval); 0580 if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points) { 0581 return (nullptr); 0582 } 0583 0584 snprintf(bufptr, bufend - bufptr, "%g%s", number, uval); 0585 break; 0586 0587 case PPD_CUSTOM_PASSCODE: 0588 for (uval = val; *uval; uval++) { 0589 if (!isdigit(*uval & 255)) { 0590 return (nullptr); 0591 } 0592 } 0593 case PPD_CUSTOM_PASSWORD: 0594 case PPD_CUSTOM_STRING: 0595 integer = (long)strlen(val); 0596 if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) { 0597 return (nullptr); 0598 } 0599 0600 if ((bufptr + 2) > bufend) { 0601 return (nullptr); 0602 } 0603 0604 bufend--; 0605 *bufptr++ = '\"'; 0606 0607 while (*val && bufptr < bufend) { 0608 if (*val == '\\' || *val == '\"') { 0609 if ((bufptr + 1) >= bufend) { 0610 return (nullptr); 0611 } 0612 0613 *bufptr++ = '\\'; 0614 } 0615 0616 *bufptr++ = *val++; 0617 } 0618 0619 if (bufptr >= bufend) { 0620 return (nullptr); 0621 } 0622 0623 *bufptr++ = '\"'; 0624 *bufptr = '\0'; 0625 bufend++; 0626 break; 0627 #if (CUPS_VERSION_MAJOR >= 3) || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR >= 3) \ 0628 || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR == 2 && CUPS_VERSION_PATCH >= 12) 0629 case PPD_CUSTOM_UNKNOWN: 0630 #endif 0631 default: 0632 break; 0633 } 0634 0635 bufptr += strlen(bufptr); 0636 } 0637 0638 if (bufptr == buffer || (bufend - bufptr) < 2) { 0639 return (nullptr); 0640 } 0641 0642 strcpy(bufptr, "}"); 0643 } 0644 0645 return (buffer); 0646 } 0647 0648 void PrinterOptions::save() 0649 { 0650 char tempfile[1024]; 0651 const char *var; 0652 cups_file_t *in, /* Input file */ 0653 *out; /* Output file */ 0654 char line[1024], /* Line from PPD file */ 0655 value[1024], /* Option value */ 0656 keyword[1024], /* Keyword from Default line */ 0657 *keyptr; /* Pointer into keyword... */ 0658 0659 // copy cups-1.4.2/cgi-bin line 3779 0660 if (!m_filename.isEmpty()) { 0661 out = cupsTempFile2(tempfile, sizeof(tempfile)); 0662 in = cupsFileOpen(qUtf8Printable(m_filename), "r"); 0663 0664 if (!in || !out) { 0665 if (in) { 0666 cupsFileClose(in); 0667 } 0668 0669 if (out) { 0670 cupsFileClose(out); 0671 unlink(tempfile); 0672 } 0673 0674 // TODO add a KMessageBox::error 0675 0676 return; 0677 } 0678 0679 while (cupsFileGets(in, line, sizeof(line))) { 0680 if (!strncmp(line, "*cupsProtocol:", 14)) { 0681 continue; 0682 } else if (strncmp(line, "*Default", 8)) { 0683 cupsFilePrintf(out, "%s\n", line); 0684 } else { 0685 /* 0686 * Get default option name... 0687 */ 0688 qstrncpy(keyword, line + 8, sizeof(keyword)); 0689 0690 for (keyptr = keyword; *keyptr; keyptr++) { 0691 if (*keyptr == ':' || isspace(*keyptr & 255)) { 0692 break; 0693 } 0694 } 0695 0696 *keyptr = '\0'; 0697 0698 if (!strcmp(keyword, "PageRegion") || !strcmp(keyword, "PaperDimension") || !strcmp(keyword, "ImageableArea")) { 0699 var = get_option_value(m_ppd, "PageSize", value, sizeof(value)); 0700 } else { 0701 var = get_option_value(m_ppd, keyword, value, sizeof(value)); 0702 } 0703 0704 if (!var) { 0705 cupsFilePrintf(out, "%s\n", line); 0706 } else { 0707 cupsFilePrintf(out, "*Default%s: %s\n", keyword, var); 0708 } 0709 } 0710 } 0711 0712 cupsFileClose(in); 0713 cupsFileClose(out); 0714 } else { 0715 // TODO add a KMessageBox::error 0716 qCWarning(PM_CONFIGURE_PRINTER) << "No printer PPD file set, can't save options."; 0717 0718 return; 0719 } 0720 0721 QVariantMap values; // we need null values 0722 QPointer<KCupsRequest> request = new KCupsRequest; 0723 if (m_isClass) { 0724 request->addOrModifyClass(m_destName, values); 0725 } else { 0726 request->addOrModifyPrinter(m_destName, values, QString::fromUtf8(tempfile)); 0727 } 0728 0729 // Disable the widget till the request is processed 0730 // Otherwise the user might change something in the ui 0731 // which won't be saved but the apply but when the request 0732 // finishes we will set the current options as default 0733 setEnabled(false); 0734 request->waitTillFinished(); 0735 0736 // unlink the file 0737 unlink(tempfile); 0738 0739 if (request) { 0740 setEnabled(true); 0741 if (!request->hasError()) { 0742 // if we succefully save the new ppd we need now to 0743 // clear our changes 0744 auto i = m_customValues.constBegin(); 0745 while (i != m_customValues.constEnd()) { 0746 QObject *obj = i.value(); 0747 const QString currentChoice = obj->property("currentChoice").toString(); 0748 // Store the current choice as the default one 0749 obj->setProperty(DEFAULT_CHOICE, currentChoice); 0750 obj->setProperty("currentChoice", QVariant()); 0751 obj->setProperty("different", false); 0752 ++i; 0753 } 0754 m_changes = 0; 0755 m_customValues.clear(); 0756 Q_EMIT changed(false); 0757 } 0758 request->deleteLater(); 0759 } 0760 } 0761 0762 bool PrinterOptions::hasChanges() 0763 { 0764 return m_changes; 0765 } 0766 0767 QString PrinterOptions::currentMake() const 0768 { 0769 return m_make; 0770 } 0771 0772 QString PrinterOptions::currentMakeAndModel() const 0773 { 0774 return m_makeAndModel; 0775 } 0776 0777 #include "moc_PrinterOptions.cpp"