Warning, /plasma/print-manager/src/kcm/ui/MakeModel.qml is written in an unsupported language. File is not indexed.

0001 /**
0002  SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
0003  SPDX-License-Identifier: GPL-2.0-or-later
0004  */
0005 
0006 import QtQuick
0007 import QtQuick.Layouts
0008 import QtQuick.Controls as QQC2
0009 import QtQuick.Dialogs as Dialogs
0010 import org.kde.kirigami as Kirigami
0011 import org.kde.kitemmodels as KSFM
0012 import org.kde.plasma.components as PComp
0013 import org.kde.plasma.extras as PlasmaExtras
0014 import org.kde.plasma.printmanager as PM
0015 
0016 /*
0017 * PPD driver selection
0018 */
0019 Kirigami.Dialog {
0020     id: root
0021 
0022     property PM.PPDModel model
0023     property bool loading: true
0024     // ppd attributes for the make/model
0025     property var ppdData: ({})
0026 
0027     // make is the first level in the hierarchy of the model
0028     function getMakeIndex(make) {
0029         print("GETMAKE:", make)
0030         for (let i=0, len=model.rowCount(); i<len; ++i) {
0031             const val = model.data(model.index(i,0), Qt.DisplayRole).toString()
0032             if (val === make)
0033                 return i
0034         }
0035         return -1
0036     }
0037 
0038     // make/model is the second level
0039     // try to select based on CUPS pcfile *and* makemodel desc
0040     // preference to pcfile
0041     function getMakeModelIndex() {
0042         const file = ppdData.pcfile?.toLowerCase()
0043         const mm = ppdData.makeModel?.toLowerCase()
0044         print("GETMAKEMODEL pcfile:", file, ", mm:", mm)
0045         for (let i=0, len=printerModels.rowCount(); i<len; ++i) {
0046             const ndx = printerModels.mapToSource(printerModels.index(i,0))
0047             const f = printerModels.sourceModel.data(ndx, PM.PPDModel.PPDName).toString()
0048             const m = printerModels.sourceModel.data(ndx, PM.PPDModel.PPDMakeAndModel).toString()
0049             if ((file && f && f.toLowerCase().includes(file)) | (mm && m && m.toLowerCase().includes(mm))) {
0050                 return i
0051             }
0052         }
0053         return -1
0054     }
0055 
0056     function setCurrentMakeModel(index = 0) {
0057         if (index === -1) {
0058             return
0059         }
0060         const ndx = printerModels.mapToSource(printerModels.index(index,0))
0061         ppdData.makeModel = printerModels.sourceModel.data(ndx, PM.PPDModel.PPDMakeAndModel).toString()
0062         ppdData.file = printerModels.sourceModel.data(ndx, PM.PPDModel.PPDName).toString()
0063     }
0064 
0065     function init() {
0066         if (ppdData.type === PM.PPDType.Manual) {
0067             rbFile.checked = true
0068             customFilename.text = ppdData.file
0069         } else {
0070             rbMake.checked = true
0071 
0072             // Make/model list selections
0073             // Default to first in the list if there is no make
0074             if (ppdData.make === undefined) {
0075                 ppdData.make = model.data(model.index(0,0), Qt.DisplayRole).toString()
0076                 makesList.currentIndex = 0
0077             } else {
0078                 makesList.currentIndex = getMakeIndex(ppdData.make)
0079             }
0080             makesList.positionViewAtIndex(makesList.currentIndex, ListView.Center)
0081             printerModels.invalidateFilter()
0082             // Select the printer model
0083             makeModelList.currentIndex = getMakeModelIndex()
0084             makeModelList.positionViewAtIndex(makeModelList.currentIndex, ListView.Center)
0085             setCurrentMakeModel(makeModelList.currentIndex)
0086         }
0087 
0088         if (ppdData.makeModel === undefined) {
0089             title = i18nc("@title:window", "Select a printer make/model")
0090         } else {
0091             title = i18nc("@title:window", "Printer Driver (%1)", ppdData.makeModel)
0092         }
0093 
0094     }
0095 
0096     signal saveValues(var data)
0097 
0098     title: i18nc("@title:window", "Printer Driver")
0099 
0100     onLoadingChanged: {
0101         print("PPD Database Count:", model.rowCount())
0102         if (!loading) {
0103             // empty ppdData means we're selecting new make/model
0104             if (Object.keys(ppdData).length === 0) {
0105                 rbMake.checked = true
0106                 ppdData.type = PM.PPDType.Custom
0107                 ppdData.pcfile = ""
0108                 makesList.currentIndex = 0
0109                 makesList.positionViewAtBeginning()
0110                 ppdData.make = model.data(model.index(0,0), Qt.DisplayRole).toString()
0111                 if (ppdData.make.length > 0) {
0112                     printerModels.invalidateFilter()
0113                     setCurrentMakeModel()
0114                 }
0115             } else {
0116                 init()
0117             }
0118         }
0119     }
0120 
0121     // Try to load PPDModel if empty
0122     Component.onCompleted: {
0123         if (model.rowCount() === 0) {
0124             model.load()
0125         } else {
0126             loading = false
0127         }
0128     }
0129 
0130     Connections {
0131         target: model
0132         enabled: loading
0133 
0134         function onLoaded() {
0135             loading = false
0136         }
0137     }
0138 
0139     onClosed: destroy(10)
0140 
0141     standardButtons: Kirigami.Dialog.NoButton
0142 
0143     customFooterActions: [
0144         Kirigami.Action {
0145             text: i18n("Save")
0146             enabled: !loading
0147             icon.name: "dialog-ok-symbolic"
0148             onTriggered: {
0149                 if (rbFile.checked && customFilename.text.length === 0) {
0150                     error.text = i18n("Select a PostScript Printer Description (PPD) file")
0151                     error.visible = true
0152                     fileButton.focus = true
0153                     return
0154                 }
0155 
0156                 if (rbMake.checked && makeModelList.currentIndex === -1) {
0157                     error.text = i18n("Printer model is required.  Please select a printer model.")
0158                     error.visible = true
0159                     makeModelList.focus = true
0160                     return
0161                 }
0162 
0163                 if (rbFile.checked) {
0164                     ppdData.file = customFilename.text
0165                 }
0166                 saveValues(ppdData)
0167                 root.close()
0168             }
0169         }
0170     ]
0171 
0172     // Printer models are filtered by selected make and
0173     // optionally, by a second user filter
0174     KSFM.KSortFilterProxyModel {
0175         id: printerModels
0176         // This holds printer model lists, which are descendents
0177         // of the make items
0178         sourceModel: KSFM.KDescendantsProxyModel {
0179             sourceModel: root.model
0180         }
0181 
0182         filterRowCallback: (source_row, source_parent) => {
0183             const make = sourceModel.data(sourceModel.index(source_row, 0, source_parent)
0184                                           , PM.PPDModel.PPDMake)
0185             if (filterString.length === 0) {
0186                 return make === ppdData.make
0187             }
0188 
0189             return make === ppdData.make
0190                 && sourceModel.data(sourceModel.index(source_row, 0, source_parent)
0191                                     , PM.PPDModel.PPDMakeAndModel).toLowerCase().includes(filterString)
0192         }
0193     }
0194 
0195     contentItem: ColumnLayout {
0196         spacing: Kirigami.Units.largeSpacing
0197         enabled: !loading
0198 
0199         BannerWithTimer {
0200             id: error
0201             Layout.fillWidth: true
0202         }
0203 
0204         QQC2.ButtonGroup {
0205             id: buttonGroup
0206         }
0207 
0208         RowLayout {
0209             QQC2.RadioButton {
0210                 id: rbMake
0211                 text: i18nc("@option:radio", "Select Make and Model:")
0212                 Layout.fillWidth: true
0213                 onToggled: {
0214                     if (checked) {
0215                         ppdData.type = PM.PPDType.Custom
0216                     }
0217                 }
0218                 QQC2.ButtonGroup.group: buttonGroup
0219             }
0220 
0221             Kirigami.SearchField {
0222                 enabled: rbMake.checked && makesList.count > 0
0223                 placeholderText: i18nc("@info:placeholder", "Filter Models")
0224                 onAccepted: {
0225                     printerModels.filterString = text.toLowerCase()
0226                     makeModelList.currentIndex = -1
0227                 }
0228             }
0229         }
0230 
0231         RowLayout {
0232             enabled: rbMake.checked
0233 
0234             QQC2.ScrollView {
0235                 Layout.fillHeight: true
0236                 Layout.preferredWidth: Math.floor(root.width/3)
0237                 clip: true
0238 
0239                 contentItem: ListView {
0240                     id: makesList
0241                     clip: true
0242                     highlight: PlasmaExtras.Highlight {}
0243                     highlightMoveDuration: 0
0244                     highlightResizeDuration: 0
0245 
0246                     model: root.model
0247 
0248                     delegate: PComp.ItemDelegate {
0249                         width: ListView.view.width
0250                         text: model?.display
0251                         icon.name: "system-user-prompt"
0252 
0253                         onClicked: {
0254                             ListView.view.currentIndex = index
0255                             ppdData.make = model.display
0256                             printerModels.invalidateFilter()
0257                             makeModelList.currentIndex = 0
0258                             makeModelList.positionViewAtBeginning()
0259                             setCurrentMakeModel()
0260                         }
0261                     }
0262                 }
0263             }
0264 
0265             QQC2.ScrollView {
0266                 Layout.fillHeight: true
0267                 Layout.fillWidth: true
0268                 clip: true
0269 
0270                 contentItem: ListView {
0271                     id: makeModelList
0272                     clip: true
0273                     highlight: PlasmaExtras.Highlight {}
0274                     highlightMoveDuration: 0
0275                     highlightResizeDuration: 0
0276 
0277                     PComp.BusyIndicator {
0278                         running: loading
0279                         visible: loading
0280                         anchors.left: parent.left
0281                         anchors.verticalCenter: parent.verticalCenter
0282                         implicitWidth: Kirigami.Units.gridUnit * 6
0283                         implicitHeight: Kirigami.Units.gridUnit * 6
0284                     }
0285 
0286                     model: printerModels
0287 
0288                     delegate: PComp.ItemDelegate {
0289                         width: ListView.view.width
0290                         text: model?.ppdMakeModel
0291                         icon.name: "printer-symbolic"
0292 
0293                         onClicked: {
0294                             ListView.view.currentIndex = index
0295                             ppdData.file = ppdName
0296                             ppdData.makeModel = ppdMakeModel
0297                         }
0298                     }
0299                 }
0300             }
0301 
0302         }
0303 
0304         QQC2.RadioButton {
0305             id: rbFile
0306             text: i18nc("@option:radio", "PPD file:")
0307             Layout.topMargin: Kirigami.Units.largeSpacing*3
0308             onToggled: {
0309                 if (checked) {
0310                     ppdData.type = PM.PPDType.Manual
0311                     fileButton.focus = true
0312                 }
0313             }
0314             QQC2.ButtonGroup.group: buttonGroup
0315         }
0316 
0317         RowLayout {
0318             enabled: rbFile.checked
0319 
0320             QQC2.TextField {
0321                 id: customFilename
0322                 readOnly: true
0323                 Layout.fillWidth: true
0324             }
0325 
0326             QQC2.Button {
0327                 id: fileButton
0328                 text: i18nc("@action:button", "Select PPD Fileā€¦")
0329                 icon.name: "folder-print-symbolic"
0330                 onClicked: fileDlg.open()
0331             }
0332 
0333             Dialogs.FileDialog {
0334                 id: fileDlg
0335                 acceptLabel: i18n("Select")
0336                 nameFilters: [i18n("PostScript Printer Description Files (*.ppd)")]
0337 
0338                 onAccepted: customFilename.text = currentFile
0339             }
0340         }
0341     }
0342 
0343  }