Warning, /plasma/print-manager/src/kcm/ui/FindPrinter.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 org.kde.plasma.components as PComp 0010 import org.kde.kirigami as Kirigami 0011 import org.kde.plasma.printmanager as PM 0012 import org.kde.kitemmodels as KSFM 0013 import org.kde.plasma.extras as PlasmaExtras 0014 0015 Kirigami.Dialog { 0016 id: root 0017 0018 property bool loading: false 0019 property bool showingManual: false 0020 property bool hasDetectedDevices: false 0021 0022 // MFG:HP;MDL:ENVY 4520 series;CLS:PRINTER;DES:ENVY 4520 series;SN:TH6BN4M1390660; 0023 function parseDeviceId(devId: string, key: string) { 0024 if (devId === undefined) { 0025 return "" 0026 } 0027 0028 // if no key, return the array 0029 const arr = devId.split(";") 0030 if (key === undefined) { 0031 return arr 0032 } 0033 0034 // otherwise, return key[value] 0035 for (let i=0, len=arr.length; i<len; ++i) { 0036 const a = arr[i].split(":") 0037 if (a[0] === key) 0038 return a[1] 0039 } 0040 0041 return "" 0042 } 0043 0044 // Remove the ppd settings to force manual make/model driver selection 0045 function manualDriverSelect() { 0046 settings.remove("ppd-name") 0047 settings.add("ppd-type", PM.PPDType.Custom) 0048 root.setValues(settings.pending) 0049 close() 0050 } 0051 0052 // Find the first direct device and network device 0053 // note, we're looping the decscendent filter model 0054 function setDeviceSelection() { 0055 let directNdx = -1 0056 let netNdx = -1 0057 for (let i=0, len=deviceItems.rowCount(); i<len; ++i) { 0058 const ndx = deviceItems.mapToSource(deviceItems.index(i,0)) 0059 const cls = deviceItems.sourceModel.data(ndx, PM.DevicesModel.DeviceClass) 0060 const devId = deviceItems.sourceModel.data(ndx, PM.DevicesModel.DeviceId) 0061 if (cls.toString() === "direct") { 0062 directNdx = i 0063 } else if (cls.toString() === "network" && devId.length > 0) { 0064 netNdx = i 0065 } 0066 if (netNdx >= 0 && directNdx >= 0) 0067 break 0068 } 0069 0070 // Did we actually find device, either direct or network? 0071 if (directNdx === -1 && netNdx === -1) { 0072 compLoader.sourceComponent = noDevicesComp 0073 hasDetectedDevices = false 0074 showingManual = true 0075 deviceItems.invalidateFilter() 0076 } else { 0077 hasDetectedDevices = true 0078 // by default, select direct connect printer 0079 deviceList.currentIndex = directNdx !== -1 ? directNdx : netNdx 0080 deviceList.itemAtIndex(deviceList.currentIndex).clicked() 0081 } 0082 0083 } 0084 0085 signal setValues(var values) 0086 0087 title: i18nc("@title:window", "Set up a Printer Connection") 0088 0089 standardButtons: Kirigami.Dialog.NoButton 0090 0091 customFooterActions: [ 0092 Kirigami.Action { 0093 text: showingManual 0094 ? i18nc("@action:button", "Show Detected Devices") 0095 : i18nc("@action:button", "Show Manual Options") 0096 icon.name: showingManual 0097 ? "standard-connector-symbolic" 0098 : "internet-services" 0099 visible: hasDetectedDevices 0100 onTriggered: { 0101 showingManual = !showingManual 0102 deviceItems.invalidateFilter() 0103 deviceList.currentIndex = -1 0104 compLoader.sourceComponent = undefined 0105 0106 if (!showingManual) { 0107 setDeviceSelection() 0108 } else { 0109 compLoader.sourceComponent = chooseManualComp 0110 } 0111 } 0112 }, 0113 Kirigami.Action { 0114 text: i18n("Refresh") 0115 enabled: !loading 0116 icon.name: "view-refresh-symbolic" 0117 onTriggered: { 0118 showingManual = false 0119 devices.load() 0120 } 0121 } 0122 ] 0123 0124 footerLeadingComponent: Kirigami.UrlButton { 0125 text: i18n("CUPS Network Printers Help") 0126 url: "http://localhost:631/help/network.html" 0127 padding: Kirigami.Units.largeSpacing 0128 } 0129 0130 onClosed: destroy(10) 0131 0132 ConfigValues { 0133 id: settings 0134 } 0135 0136 // Filter the descendants to exclude "null" deviceClass 0137 KSFM.KSortFilterProxyModel { 0138 id: deviceItems 0139 sortRole: PM.DevicesModel.DeviceCategory 0140 0141 // Descendants are the actual printer devices 0142 sourceModel: KSFM.KDescendantsProxyModel { 0143 sourceModel: devices 0144 } 0145 0146 filterRowCallback: (source_row, source_parent) => { 0147 const ndx = sourceModel.index(source_row, 0, source_parent) 0148 if (sourceModel.data(ndx, PM.DevicesModel.DeviceClass) === undefined) { 0149 return false 0150 } 0151 const cat = sourceModel.data(ndx, PM.DevicesModel.DeviceCategory) 0152 if (showingManual) { 0153 return cat === "Manual" 0154 } else { 0155 return cat !== "Manual" 0156 } 0157 0158 } 0159 } 0160 0161 // Two-level QSIM, top level is "device category" (Qt.UserRole) 0162 PM.DevicesModel { 0163 id: devices 0164 0165 function load() { 0166 loading = true 0167 kcm.clearRemotePrinters() 0168 kcm.clearRecommendedDrivers() 0169 update() 0170 } 0171 0172 Component.onCompleted: load() 0173 0174 onLoaded: { 0175 loading = false 0176 setDeviceSelection() 0177 } 0178 } 0179 0180 Component { 0181 id: uriComp 0182 0183 BaseDevice { 0184 id: uriItem 0185 0186 title: compLoader.info 0187 subtitle: i18nc("@title:group", "Find remote printing devices") 0188 helpText: i18nc("@info:usagetip", "Enter the address of the remote host/device") 0189 icon.source: "internet-services" 0190 0191 actions: [ 0192 Kirigami.Action { 0193 text: i18nc("@action:button", "Select Printer") 0194 enabled: list.currentIndex !== -1 0195 icon.name: "dialog-ok-symbolic" 0196 onTriggered: { 0197 settings.set(kcm.remotePrinters[list.currentIndex]) 0198 manualDriverSelect() 0199 } 0200 } 0201 ] 0202 0203 Component.onCompleted: { 0204 connSearch.text = compLoader.selector !== "other" 0205 ? compLoader.selector + "://" 0206 : "ipp://" 0207 connSearch.forceActiveFocus() 0208 } 0209 0210 Connections { 0211 target: kcm 0212 0213 function onRemotePrintersLoaded() { 0214 if (list.count > 0) { 0215 list.itemAtIndex(0).clicked() 0216 } 0217 } 0218 } 0219 0220 RowLayout { 0221 Layout.alignment: Qt.AlignHCenter 0222 QQC2.Label { 0223 text: i18nc("@label:textbox", "Uri:") 0224 } 0225 0226 Kirigami.SearchField { 0227 id: connSearch 0228 focus: true 0229 delaySearch: true 0230 autoAccept: false 0231 placeholderText: i18n("Enter host URI") 0232 Layout.fillWidth: true 0233 0234 KeyNavigation.left: uriItem.parent 0235 KeyNavigation.right: list 0236 0237 KeyNavigation.down: list 0238 0239 KeyNavigation.backtab: KeyNavigation.left 0240 KeyNavigation.tab: KeyNavigation.right 0241 0242 onAccepted: { 0243 if (text.length > 0) 0244 kcm.getRemotePrinters(text, compLoader.selector) 0245 else 0246 kcm.clearRemotePrinters() 0247 } 0248 } 0249 } 0250 0251 // remote printer list 0252 QQC2.ScrollView { 0253 Layout.alignment: Qt.AlignHCenter 0254 Layout.fillWidth: true 0255 Layout.fillHeight: true 0256 0257 contentItem: ListView { 0258 id: list 0259 currentIndex: -1 0260 highlight: PlasmaExtras.Highlight {} 0261 highlightMoveDuration: 0 0262 highlightResizeDuration: 0 0263 0264 activeFocusOnTab: true 0265 keyNavigationWraps: true 0266 0267 KeyNavigation.up: connSearch 0268 KeyNavigation.backtab: uriItem.parent 0269 Keys.onUpPressed: event => { 0270 if (currentIndex === 0) { 0271 currentIndex = -1 0272 } 0273 event.accepted = false 0274 } 0275 0276 model: kcm.remotePrinters 0277 0278 delegate: Kirigami.SubtitleDelegate { 0279 width: ListView.view.width 0280 text: modelData["printer-info"] 0281 subtitle: modelData["printer-name"] 0282 icon.name: modelData.remote 0283 ? "folder-network-symbolic" 0284 : modelData["printer-is-class"] ? "folder" : modelData.iconName 0285 0286 onClicked: { 0287 ListView.view.currentIndex = index 0288 } 0289 } 0290 } 0291 } 0292 0293 AddressExamples { 0294 Layout.alignment: Qt.AlignHCenter 0295 Layout.fillWidth: true 0296 Layout.fillHeight: true 0297 0298 onSelected: address => connSearch.text = address 0299 } 0300 0301 } 0302 } 0303 0304 Component { 0305 id: networkComp 0306 0307 BaseDevice { 0308 title: settings.value("printer-make-and-model") 0309 subtitle: settings.value("device-desc") 0310 0311 Component.onCompleted: { 0312 drivers.load(settings.value("device-id") 0313 , settings.value("printer-make-and-model") 0314 , settings.value("device-uri")) 0315 } 0316 0317 // Recommended Driver list 0318 Drivers { 0319 id: drivers 0320 0321 onSelected: driverMap => { 0322 settings.set(driverMap) 0323 root.setValues(settings.pending) 0324 close() 0325 } 0326 } 0327 } 0328 } 0329 0330 Component { 0331 id: directComp 0332 0333 BaseDevice { 0334 title: settings.value("printer-make-and-model") 0335 subtitle: settings.value("device-desc") 0336 helpText: i18nc("@info:usagetip", "Choose a device connection") 0337 0338 // Connection list 0339 QQC2.ScrollView { 0340 Layout.alignment: Qt.AlignHCenter 0341 Layout.fillWidth: true 0342 Layout.fillHeight: true 0343 0344 contentItem: ListView { 0345 id: directlist 0346 highlight: PlasmaExtras.Highlight {} 0347 highlightMoveDuration: 0 0348 highlightResizeDuration: 0 0349 0350 activeFocusOnTab: true 0351 keyNavigationWraps: true 0352 0353 KeyNavigation.backtab: root.parent 0354 Keys.onUpPressed: event => { 0355 if (currentIndex === 0) { 0356 currentIndex = -1; 0357 } 0358 event.accepted = false; 0359 } 0360 0361 model: settings.value("device-uris") 0362 0363 delegate: PComp.ItemDelegate { 0364 width: ListView.view.width 0365 text: devices.uriDevice(modelData) 0366 icon.name: "standard-connector-symbolic" 0367 0368 Component.onCompleted: { 0369 if (index === 0) 0370 onClicked() 0371 } 0372 0373 onClicked: { 0374 ListView.view.currentIndex = index 0375 settings.add("device-uri", modelData) 0376 drivers.load(settings.value("device-id") 0377 , settings.value("printer-make-and-model") 0378 , modelData) 0379 } 0380 } 0381 } 0382 } 0383 0384 // Recommended Driver list 0385 Drivers { 0386 id: drivers 0387 0388 onSelected: driverMap => { 0389 settings.set(driverMap) 0390 root.setValues(settings.pending) 0391 close() 0392 } 0393 } 0394 } 0395 } 0396 0397 Component { 0398 id: lpdComp 0399 NotAvailable {} 0400 } 0401 0402 Component { 0403 id: socketComp 0404 NotAvailable {} 0405 } 0406 0407 Component { 0408 id: serialComp 0409 NotAvailable {} 0410 } 0411 0412 Component { 0413 id: smbComp 0414 NotAvailable {} 0415 } 0416 0417 Component { 0418 id: naComp 0419 NotAvailable {} 0420 } 0421 0422 Component { 0423 id: noDevicesComp 0424 0425 Kirigami.PlaceholderMessage { 0426 text: i18nc("@info:status", "Unable to automatically discover any printing devices") 0427 explanation: i18nc("@info:usagetip", "Choose \"Refresh\" to try again or choose a manual configuration option from the list") 0428 Layout.maximumWidth: parent.width - Kirigami.Units.largeSpacing * 4 0429 } 0430 } 0431 0432 Component { 0433 id: chooseManualComp 0434 0435 Kirigami.PlaceholderMessage { 0436 text: i18nc("@info:usagetip", "Choose a manual configuration option from the list") 0437 Layout.maximumWidth: parent.width - Kirigami.Units.largeSpacing * 4 0438 } 0439 } 0440 0441 component NotAvailable: ColumnLayout { 0442 // This is inside a Loader that is fillW/fillH: true 0443 // pad with "spacers" top/bottom to force centering 0444 Item { Layout.fillHeight: true } 0445 0446 Kirigami.PlaceholderMessage { 0447 icon.name: "package-available-locked" 0448 text: compLoader.info 0449 explanation: i18nc("@info:status", "This feature is not yet available (%1)", compLoader.selector) 0450 Layout.maximumWidth: parent.width - Kirigami.Units.largeSpacing * 4 0451 } 0452 0453 Item { Layout.fillHeight: true } 0454 } 0455 0456 contentItem: RowLayout { 0457 spacing: 0 0458 0459 QQC2.ScrollView { 0460 Layout.fillHeight: true 0461 Layout.preferredWidth: Kirigami.Units.gridUnit*13 0462 clip: true 0463 0464 contentItem: ListView { 0465 id: deviceList 0466 0467 PComp.BusyIndicator { 0468 id: busyInd 0469 running: loading 0470 anchors.centerIn: parent 0471 implicitWidth: Math.floor(parent.width/2) 0472 implicitHeight: implicitWidth 0473 } 0474 0475 clip: true 0476 currentIndex: -1 0477 highlight: PlasmaExtras.Highlight {} 0478 highlightMoveDuration: 0 0479 highlightResizeDuration: 0 0480 0481 activeFocusOnTab: true 0482 keyNavigationWraps: true 0483 0484 model: deviceItems 0485 0486 section { 0487 property: "deviceCategory" 0488 delegate: Kirigami.ListSectionHeader { 0489 width: ListView.view.width 0490 required property string section 0491 label: section 0492 } 0493 } 0494 0495 delegate: Kirigami.SubtitleDelegate { 0496 width: ListView.view.width 0497 visible: deviceClass !== undefined 0498 0499 text: deviceInfo.replace("Internet Printing Protocol", "IPP") 0500 subtitle: deviceMakeModel.replace("Unknown", "") 0501 0502 icon.name: deviceId.length === 0 0503 ? "internet-services" 0504 : "printer-symbolic" 0505 0506 onClicked: { 0507 ListView.view.currentIndex = index 0508 compLoader.selector = "" 0509 compLoader.info = "" 0510 settings.clear() 0511 0512 if (deviceUri && deviceUri.length > 0) { 0513 compLoader.info = deviceInfo 0514 if (deviceId && deviceId.length > 0) { 0515 if (deviceClass === "file") { 0516 compLoader.selector = deviceUri.replace(/:\//g,'') 0517 } else { 0518 // a printer device 0519 settings.set({"device-id": deviceId 0520 , "device-uri": deviceUri 0521 , "device-uris": deviceUris 0522 , "device-class": deviceClass 0523 , "device-desc": deviceDescription 0524 , "printer-info": deviceInfo 0525 , "printer-make": parseDeviceId(deviceId, "MFG") 0526 , "printer-model": parseDeviceId(deviceId, "MDL") 0527 , "printer-make-and-model": deviceMakeModel 0528 , "printer-location": deviceLocation 0529 , "ppd-type": PM.PPDType.Custom 0530 }) 0531 compLoader.selector = deviceClass 0532 } 0533 0534 } else { 0535 // a category item 0536 compLoader.selector = deviceUri 0537 } 0538 } 0539 } 0540 } 0541 } 0542 } 0543 0544 Kirigami.Separator { 0545 Layout.fillHeight: true 0546 width: 1 0547 } 0548 0549 Loader { 0550 id: compLoader 0551 active: !loading 0552 0553 Layout.fillWidth: true 0554 Layout.fillHeight: true 0555 Layout.margins: Kirigami.Units.largeSpacing 0556 0557 property string selector: "" 0558 property string info: "" 0559 0560 onSelectorChanged: { 0561 switch (selector) { 0562 case "other": 0563 case "ipp": 0564 case "ipps": 0565 case "http": 0566 case "https": 0567 case "scsi": 0568 case "cups-brf": 0569 sourceComponent = uriComp 0570 break 0571 case "network": 0572 sourceComponent = networkComp 0573 break 0574 case "direct": 0575 sourceComponent = directComp 0576 break 0577 case "lpd": 0578 sourceComponent = lpdComp 0579 break 0580 case "socket": 0581 sourceComponent = socketComp 0582 break 0583 case "smb": 0584 sourceComponent = sambaComp 0585 break 0586 case "serial": 0587 sourceComponent = serialComp 0588 break 0589 default: 0590 sourceComponent = naComp 0591 } 0592 } 0593 } 0594 0595 } 0596 0597 }