├── .pylintrc ├── udev ├── .gitignore ├── configure-printer@.service.in └── 70-printers.rules ├── .gitattributes ├── AUTHORS ├── HIG.py ├── system-config-printer-applet.in ├── dbus ├── scp-dbus-service.in ├── org.fedoraproject.Config.Printing.service.in ├── com.redhat.NewPrinterNotification.conf └── com.redhat.PrinterDriversInstaller.conf ├── icons └── i-network-printer.png ├── install-printerdriver.in ├── system-config-printer.in ├── data ├── screenshot-mainwindow.png └── screenshot-properties.png ├── po ├── quot.sed ├── boldquot.sed ├── ChangeLog ├── remove-potcdate.sin ├── insert-header.sin ├── en@quot.header ├── en@boldquot.header ├── Makevars ├── Rules-quot └── POTFILES.in ├── system-config-printer.desktop.in ├── print-applet.desktop.in ├── .travis.yml ├── profile-ppds.py ├── zanata.xml.in ├── README.md ├── bootstrap ├── ui ├── remove-gtkalignment.xsl ├── statusicon_popupmenu.ui ├── WaitWindow.ui ├── AboutDialog.ui ├── NewPrinterName.ui ├── ConnectingDialog.ui ├── JobsWindow.ui ├── SMBBrowseDialog.ui └── InstallDialog.ui ├── .gitignore ├── cupshelpers ├── config.py.in ├── __init__.py └── installdriver.py ├── setup.py ├── config.py.in ├── SearchCriterion.py ├── troubleshoot ├── LocalOrRemote.py ├── ServerFirewalled.py ├── SchedulerNotRunning.py ├── ErrorLogParse.py ├── RemoteAddress.py ├── VerifyPackages.py ├── Welcome.py ├── CheckSELinux.py ├── QueueNotEnabled.py ├── NetworkCUPSPrinterShared.py ├── QueueRejectingJobs.py ├── CheckLocalServerPublishing.py ├── base.py ├── Shrug.py ├── PrinterStateReasons.py ├── Locale.py ├── CheckPrinterSanity.py ├── ChoosePrinter.py ├── CheckUSBPermissions.py └── ChooseNetworkPrinter.py ├── system-config-printer.appdata.xml.in ├── debug.py ├── gui.py ├── killtimer.py ├── installpackage.py ├── xml └── validate.py ├── configure.ac ├── errordialogs.py ├── NEWS ├── smburi.py ├── test_PhysicalDevice.py ├── mkinstalldirs ├── man └── system-config-printer.xml ├── dnssdresolve.py ├── gtkinklevel.py ├── gitlog-to-changelog ├── test └── test-cups-driver.py ├── install-printerdriver.py └── userdefault.py /.pylintrc: -------------------------------------------------------------------------------- 1 | [VARIABLES] 2 | additional-builtins=_ 3 | -------------------------------------------------------------------------------- /udev/.gitignore: -------------------------------------------------------------------------------- 1 | /udev-configure-printer.service 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ChangeLog merge=merge-changelog 2 | po/*.po merge=binary 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Florian Festi 2 | Tim Waugh 3 | -------------------------------------------------------------------------------- /HIG.py: -------------------------------------------------------------------------------- 1 | PAD_SMALL = 3 2 | PAD_NORMAL = 6 3 | PAD_BIG = 12 4 | PAD_LARGE = 18 5 | -------------------------------------------------------------------------------- /system-config-printer-applet.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/applet.py "$@" 4 | -------------------------------------------------------------------------------- /dbus/scp-dbus-service.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/scp-dbus-service.py "$@" 4 | -------------------------------------------------------------------------------- /icons/i-network-printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdohnal/system-config-printer/HEAD/icons/i-network-printer.png -------------------------------------------------------------------------------- /install-printerdriver.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/install-printerdriver.py "$@" 4 | -------------------------------------------------------------------------------- /system-config-printer.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | prefix=@prefix@ 3 | exec @datarootdir@/@PACKAGE@/system-config-printer.py "$@" 4 | -------------------------------------------------------------------------------- /data/screenshot-mainwindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdohnal/system-config-printer/HEAD/data/screenshot-mainwindow.png -------------------------------------------------------------------------------- /data/screenshot-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdohnal/system-config-printer/HEAD/data/screenshot-properties.png -------------------------------------------------------------------------------- /dbus/org.fedoraproject.Config.Printing.service.in: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.fedoraproject.Config.Printing 3 | Exec=@bindir@/scp-dbus-service 4 | -------------------------------------------------------------------------------- /po/quot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | -------------------------------------------------------------------------------- /udev/configure-printer@.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Configure Plugged-In Printer 3 | Requires=cups.socket 4 | After=cups.socket 5 | 6 | [Service] 7 | ExecStart=@udevdir@/udev-configure-printer add "%i" 8 | -------------------------------------------------------------------------------- /po/boldquot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | s/“/“/g 8 | s/”/”/g 9 | s/‘/‘/g 10 | s/’/’/g 11 | -------------------------------------------------------------------------------- /system-config-printer.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | _Name=Print Settings 3 | _GenericName=Print Settings 4 | _X-GNOME-FullName=Print Settings 5 | _Comment=Configure printers 6 | Exec=system-config-printer 7 | Terminal=false 8 | Type=Application 9 | Icon=printer 10 | StartupNotify=true 11 | -------------------------------------------------------------------------------- /print-applet.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | _Name=Print Queue Applet 3 | _Comment=System tray icon for managing print jobs 4 | Exec=system-config-printer-applet 5 | Terminal=false 6 | Type=Application 7 | Icon=printer 8 | NotShowIn=KDE;GNOME;Cinnamon; 9 | StartupNotify=false 10 | X-GNOME-Autostart-Delay=30 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: python 4 | python: 5 | - "3.5" 6 | before_install: 7 | - "sudo apt-get update -qq" 8 | install: 9 | - "sudo apt-get install -y intltool xmlto desktop-file-utils libcups2-dev" 10 | - "pip install pytest" 11 | script: ./bootstrap && ./configure && make && make check && make distcheck 12 | notifications: 13 | email: false 14 | -------------------------------------------------------------------------------- /profile-ppds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import cups 3 | import cupshelpers 4 | import hotshot 5 | import hotshot.stats 6 | 7 | ppds = cupshelpers.ppds.PPDs (cups.Connection ().getPPDs ()) 8 | prof = hotshot.Profile ("a.prof") 9 | prof.runcall (lambda: ppds.getPPDNameFromDeviceID('','','')) 10 | prof.close () 11 | stats = hotshot.stats.load ("a.prof") 12 | stats.sort_stats ('time') 13 | stats.print_stats (100) 14 | -------------------------------------------------------------------------------- /zanata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://fedora.zanata.org/ 4 | @PACKAGE@ 5 | master 6 | gettext 7 | po 8 | po 9 | 10 | @LOCALES@ 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # system-config-printer 2 | 3 | [![Code Health](https://landscape.io/github/twaugh/system-config-printer/master/landscape.svg?style=flat)](https://landscape.io/github/twaugh/system-config-printer/master) 4 | [![Build Status](https://travis-ci.org/twaugh/system-config-printer.svg?branch=master)](https://travis-ci.org/twaugh/system-config-printer) 5 | 6 | This is a graphical tool for CUPS administration. It uses IPP to 7 | configure a CUPS server. 8 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | intltoolize --force --copy 3 | aclocal 4 | automake --foreign --copy --add-missing 5 | autoconf 6 | 7 | # If this is a git repository, and git-merge-changelog is available, 8 | # use it. 9 | if [ -d .git ] && git --version 2>/dev/null >/dev/null && \ 10 | git-merge-changelog 2>/dev/null >/dev/null; then 11 | git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' 12 | git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' 13 | fi 14 | -------------------------------------------------------------------------------- /po/ChangeLog: -------------------------------------------------------------------------------- 1 | 2006-06-19 gettextize 2 | 3 | * Makefile.in.in: New file, from gettext-0.14.5. 4 | * boldquot.sed: New file, from gettext-0.14.5. 5 | * en@boldquot.header: New file, from gettext-0.14.5. 6 | * en@quot.header: New file, from gettext-0.14.5. 7 | * insert-header.sin: New file, from gettext-0.14.5. 8 | * quot.sed: New file, from gettext-0.14.5. 9 | * remove-potcdate.sin: New file, from gettext-0.14.5. 10 | * Rules-quot: New file, from gettext-0.14.5. 11 | * POTFILES.in: New file. 12 | 13 | -------------------------------------------------------------------------------- /po/remove-potcdate.sin: -------------------------------------------------------------------------------- 1 | # Sed script that remove the POT-Creation-Date line in the header entry 2 | # from a POT file. 3 | # 4 | # The distinction between the first and the following occurrences of the 5 | # pattern is achieved by looking at the hold space. 6 | /^"POT-Creation-Date: .*"$/{ 7 | x 8 | # Test if the hold space is empty. 9 | s/P/P/ 10 | ta 11 | # Yes it was empty. First occurrence. Remove the line. 12 | g 13 | d 14 | bb 15 | :a 16 | # The hold space was nonempty. Following occurrences. Do nothing. 17 | x 18 | :b 19 | } 20 | -------------------------------------------------------------------------------- /ui/remove-gtkalignment.xsl: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /po/insert-header.sin: -------------------------------------------------------------------------------- 1 | # Sed script that inserts the file called HEADER before the header entry. 2 | # 3 | # At each occurrence of a line starting with "msgid ", we execute the following 4 | # commands. At the first occurrence, insert the file. At the following 5 | # occurrences, do nothing. The distinction between the first and the following 6 | # occurrences is achieved by looking at the hold space. 7 | /^msgid /{ 8 | x 9 | # Test if the hold space is empty. 10 | s/m/m/ 11 | ta 12 | # Yes it was empty. First occurrence. Read the file. 13 | r HEADER 14 | # Output the file's contents by reading the next line. But don't lose the 15 | # current line while doing this. 16 | g 17 | N 18 | bb 19 | :a 20 | # The hold space was nonempty. Following occurrences. Do nothing. 21 | x 22 | :b 23 | } 24 | -------------------------------------------------------------------------------- /udev/70-printers.rules: -------------------------------------------------------------------------------- 1 | # UDEV rules for USB devices - ENV variables can be monitored 2 | # via 'sudo udevadm monitor --udev --subsystem-match=usb --property' 3 | # Low-level USB device add trigger 4 | ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="configure-printer@usb-$env{BUSNUM}-$env{DEVNUM}.service" 5 | # Low-level USB device remove trigger 6 | # Interface types: 7 | # 7/1/1 - device with unidirectional USB, managed by usb backend 8 | # 7/1/2 - device with bidirectional USB, managed by usb backend 9 | # 7/1/3 - device with special USB protocol, managed by hp backend 10 | # 7/1/4 - device with IPP over USB protocol, managed by ipp-usb daemon 11 | ACTION=="remove", SUBSYSTEM=="usb", ENV{INTERFACE}=="7/1/*", RUN+="udev-configure-printer remove %p" 12 | -------------------------------------------------------------------------------- /dbus/com.redhat.NewPrinterNotification.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /dbus/com.redhat.PrinterDriversInstaller.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | Makefile 3 | /configure 4 | /config.status 5 | /config.log 6 | /autom4te.cache 7 | /missing 8 | /compile 9 | /depcomp 10 | /INSTALL 11 | /ChangeLog 12 | TAGS 13 | .deps 14 | .dirstamp 15 | 16 | intltool-extract* 17 | intltool-merge* 18 | intltool-update* 19 | po/.intltool-merge-cache 20 | 21 | .stamp-distutils-in-builddir 22 | *.pyc 23 | *.o 24 | *.gladep 25 | 26 | /test-ppd-module.sh 27 | /pickled-ppds 28 | 29 | po/*.mo 30 | po/*.gmo 31 | po/POTFILES 32 | po/Makefile.in 33 | po/Makefile 34 | po/stamp-it 35 | 36 | # These are generated from *.in files 37 | config.py 38 | my-default-printer 39 | system-config-printer 40 | system-config-printer-applet 41 | manage-print-jobs.desktop 42 | my-default-printer.desktop 43 | print-applet.desktop 44 | system-config-printer.desktop 45 | 46 | # These are compiled 47 | udev/udev-configure-printer 48 | 49 | # These are generated from the XML file. 50 | man/*.1 51 | 52 | # Backup files 53 | *.bak 54 | *~ 55 | 56 | *.sw[nop] 57 | 58 | # Tarballs 59 | system-config-printer-*.tar.?z* 60 | -------------------------------------------------------------------------------- /cupshelpers/config.py.in: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2006, 2007, 2010 Red Hat, Inc. 4 | ## Authors: 5 | ## Florian Festi 6 | ## Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | prefix="@prefix@" 23 | sysconfdir="@sysconfdir@" 24 | cupsserverbindir="@cupsserverbindir@" 25 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2008 Red Hat, Inc. 4 | ## Copyright (C) 2008 Tim Waugh 5 | 6 | ## This program is free software; you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation; either version 2 of the License, or 9 | ## (at your option) any later version. 10 | 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program; if not, write to the Free Software 18 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | from distutils.core import setup 21 | setup(name='cupshelpers', 22 | version='1.0', 23 | description='Helper functions and classes for using CUPS', 24 | maintainer='Tim Waugh', 25 | maintainer_email='twaugh@redhat.com', 26 | packages=['cupshelpers']) 27 | -------------------------------------------------------------------------------- /po/en@quot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | -------------------------------------------------------------------------------- /config.py.in: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2006, 2007, 2008, 2014 Red Hat, Inc. 4 | ## Copyright (C) 2006, 2007 Florian Festi 5 | ## Copyright (C) 2006, 2007, 2008 Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | prefix="@prefix@" 22 | datadir="@datadir@" 23 | localedir="@localedir@" 24 | pkgdatadir="@datadir@/@PACKAGE@" 25 | VERSION="@VERSION@" 26 | PACKAGE="@PACKAGE@" 27 | DOWNLOADABLE_ONLYPPD=True 28 | DOWNLOADABLE_ONLYFREE=True 29 | DOWNLOADABLE_PKG_ONLYSIGNED=True 30 | packagesystem = None # discovered at runtime 31 | -------------------------------------------------------------------------------- /po/en@boldquot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | # This catalog furthermore displays the text between the quotation marks in 24 | # bold face, assuming the VT100/XTerm escape sequences. 25 | # 26 | -------------------------------------------------------------------------------- /SearchCriterion.py: -------------------------------------------------------------------------------- 1 | ## Copyright (C) 2008 Rui Matos 2 | 3 | ## This program is free software; you can redistribute it and/or modify 4 | ## it under the terms of the GNU General Public License as published by 5 | ## the Free Software Foundation; either version 2 of the License, or 6 | ## (at your option) any later version. 7 | 8 | ## This program is distributed in the hope that it will be useful, 9 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ## GNU General Public License for more details. 12 | 13 | ## You should have received a copy of the GNU General Public License 14 | ## along with this program; if not, write to the Free Software 15 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | class SearchCriterion: 18 | SUBJECT_NAME = 0 19 | SUBJECT_DESC = 1 20 | SUBJECT_MANUF = 2 21 | SUBJECT_MODEL = 3 22 | SUBJECT_URI = 4 23 | SUBJECT_MEDIA = 5 24 | SUBJECT_STAT = 6 25 | SUBJECT_COUNT = 7 26 | SUBJECT_LOCATION = 8 27 | 28 | RULE_IS = 0 29 | RULE_ISNOT = 1 30 | RULE_CONT = 2 31 | RULE_NOTCONT = 3 32 | RULE_COUNT = 4 33 | 34 | def __init__ (self, 35 | subject = SUBJECT_NAME, 36 | rule = RULE_CONT, 37 | value = ""): 38 | self.subject = subject 39 | self.rule = rule 40 | self.value = value 41 | -------------------------------------------------------------------------------- /troubleshoot/LocalOrRemote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from .base import * 23 | class LocalOrRemote(Multichoice): 24 | def __init__ (self, troubleshooter): 25 | Multichoice.__init__ (self, troubleshooter, "printer_is_remote", 26 | _("Printer Location"), 27 | _("Is the printer connected to this computer " 28 | "or available on the network?"), 29 | [(_("Locally connected printer"), False), 30 | (_("Network printer"), True)], 31 | "Local or remote?") 32 | 33 | def display (self): 34 | return not self.troubleshooter.answers['cups_queue_listed'] 35 | -------------------------------------------------------------------------------- /system-config-printer.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.fedoraproject.systemconfigprinter 4 | CC0 5 | GPL-2.0+ 6 | Print Settings 7 | Configure printer queues 8 | 9 | 10 |

With system-config-printer you can add, edit and delete 11 | printer queues. It allows you to choose the connection method and 12 | the printer driver.

13 | 14 |

For each queue, you can adjust the default page size and other 15 | driver options, as well as seeing ink/toner levels and status 16 | messages.

17 |
18 | 19 | 20 | 21 | Main window 22 | https://raw.githubusercontent.com/OpenPrinting/system-config-printer/master/data/screenshot-mainwindow.png 23 | 24 | 25 | Printer properties 26 | https://raw.githubusercontent.com/OpenPrinting/system-config-printer/master/data/screenshot-properties.png 27 | 28 | 29 | 30 | https://github.com/OpenPrinting/system-config-printer/ 31 | https://github.com/OpenPrinting/system-config-printer/issues 32 | 33 | 34 | system-config-printer 35 | org.fedoraproject.Config.Printing 36 | system-config-printer.desktop 37 | 38 | 39 | zdohnal@redhat.com 40 |
41 | -------------------------------------------------------------------------------- /debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2008, 2010 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import sys 22 | import traceback 23 | 24 | _debug=False 25 | def debugprint (x): 26 | if _debug: 27 | try: 28 | sys.stderr.write (x + "\n") 29 | sys.stderr.flush () 30 | except: 31 | pass 32 | 33 | def get_debugging (): 34 | return _debug 35 | 36 | def set_debugging (d): 37 | global _debug 38 | _debug = d 39 | 40 | def fatalException (exitcode=1): 41 | nonfatalException (type="fatal", end="Exiting") 42 | sys.exit (exitcode) 43 | 44 | def nonfatalException (type="non-fatal", end="Continuing anyway.."): 45 | d = get_debugging () 46 | set_debugging (True) 47 | debugprint ("Caught %s exception. Traceback:" % type) 48 | (type, value, tb) = sys.exc_info () 49 | extxt = traceback.format_exception_only (type, value) 50 | for line in traceback.format_tb(tb): 51 | debugprint (line.strip ()) 52 | debugprint (extxt[0].strip ()) 53 | debugprint (end) 54 | set_debugging (d) 55 | -------------------------------------------------------------------------------- /ui/statusicon_popupmenu.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | True 10 | False 11 | _Hide 12 | True 13 | 14 | 15 | 16 | 17 | 18 | _Configure Printers 19 | True 20 | False 21 | True 22 | 23 | 24 | 25 | 26 | 27 | True 28 | False 29 | 30 | 31 | 32 | 33 | _Quit 34 | True 35 | True 36 | False 37 | True 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /po/Makevars: -------------------------------------------------------------------------------- 1 | # Makefile variables for PO directory in any package using GNU gettext. 2 | 3 | # Usually the message domain is the same as the package name. 4 | DOMAIN = $(PACKAGE) 5 | 6 | # These two variables depend on the location of this directory. 7 | subdir = po 8 | top_builddir = .. 9 | 10 | # These options get passed to xgettext. 11 | XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ 12 | 13 | # This is the copyright holder that gets inserted into the header of the 14 | # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding 15 | # package. (Note that the msgstr strings, extracted from the package's 16 | # sources, belong to the copyright holder of the package.) Translators are 17 | # expected to transfer the copyright for their translations to this person 18 | # or entity, or to disclaim their copyright. The empty string stands for 19 | # the public domain; in this case the translators are expected to disclaim 20 | # their copyright. 21 | COPYRIGHT_HOLDER = Red Hat 22 | 23 | # This is the email address or URL to which the translators shall report 24 | # bugs in the untranslated strings: 25 | # - Strings which are not entire sentences, see the maintainer guidelines 26 | # in the GNU gettext documentation, section 'Preparing Strings'. 27 | # - Strings which use unclear terms or require additional context to be 28 | # understood. 29 | # - Strings which make invalid assumptions about notation of date, time or 30 | # money. 31 | # - Pluralisation problems. 32 | # - Incorrect English spelling. 33 | # - Incorrect formatting. 34 | # It can be your email address, or a mailing list address where translators 35 | # can write to without being subscribed, or the URL of a web page through 36 | # which the translators can contact you. 37 | MSGID_BUGS_ADDRESS = https://bugzilla.redhat.com/bugzilla 38 | 39 | # This is the list of locale categories, beyond LC_MESSAGES, for which the 40 | # message catalogs shall be used. It is usually empty. 41 | EXTRA_LOCALE_CATEGORIES = 42 | -------------------------------------------------------------------------------- /po/Rules-quot: -------------------------------------------------------------------------------- 1 | # Special Makefile rules for English message catalogs with quotation marks. 2 | 3 | DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot 4 | 5 | .SUFFIXES: .insert-header .po-update-en 6 | 7 | en@quot.po-create: 8 | $(MAKE) en@quot.po-update 9 | en@boldquot.po-create: 10 | $(MAKE) en@boldquot.po-update 11 | 12 | en@quot.po-update: en@quot.po-update-en 13 | en@boldquot.po-update: en@boldquot.po-update-en 14 | 15 | .insert-header.po-update-en: 16 | @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ 17 | if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ 18 | tmpdir=`pwd`; \ 19 | echo "$$lang:"; \ 20 | ll=`echo $$lang | sed -e 's/@.*//'`; \ 21 | LC_ALL=C; export LC_ALL; \ 22 | cd $(srcdir); \ 23 | if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ 24 | if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ 25 | rm -f $$tmpdir/$$lang.new.po; \ 26 | else \ 27 | if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ 28 | :; \ 29 | else \ 30 | echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ 31 | exit 1; \ 32 | fi; \ 33 | fi; \ 34 | else \ 35 | echo "creation of $$lang.po failed!" 1>&2; \ 36 | rm -f $$tmpdir/$$lang.new.po; \ 37 | fi 38 | 39 | en@quot.insert-header: insert-header.sin 40 | sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header 41 | 42 | en@boldquot.insert-header: insert-header.sin 43 | sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header 44 | 45 | mostlyclean: mostlyclean-quot 46 | mostlyclean-quot: 47 | rm -f *.insert-header 48 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | # List of source files which contain translatable strings. 2 | [encoding: UTF-8] 3 | 4 | asyncipp.py 5 | authconn.py 6 | errordialogs.py 7 | jobviewer.py 8 | newprinter.py 9 | options.py 10 | optionwidgets.py 11 | ppdippstr.py 12 | ppdsloader.py 13 | printerproperties.py 14 | probe_printer.py 15 | pysmb.py 16 | serversettings.py 17 | system-config-printer.py 18 | [type: gettext/glade] ui/AboutDialog.ui 19 | [type: gettext/glade] ui/ConnectDialog.ui 20 | [type: gettext/glade] ui/ConnectingDialog.ui 21 | [type: gettext/glade] ui/InstallDialog.ui 22 | [type: gettext/glade] ui/JobsWindow.ui 23 | [type: gettext/glade] ui/NewPrinterName.ui 24 | [type: gettext/glade] ui/NewPrinterWindow.ui 25 | [type: gettext/glade] ui/PrinterPropertiesDialog.ui 26 | [type: gettext/glade] ui/PrintersWindow.ui 27 | [type: gettext/glade] ui/ServerSettingsDialog.ui 28 | [type: gettext/glade] ui/SMBBrowseDialog.ui 29 | [type: gettext/glade] ui/statusicon_popupmenu.ui 30 | [type: gettext/glade] ui/WaitWindow.ui 31 | system-config-printer.desktop.in 32 | statereason.py 33 | timedops.py 34 | ToolbarSearchEntry.py 35 | troubleshoot/__init__.py 36 | troubleshoot/base.py 37 | troubleshoot/CheckLocalServerPublishing.py 38 | troubleshoot/CheckNetworkServerSanity.py 39 | troubleshoot/CheckPPDSanity.py 40 | troubleshoot/CheckPrinterSanity.py 41 | troubleshoot/ChooseNetworkPrinter.py 42 | troubleshoot/ChoosePrinter.py 43 | troubleshoot/DeviceListed.py 44 | troubleshoot/ErrorLogCheckpoint.py 45 | troubleshoot/ErrorLogFetch.py 46 | troubleshoot/ErrorLogParse.py 47 | troubleshoot/Locale.py 48 | troubleshoot/LocalOrRemote.py 49 | troubleshoot/NetworkCUPSPrinterShared.py 50 | troubleshoot/PrinterStateReasons.py 51 | troubleshoot/PrintTestPage.py 52 | troubleshoot/QueueNotEnabled.py 53 | troubleshoot/QueueRejectingJobs.py 54 | troubleshoot/RemoteAddress.py 55 | troubleshoot/SchedulerNotRunning.py 56 | troubleshoot/ServerFirewalled.py 57 | troubleshoot/Shrug.py 58 | troubleshoot/Welcome.py 59 | applet.py 60 | print-applet.desktop.in 61 | system-config-printer.appdata.xml.in 62 | -------------------------------------------------------------------------------- /cupshelpers/__init__.py: -------------------------------------------------------------------------------- 1 | ## system-config-printer 2 | 3 | ## Copyright (C) 2008, 2011 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | __all__ = ['set_debugprint_fn', 22 | 'Device', 'Printer', 'activateNewPrinter', 23 | 'copyPPDOptions', 'getDevices', 'getPrinters', 24 | 'missingPackagesAndExecutables', 'missingExecutables', 25 | 'parseDeviceID', 26 | 'setPPDPageSize', 27 | 'ppds', 28 | 'openprinting'] 29 | 30 | def _no_debug (x): 31 | return 32 | 33 | _debugprint_fn = _no_debug 34 | def _debugprint (x): 35 | _debugprint_fn (x) 36 | 37 | def set_debugprint_fn (debugprint): 38 | """ 39 | Set debugging hook. 40 | 41 | @param debugprint: function to print debug output 42 | @type debugprint: fn (str) -> None 43 | """ 44 | global _debugprint_fn 45 | _debugprint_fn = debugprint 46 | 47 | from .cupshelpers import \ 48 | Device, \ 49 | Printer, \ 50 | activateNewPrinter, \ 51 | copyPPDOptions, \ 52 | getDevices, \ 53 | getPrinters, \ 54 | missingPackagesAndExecutables, \ 55 | missingExecutables, \ 56 | parseDeviceID, \ 57 | setPPDPageSize 58 | 59 | from . import ppds 60 | from . import openprinting 61 | -------------------------------------------------------------------------------- /troubleshoot/ServerFirewalled.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class ServerFirewalled(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Server firewalled") 28 | page = self.initial_vbox (_("Check Server Firewall"), 29 | _("It is not possible to connect to the " 30 | "server.")) 31 | self.label = Gtk.Label () 32 | self.label.set_alignment (0, 0) 33 | self.label.set_line_wrap (True) 34 | page.pack_start (self.label, False, False, 0) 35 | troubleshooter.new_page (page, self) 36 | 37 | def display (self): 38 | answers = self.troubleshooter.answers 39 | if not answers['cups_queue_listed']: 40 | return False 41 | 42 | if ('remote_server_connect_ipp' in answers and 43 | answers['remote_server_connect_ipp'] == False): 44 | self.label.set_text (_("Please check to see if a firewall or " 45 | "router configuration is blocking TCP " 46 | "port %d on server '%s'.") 47 | % (answers['remote_server_port'], 48 | answers['remote_server_try_connect'])) 49 | return True 50 | return False 51 | 52 | def can_click_forward (self): 53 | return False 54 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012 Red Hat, Inc. 6 | ## Authors: 7 | ## Florian Festi 8 | ## Tim Waugh 9 | 10 | ## This program is free software; you can redistribute it and/or modify 11 | ## it under the terms of the GNU General Public License as published by 12 | ## the Free Software Foundation; either version 2 of the License, or 13 | ## (at your option) any later version. 14 | 15 | ## This program is distributed in the hope that it will be useful, 16 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ## GNU General Public License for more details. 19 | 20 | ## You should have received a copy of the GNU General Public License 21 | ## along with this program; if not, write to the Free Software 22 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | from gi.repository import GObject 25 | from gi.repository import Gtk 26 | import os 27 | 28 | import config 29 | pkgdata = config.pkgdatadir 30 | 31 | class GtkGUI(GObject.GObject): 32 | def getWidgets(self, widgets, domain=None): 33 | ui_dir = os.environ.get ("SYSTEM_CONFIG_PRINTER_UI", 34 | os.path.join (pkgdata, "ui")) 35 | self._bld = [] 36 | for xmlfile, names in widgets.items (): 37 | bld = Gtk.Builder () 38 | self._bld.append (bld) 39 | 40 | if domain: 41 | bld.set_translation_domain (domain) 42 | 43 | bld.add_from_file (os.path.join (ui_dir, xmlfile + ".ui")) 44 | for name in names: 45 | widget = bld.get_object(name) 46 | if widget is None: 47 | raise ValueError("Widget '%s' not found" % name) 48 | setattr(self, name, widget) 49 | 50 | try: 51 | win = widget.get_top_level() 52 | except AttributeError: 53 | win = None 54 | 55 | if win is not None: 56 | Gtk.Window.set_focus_on_map(widget.get_top_level (), 57 | self.focus_on_map) 58 | widget.show() 59 | 60 | def connect_signals (self): 61 | for bld in self._bld: 62 | bld.connect_signals (self) 63 | -------------------------------------------------------------------------------- /killtimer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import threading 24 | 25 | from gi.repository import GLib 26 | 27 | from debug import * 28 | 29 | class KillTimer: 30 | def __init__ (self, timeout=30, killfunc=None): 31 | self._timeout = timeout 32 | self._killfunc = killfunc 33 | self._holds = 0 34 | self._add_timeout () 35 | self._lock = threading.Lock() 36 | 37 | def _add_timeout (self): 38 | self._timer = GLib.timeout_add_seconds (self._timeout, self._kill) 39 | 40 | def _kill (self): 41 | debugprint ("Timeout (%ds), exiting" % self._timeout) 42 | if self._killfunc: 43 | self._killfunc () 44 | else: 45 | sys.exit (0) 46 | 47 | def add_hold (self): 48 | self._lock.acquire() 49 | if self._holds == 0: 50 | debugprint ("Kill timer stopped") 51 | GLib.source_remove (self._timer) 52 | 53 | self._holds += 1 54 | self._lock.release() 55 | 56 | def remove_hold (self): 57 | self._lock.acquire() 58 | if self._holds > 0: 59 | self._holds -= 1 60 | if self._holds == 0: 61 | debugprint ("Kill timer started") 62 | self._add_timeout () 63 | self._lock.release() 64 | 65 | def alive (self): 66 | self._lock.acquire() 67 | if self._holds == 0: 68 | GLib.source_remove (self._timer) 69 | self._add_timeout () 70 | self._lock.release() 71 | -------------------------------------------------------------------------------- /installpackage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2008, 2009, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import os 23 | import dbus 24 | import dbus.glib 25 | from gi.repository import GLib 26 | from debug import * 27 | 28 | class PackageKit: 29 | DBUS_NAME="org.freedesktop.PackageKit" 30 | DBUS_PATH="/org/freedesktop/PackageKit" 31 | DBUS_IFACE="org.freedesktop.PackageKit.Modify" 32 | 33 | def __init__ (self): 34 | try: 35 | bus = dbus.SessionBus () 36 | remote_object = bus.get_object(self.DBUS_NAME, self.DBUS_PATH) 37 | iface = dbus.Interface(remote_object, self.DBUS_IFACE) 38 | except dbus.exceptions.DBusException: 39 | # System bus not running. 40 | iface = None 41 | self.iface = iface 42 | 43 | def InstallPackageName (self, xid, timestamp, name): 44 | try: 45 | if self.iface is not None: 46 | self.iface.InstallPackageNames(xid, [name], 47 | "show-progress,show-finished,show-warning", 48 | timeout = 999999) 49 | except dbus.exceptions.DBusException: 50 | pass 51 | 52 | def InstallProvideFile (self, xid, timestamp, filename): 53 | try: 54 | if self.iface is not None: 55 | self.iface.InstallProvideFiles(xid, [filename], 56 | "show-progress,show-finished,show-warning", 57 | timeout = 999999) 58 | except dbus.exceptions.DBusException: 59 | pass 60 | -------------------------------------------------------------------------------- /troubleshoot/SchedulerNotRunning.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class SchedulerNotRunning(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Scheduler not running?") 28 | page = self.initial_vbox (_("CUPS Service Stopped"), 29 | _("The CUPS print spooler does not appear " 30 | "to be running. To correct this, choose " 31 | "System->Administration->Services from " 32 | "the main menu and look for the 'cups' " 33 | "service.")) 34 | troubleshooter.new_page (page, self) 35 | 36 | def display (self): 37 | self.answers = {} 38 | if self.troubleshooter.answers.get ('cups_queue_listed', False): 39 | return False 40 | 41 | parent = self.troubleshooter.get_window () 42 | 43 | # Find out if CUPS is running. 44 | failure = False 45 | try: 46 | self.op = TimedOperation (cups.Connection, 47 | parent=parent) 48 | c = self.op.run () 49 | except RuntimeError: 50 | failure = True 51 | 52 | self.answers['cups_connection_failure'] = failure 53 | return failure 54 | 55 | def can_click_forward (self): 56 | return False 57 | 58 | def collect_answer (self): 59 | return self.answers 60 | 61 | def cancel_operation (self): 62 | self.op.cancel () 63 | -------------------------------------------------------------------------------- /troubleshoot/ErrorLogParse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | from functools import reduce 26 | class ErrorLogParse(Question): 27 | 28 | ## This could be a LOT smarter. 29 | 30 | def __init__ (self, troubleshooter): 31 | Question.__init__ (self, troubleshooter, "Error log parse") 32 | page = self.initial_vbox (_("Error log messages"), 33 | _("There are messages in the error log.")) 34 | sw = Gtk.ScrolledWindow () 35 | textview = Gtk.TextView () 36 | textview.set_editable (False) 37 | sw.add (textview) 38 | page.pack_start (sw, True, True, 0) 39 | self.buffer = textview.get_buffer () 40 | troubleshooter.new_page (page, self) 41 | 42 | def display (self): 43 | answers = self.troubleshooter.answers 44 | try: 45 | journal = answers.get ('journal') 46 | error_log = answers.get ('error_log') 47 | except KeyError: 48 | return False 49 | 50 | display = False 51 | if error_log: 52 | for line in error_log: 53 | if line[0] == 'E': 54 | display = error_log 55 | break 56 | 57 | if journal and not display: 58 | for line in journal: 59 | if line[0] == 'E': 60 | display = journal 61 | break 62 | 63 | if display: 64 | self.buffer.set_text (reduce (lambda x, y: x + '\n' + y, 65 | display)) 66 | 67 | return display != False 68 | -------------------------------------------------------------------------------- /ui/WaitWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | Please Wait 8 | False 9 | True 10 | center-on-parent 11 | True 12 | 13 | 14 | True 15 | False 16 | 0 17 | out 18 | 19 | 20 | True 21 | False 22 | 12 23 | 6 24 | 12 25 | 26 | 27 | True 28 | False 29 | dialog-information 30 | 6 31 | 32 | 33 | True 34 | True 35 | 0 36 | 37 | 38 | 39 | 40 | True 41 | False 42 | label135 43 | True 44 | True 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ui/AboutDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 5 8 | normal 9 | Copyright © 2006-2012 Red Hat, Inc. 10 | A CUPS configuration tool. 11 | https://github.com/OpenPrinting/system-config-printer/ 12 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | Florian Festi <ffesti@redhat.com> 18 | Tim Waugh <twaugh@redhat.com> 19 | Zdenek Dohnal <zdohnal@redhat.com> 20 | Till Kamppeter <till.kamppeter@gmail.com> 21 | translator-credits 22 | True 23 | 24 | 25 | False 26 | vertical 27 | 28 | 29 | False 30 | 31 | 32 | False 33 | True 34 | end 35 | 0 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /troubleshoot/RemoteAddress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class RemoteAddress(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Remote address") 28 | page = self.initial_vbox (_("Remote Address"), 29 | _("Please enter as many details as you " 30 | "can about the network address of this " 31 | "printer.")) 32 | grid = Gtk.Grid() 33 | grid.set_row_spacing (6) 34 | grid.set_column_spacing (6) 35 | page.pack_start (grid, False, False, 0) 36 | 37 | label = Gtk.Label(label=_("Server name:")) 38 | label.set_alignment (0, 0) 39 | grid.attach (label, 0, 0, 1, 1) 40 | self.server_name = Gtk.Entry () 41 | self.server_name.set_activates_default (True) 42 | grid.attach (self.server_name, 1, 0, 1, 1) 43 | 44 | label = Gtk.Label(label=_("Server IP address:")) 45 | label.set_alignment (0, 0) 46 | grid.attach (label, 0, 1, 1, 1) 47 | self.server_ipaddr = Gtk.Entry () 48 | self.server_ipaddr.set_activates_default (True) 49 | grid.attach (self.server_ipaddr, 1, 1, 1, 1) 50 | 51 | troubleshooter.new_page (page, self) 52 | 53 | def display (self): 54 | answers = self.troubleshooter.answers 55 | if answers['cups_queue_listed']: 56 | return False 57 | 58 | return answers['printer_is_remote'] 59 | 60 | def collect_answer (self): 61 | if not self.displayed: 62 | return {} 63 | 64 | return { 'remote_server_name': self.server_name.get_text (), 65 | 'remote_server_ip_address': self.server_ipaddr.get_text () } 66 | -------------------------------------------------------------------------------- /xml/validate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ## This program performs validation that cannot be performed using 24 | ## RELAX NG alone. 25 | 26 | import fnmatch 27 | import sys 28 | import xml.etree.ElementTree 29 | 30 | class Validator: 31 | def __init__ (self, filename): 32 | self._filename = filename 33 | 34 | def validate (self): 35 | filename = self._filename 36 | print ("Validating %s" % filename) 37 | preferreddrivers = xml.etree.ElementTree.XML (open (filename).read ()) 38 | (drivertypes, preferenceorder) = list(preferreddrivers) 39 | validates = True 40 | 41 | names = set() 42 | for drivertype in list(drivertypes): 43 | name = drivertype.get ("name") 44 | names.add (name) 45 | 46 | for printer in list(preferenceorder): 47 | types = [] 48 | drivers = printer.find ("drivers") 49 | if drivers is not None: 50 | types.extend (list(drivers)) 51 | 52 | blacklist = printer.find ("blacklist") 53 | if blacklist is not None: 54 | types.extend (list(blacklist)) 55 | 56 | for drivertype in types: 57 | pattern = drivertype.text.strip () 58 | matches = fnmatch.filter (names, pattern) 59 | names -= set (matches) 60 | 61 | for name in names: 62 | validates = False 63 | print(("*** Driver type \"%s\" is never used" % 64 | name), file=sys.stderr) 65 | 66 | return validates 67 | 68 | import getopt 69 | import os 70 | opts, args = getopt.getopt (sys.argv[1:], "") 71 | 72 | if len (args) < 1: 73 | dirname = os.path.dirname (sys.argv[0]) 74 | args = [os.path.join (dirname, "preferreddrivers.xml")] 75 | 76 | exitcode = 0 77 | for filename in args: 78 | validator = Validator (filename) 79 | if not validator.validate (): 80 | exitcode = 1 81 | 82 | sys.exit (exitcode) 83 | -------------------------------------------------------------------------------- /cupshelpers/installdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2010 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import dbus 23 | import dbus.glib 24 | import dbus.service 25 | from . import _debugprint, set_debugprint_fn 26 | 27 | class PrinterDriversInstaller(dbus.service.Object): 28 | DBUS_PATH = "/com/redhat/PrinterDriversInstaller" 29 | DBUS_IFACE = "com.redhat.PrinterDriversInstaller" 30 | DBUS_OBJ = "com.redhat.PrinterDriversInstaller" 31 | 32 | def __init__ (self, bus): 33 | self.bus = bus 34 | bus_name = dbus.service.BusName (self.DBUS_OBJ, bus=bus) 35 | dbus.service.Object.__init__ (self, bus_name, self.DBUS_PATH) 36 | 37 | @dbus.service.method(DBUS_IFACE, 38 | in_signature="sss", 39 | async_callbacks=("reply_handler", 40 | "error_handler")) 41 | def InstallDrivers (self, mfg, mdl, cmd, 42 | reply_handler, error_handler): 43 | bus = dbus.SessionBus () 44 | obj = bus.get_object ("org.freedesktop.PackageKit", 45 | "/org/freedesktop/PackageKit") 46 | proxy = dbus.Interface (obj, "org.freedesktop.PackageKit.Modify") 47 | xid = 0 48 | resources = ["MFG:%s;MDL:%s;" % (mfg, mdl)] 49 | interaction = "hide-finished" 50 | _debugprint ("Calling InstallPrinterDrivers (%s, %s, %s)" % 51 | (repr (xid), repr (resources), repr (interaction))) 52 | proxy.InstallPrinterDrivers (dbus.UInt32 (xid), 53 | resources, interaction, 54 | reply_handler=reply_handler, 55 | error_handler=error_handler, 56 | timeout=3600) 57 | 58 | def client_test(): 59 | bus = dbus.SystemBus () 60 | import sys 61 | obj = bus.get_object (PrinterDriversInstaller.DBUS_OBJ, 62 | PrinterDriversInstaller.DBUS_PATH) 63 | proxy = dbus.Interface (obj, PrinterDriversInstaller.DBUS_IFACE) 64 | print (proxy.InstallDrivers ("MFG", "MDL", "CMD")) 65 | -------------------------------------------------------------------------------- /troubleshoot/VerifyPackages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2010, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2010 Jiri Popelka 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import subprocess 25 | from .base import * 26 | import os 27 | from timedops import TimedSubprocess 28 | 29 | class VerifyPackages(Question): 30 | def __init__ (self, troubleshooter): 31 | Question.__init__ (self, troubleshooter, "Verify packages") 32 | troubleshooter.new_page (Gtk.Label (), self) 33 | 34 | def display (self): 35 | self.answers = {} 36 | packages_verification = {} 37 | 38 | package_manager="/bin/rpm" 39 | if not os.access (package_manager, os.X_OK): 40 | return False 41 | 42 | packages = ["cups", 43 | "foomatic", 44 | "gutenprint", 45 | "hpijs", 46 | "hplip", 47 | "system-config-printer"] 48 | parent = self.troubleshooter.get_window () 49 | 50 | new_environ = os.environ.copy() 51 | new_environ['LC_ALL'] = "C" 52 | 53 | for package in packages: 54 | verification_args = [package_manager, "-V", package] 55 | try: 56 | self.op = TimedSubprocess (parent=parent, 57 | args=verification_args, 58 | close_fds=True, 59 | env=new_environ, 60 | stdin=subprocess.DEVNULL, 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.DEVNULL) 63 | (verif_stdout, verif_stderr, result) = self.op.run () 64 | except: 65 | # Problem executing command. 66 | return False 67 | packages_verification[package] = verif_stdout[:-1] 68 | 69 | self.answers['packages_verification'] = packages_verification 70 | return False 71 | 72 | def collect_answer (self): 73 | return self.answers 74 | 75 | def cancel_operation (self): 76 | self.op.cancel () 77 | -------------------------------------------------------------------------------- /troubleshoot/Welcome.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | from timedops import TimedOperation 26 | import authconn 27 | 28 | class AuthConnFactory: 29 | def __init__ (self, parent): 30 | self.parent = parent 31 | 32 | def get_connection (self): 33 | return authconn.Connection (self.parent, lock=True) 34 | 35 | class Welcome(Question): 36 | def __init__ (self, troubleshooter): 37 | Question.__init__ (self, troubleshooter, "Welcome") 38 | welcome = Gtk.HBox () 39 | welcome.set_spacing (12) 40 | welcome.set_border_width (12) 41 | image = Gtk.Image () 42 | image.set_alignment (0, 0) 43 | image.set_from_stock (Gtk.STOCK_PRINT, Gtk.IconSize.DIALOG) 44 | intro = Gtk.Label(label='' + 45 | _("Trouble-shooting Printing") + 46 | '\n\n' + 47 | _("The next few screens will contain some " 48 | "questions about your problem with printing. " 49 | "Based on your answers a solution may be " 50 | "suggested.") + '\n\n' + 51 | _("Click 'Forward' to begin.")) 52 | intro.set_alignment (0, 0) 53 | intro.set_use_markup (True) 54 | intro.set_line_wrap (True) 55 | welcome.pack_start (image, False, False, 0) 56 | welcome.pack_start (intro, True, True, 0) 57 | page = troubleshooter.new_page (welcome, self) 58 | 59 | def collect_answer (self): 60 | parent = self.troubleshooter.get_window () 61 | # Store the authentication dialog instance in the answers. This 62 | # allows the password to be cached. 63 | factory = AuthConnFactory (parent) 64 | self.op = TimedOperation (factory.get_connection, parent=parent) 65 | return {'_authenticated_connection_factory': factory, 66 | '_authenticated_connection': self.op.run () } 67 | 68 | def cancel_operation (self): 69 | self.op.cancel () 70 | -------------------------------------------------------------------------------- /troubleshoot/CheckSELinux.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2010, 2014 Red Hat, Inc. 6 | ## Copyright (C) 2010 Jiri Popelka 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import subprocess 25 | from .base import * 26 | import os 27 | import shlex 28 | from timedops import TimedSubprocess 29 | 30 | class CheckSELinux(Question): 31 | def __init__ (self, troubleshooter): 32 | Question.__init__ (self, troubleshooter, "Check SELinux contexts") 33 | troubleshooter.new_page (Gtk.Label (), self) 34 | 35 | def display (self): 36 | self.answers = {} 37 | #answers = self.troubleshooter.answers 38 | 39 | RESTORECON = "/sbin/restorecon" 40 | if not os.access (RESTORECON, os.X_OK): 41 | return False 42 | 43 | try: 44 | import selinux 45 | except ImportError: 46 | return False 47 | if not selinux.is_selinux_enabled(): 48 | return False 49 | 50 | paths = ["/etc/cups/", "/usr/lib/cups/", "/usr/share/cups/"] 51 | parent = self.troubleshooter.get_window () 52 | contexts = {} 53 | new_environ = os.environ.copy() 54 | new_environ['LC_ALL'] = "C" 55 | restorecon_args = [RESTORECON, "-nvR"].extend(paths) 56 | try: 57 | # Run restorecon -nvR 58 | self.op = TimedSubprocess (parent=parent, 59 | args=restorecon_args, 60 | close_fds=True, 61 | env=new_environ, 62 | stdin=subprocess.DEVNULL, 63 | stdout=subprocess.PIPE, 64 | stderr=subprocess.DEVNULL) 65 | (restorecon_stdout, restorecon_stderr, result) = self.op.run () 66 | except: 67 | # Problem executing command. 68 | return False 69 | for line in restorecon_stdout: 70 | l = shlex.split (line) 71 | if (len (l) < 1): 72 | continue 73 | contexts[l[2]] = l[4] 74 | self.answers['selinux_contexts'] = contexts 75 | return False 76 | 77 | def collect_answer (self): 78 | return self.answers 79 | 80 | def cancel_operation (self): 81 | self.op.cancel () 82 | -------------------------------------------------------------------------------- /troubleshoot/QueueNotEnabled.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import cups 25 | from .base import * 26 | class QueueNotEnabled(Question): 27 | def __init__ (self, troubleshooter): 28 | Question.__init__ (self, troubleshooter, "Queue not enabled?") 29 | self.label = Gtk.Label () 30 | solution = Gtk.VBox () 31 | self.label.set_line_wrap (True) 32 | self.label.set_alignment (0, 0) 33 | solution.pack_start (self.label, False, False, 0) 34 | solution.set_border_width (12) 35 | troubleshooter.new_page (solution, self) 36 | 37 | def display (self): 38 | answers = self.troubleshooter.answers 39 | if not answers['cups_queue_listed']: 40 | return False 41 | 42 | if answers['is_cups_class']: 43 | queue = answers['cups_class_dict'] 44 | else: 45 | queue = answers['cups_printer_dict'] 46 | 47 | enabled = queue['printer-state'] != cups.IPP_PRINTER_STOPPED 48 | if enabled: 49 | return False 50 | 51 | if answers['cups_printer_remote']: 52 | attrs = answers['remote_cups_queue_attributes'] 53 | reason = attrs['printer-state-message'] 54 | else: 55 | reason = queue['printer-state-message'] 56 | 57 | if reason: 58 | reason = _("The reason given is: '%s'.") % reason 59 | else: 60 | reason = _("This may be due to the printer being disconnected or " 61 | "switched off.") 62 | 63 | text = ('' + 64 | _("Queue Not Enabled") + '\n\n' + 65 | _("The queue '%s' is not enabled.") % 66 | answers['cups_queue']) 67 | 68 | if reason: 69 | text += ' ' + reason 70 | 71 | if not answers['cups_printer_remote']: 72 | text += '\n\n' 73 | text += _("To enable it, select the 'Enabled' checkbox in the " 74 | "'Policies' tab for the printer in the printer " 75 | "administration tool.") 76 | text += ' ' + _(TEXT_start_print_admin_tool) 77 | 78 | self.label.set_markup (text) 79 | return True 80 | 81 | def can_click_forward (self): 82 | return False 83 | -------------------------------------------------------------------------------- /troubleshoot/NetworkCUPSPrinterShared.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class NetworkCUPSPrinterShared(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Queue not shared?") 28 | page = self.initial_vbox (_("Queue Not Shared"), 29 | _("The CUPS printer on the server is not " 30 | "shared.")) 31 | troubleshooter.new_page (page, self) 32 | 33 | def display (self): 34 | self.answers = {} 35 | answers = self.troubleshooter.answers 36 | if ('remote_cups_queue_listed' in answers and 37 | answers['remote_cups_queue_listed'] == False): 38 | return False 39 | 40 | parent = self.troubleshooter.get_window () 41 | if 'remote_cups_queue_attributes' not in answers: 42 | if not ('remote_server_try_connect' in answers and 43 | 'remote_cups_queue' in answers): 44 | return False 45 | 46 | try: 47 | host = answers['remote_server_try_connect'] 48 | self.op = TimedOperation (cups.Connection, 49 | kwargs={"host": host}, 50 | parent=parent) 51 | c = self.op.run () 52 | self.op = TimedOperation (c.getPrinterAttributes, 53 | args=(answers['remote_cups_queue'],), 54 | parent=parent) 55 | attr = self.op.run () 56 | except RuntimeError: 57 | return False 58 | except cups.IPPError: 59 | return False 60 | 61 | self.answers['remote_cups_queue_attributes'] = attr 62 | else: 63 | attr = answers['remote_cups_queue_attributes'] 64 | 65 | if 'printer-is-shared' in attr: 66 | # CUPS >= 1.2 67 | if not attr['printer-is-shared']: 68 | return True 69 | 70 | return False 71 | 72 | def can_click_forward (self): 73 | return False 74 | 75 | def collect_answer (self): 76 | return self.answers 77 | 78 | def cancel_operation (self): 79 | self.op.cancel () 80 | -------------------------------------------------------------------------------- /troubleshoot/QueueRejectingJobs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2009 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import cups 25 | from .base import * 26 | class QueueRejectingJobs(Question): 27 | def __init__ (self, troubleshooter): 28 | Question.__init__ (self, troubleshooter, "Queue rejecting jobs?") 29 | solution = Gtk.VBox () 30 | solution.set_border_width (12) 31 | solution.set_spacing (12) 32 | label = Gtk.Label(label='' + 33 | _("Queue Rejecting Jobs") + '') 34 | label.set_alignment (0, 0) 35 | label.set_use_markup (True) 36 | solution.pack_start (label, False, False, 0) 37 | self.label = Gtk.Label () 38 | self.label.set_alignment (0, 0) 39 | self.label.set_line_wrap (True) 40 | solution.pack_start (self.label, False, False, 0) 41 | solution.set_border_width (12) 42 | 43 | troubleshooter.new_page (solution, self) 44 | 45 | def display (self): 46 | answers = self.troubleshooter.answers 47 | if not answers['cups_queue_listed']: 48 | return False 49 | 50 | if answers['is_cups_class']: 51 | queue = answers['cups_class_dict'] 52 | else: 53 | queue = answers['cups_printer_dict'] 54 | 55 | rejecting = queue['printer-type'] & cups.CUPS_PRINTER_REJECTING 56 | if not rejecting: 57 | return False 58 | 59 | if answers['cups_printer_remote']: 60 | attrs = answers['remote_cups_queue_attributes'] 61 | reason = attrs['printer-state-message'] 62 | else: 63 | reason = queue['printer-state-message'] 64 | 65 | text = (_("The queue '%s' is rejecting jobs.") % answers['cups_queue']) 66 | 67 | if reason: 68 | text += ' ' + _("The reason given is: '%s'.") % reason 69 | 70 | if not answers['cups_printer_remote']: 71 | text += "\n\n" 72 | text += _("To make the queue accept jobs, select the " 73 | "'Accepting Jobs' checkbox in the 'Policies' " 74 | "tab for the printer in the printer administration " 75 | "tool.") + ' ' + _(TEXT_start_print_admin_tool) 76 | 77 | self.label.set_text (text) 78 | return True 79 | 80 | def can_click_forward (self): 81 | return False 82 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(system-config-printer, 1.5.13) 2 | AC_CONFIG_SRCDIR(system-config-printer.py) 3 | AM_INIT_AUTOMAKE([dist-xz dist-bzip2 subdir-objects 1.6]) 4 | IT_PROG_INTLTOOL 5 | AM_GNU_GETTEXT([external]) 6 | AM_PATH_PYTHON([3]) 7 | PACKAGE="AC_PACKAGE_NAME" 8 | VERSION="AC_PACKAGE_VERSION" 9 | GETTEXT_PACKAGE="AC_PACKAGE_NAME" 10 | CATOBJEXT=".gmo" 11 | DATADIRNAME=share 12 | AC_SUBST(PACKAGE) 13 | AC_SUBST(VERSION) 14 | AC_SUBST(GETTEXT_PACKAGE) 15 | AC_SUBST(CATOBJEXT) 16 | AC_SUBST(DATADIRNAME) 17 | 18 | # Let distributor specify if they want to use a vendor with desktop-file-install 19 | AC_ARG_WITH(desktop-vendor, 20 | [AC_HELP_STRING([--with-desktop-vendor], 21 | [Specify the vendor for use in calls to desktop-file-install @<:@default=@:>@])],, 22 | [with_desktop_vendor=""]) 23 | 24 | VENDOR=$with_desktop_vendor 25 | if test "x$VENDOR" = "x"; then 26 | DESKTOPVENDOR= 27 | DESKTOPPREFIX= 28 | else 29 | DESKTOPVENDOR="--vendor $VENDOR" 30 | DESKTOPPREFIX="$VENDOR-" 31 | fi 32 | AC_SUBST(DESKTOPVENDOR) 33 | AC_SUBST(DESKTOPPREFIX) 34 | 35 | cupsserverbindir="`cups-config --serverbin`" 36 | AC_SUBST(cupsserverbindir) 37 | 38 | PKG_CHECK_MODULES(GLIB, glib-2.0, has_glib=yes, has_glib=no) 39 | 40 | AC_ARG_WITH(udev-rules, 41 | [AC_HELP_STRING([--with-udev-rules], 42 | [Enable automatic USB print queue configuration @<:@default=no@:>@])], 43 | [], 44 | [with_udev_rules=no]) 45 | AM_CONDITIONAL([UDEV_RULES], [test x$with_udev_rules != xno]) 46 | 47 | AC_ARG_WITH([udevdir], 48 | AS_HELP_STRING([--with-udevdir=DIR], [Directory for udev helper programs]), 49 | [], [with_udevdir=$($PKG_CONFIG --variable=udevdir udev)]) 50 | if test "x$with_udevdir" != xno; then 51 | AC_SUBST([udevdir], [$with_udevdir]) 52 | AC_SUBST([udevrulesdir], [$with_udevdir/rules.d]) 53 | fi 54 | 55 | if test "x$with_udev_rules" != xno -a "x$with_udevdir" != xno; then 56 | PKG_CHECK_MODULES(libudev, [libudev >= 172], has_libudev=yes, has_libudev=no) 57 | PKG_CHECK_MODULES(libusb, libusb-1.0, has_libusb=yes, has_libusb=no) 58 | if test x$has_glib == xno -o \ 59 | x$has_udev == xno -o \ 60 | x$has_libudev == xno -o \ 61 | x$has_libusb == xno ; then 62 | AC_MSG_ERROR([Missing packages]) 63 | fi 64 | 65 | AM_PROG_CC_C_O 66 | fi 67 | 68 | PKG_PROG_PKG_CONFIG 69 | AC_ARG_WITH([systemdsystemunitdir], 70 | AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), 71 | [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) 72 | if test "x$with_systemdsystemunitdir" != xno; then 73 | AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) 74 | fi 75 | AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) 76 | 77 | ALL_LINGUAS="ar as ast bg bn bn_IN br bs ca cs cy da de el en_GB es et fa fi fr fur gu he hi hr hu id is it ja kn ko lt lv mai ml mr ms nb nds nl nn oc or pa pl pt pt_BR ro ru si sk sl sr sr@latin sv ta te th tr uk vi zh_CN zh_TW" 78 | AC_CONFIG_FILES([ 79 | Makefile 80 | po/Makefile.in 81 | system-config-printer 82 | system-config-printer-applet 83 | install-printerdriver 84 | dbus/scp-dbus-service 85 | udev/configure-printer@.service 86 | ]) 87 | AC_OUTPUT 88 | -------------------------------------------------------------------------------- /troubleshoot/CheckLocalServerPublishing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import cups 23 | from timedops import TimedOperation 24 | from .base import * 25 | class CheckLocalServerPublishing(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Is local server publishing?") 28 | vbox = self.initial_vbox (_("Server Not Exporting Printers"), 29 | _("Although one or more printers are marked " 30 | "as being shared, this print server is " 31 | "not exporting shared printers to the " 32 | "network.") + '\n\n' + 33 | _("Enable the 'Publish shared printers " 34 | "connected to this system' option in " 35 | "the server settings using the printing " 36 | "administration tool.") + ' ' + 37 | _(TEXT_start_print_admin_tool)) 38 | troubleshooter.new_page (vbox, self) 39 | 40 | def display (self): 41 | self.answers = {} 42 | cups.setServer ('') 43 | parent = self.troubleshooter.get_window () 44 | try: 45 | c = self.timedop (cups.Connection, parent=parent).run () 46 | printers = self.timedop (c.getPrinters, parent=parent).run () 47 | if len (printers) == 0: 48 | return False 49 | 50 | for name, printer in printers.items (): 51 | if printer.get ('printer-is-shared', False): 52 | break 53 | 54 | attr = self.timedop (c.getPrinterAttributes, 55 | args=(name,), 56 | parent=parent).run () 57 | except RuntimeError: 58 | return False 59 | except cups.IPPError: 60 | return False 61 | 62 | if not printer.get ('printer-is-shared', False): 63 | return False 64 | 65 | if attr.get ('server-is-sharing-printers', True): 66 | # server-is-sharing-printers is in CUPS 1.4 67 | return False 68 | 69 | return True 70 | 71 | def collect_answer (self): 72 | if self.displayed: 73 | return { 'local_server_exporting_printers': False } 74 | 75 | return {} 76 | 77 | def cancel_operation (self): 78 | self.op.cancel () 79 | 80 | def timedop (self, *args, **kwargs): 81 | self.op = TimedOperation (*args, **kwargs) 82 | return self.op 83 | -------------------------------------------------------------------------------- /errordialogs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2010, 2013 Red Hat, Inc. 6 | ## Authors: 7 | ## Florian Festi 8 | ## Tim Waugh 9 | 10 | ## This program is free software; you can redistribute it and/or modify 11 | ## it under the terms of the GNU General Public License as published by 12 | ## the Free Software Foundation; either version 2 of the License, or 13 | ## (at your option) any later version. 14 | 15 | ## This program is distributed in the hope that it will be useful, 16 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ## GNU General Public License for more details. 19 | 20 | ## You should have received a copy of the GNU General Public License 21 | ## along with this program; if not, write to the Free Software 22 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | import config 25 | import cups 26 | from gi.repository import Gtk 27 | import gettext 28 | gettext.install(domain=config.PACKAGE, localedir=config.localedir) 29 | 30 | def show_dialog (title, text, type, parent=None): 31 | dialog = Gtk.MessageDialog (parent=parent, 32 | modal=True, destroy_with_parent=True, 33 | message_type=type, 34 | buttons=Gtk.ButtonsType.OK, 35 | text=title) 36 | dialog.format_secondary_text (text) 37 | dialog.run () 38 | dialog.destroy () 39 | 40 | def show_info_dialog (title, text, parent=None): 41 | return show_dialog (title, text, Gtk.MessageType.INFO, parent=parent) 42 | 43 | def show_error_dialog (title, text, parent=None): 44 | return show_dialog (title, text, Gtk.MessageType.ERROR, parent=parent) 45 | 46 | def show_IPP_Error(exception, message, parent=None): 47 | if exception == 0: 48 | # In this case, the user has canceled an authentication dialog. 49 | return 50 | elif exception == cups.IPP_SERVICE_UNAVAILABLE: 51 | # In this case, the user has canceled a retry dialog. 52 | return 53 | else: 54 | title = _("CUPS server error") 55 | text = _("There was an error during the CUPS " 56 | "operation: '%s'.") % message 57 | 58 | show_error_dialog (title, text, parent) 59 | 60 | def show_HTTP_Error(status, parent=None): 61 | if (status == cups.HTTP_UNAUTHORIZED or 62 | status == cups.HTTP_FORBIDDEN): 63 | title = _('Not authorized') 64 | text = (_('The password may be incorrect, or the ' 65 | 'server may be configured to deny ' 66 | 'remote administration.')) 67 | else: 68 | title = _('CUPS server error') 69 | if status == cups.HTTP_BAD_REQUEST: 70 | msg = _("Bad request") 71 | elif status == cups.HTTP_NOT_FOUND: 72 | msg = _("Not found") 73 | elif status == cups.HTTP_REQUEST_TIMEOUT: 74 | msg = _("Request timeout") 75 | elif status == cups.HTTP_UPGRADE_REQUIRED: 76 | msg = _("Upgrade required") 77 | elif status == cups.HTTP_SERVER_ERROR: 78 | msg = _("Server error") 79 | elif status == -1: 80 | msg = _("Not connected") 81 | else: 82 | msg = _("status %s") % status 83 | 84 | text = _("There was an HTTP error: %s.") % msg 85 | 86 | show_error_dialog (title, text, parent) 87 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 1.5.14 changes 2 | -------------- 3 | 4 | 1.5.13 changes 5 | -------------- 6 | - add checks for NULL in udev-configure-printer (Fedora #1761097) 7 | - github #174 - put back notification about missing pysmbc 8 | - update .pot file because of fix #174 9 | - python3.9 - xml module removed elem.getchildren() method, use list(elem) 10 | - Make the matching rule of printer device path more flexible (#183) 11 | 12 | 1.5.12 changes 13 | -------------- 14 | - when you use different component id, add provide for old name (issues/99) 15 | - fix other issues in system-config-printer.appdata.xml to be completely valid 16 | for new format 17 | - make the appstream file validate with version >= 0.6 (issues/97) 18 | - fixes for scp-dbus-services (pull/96) 19 | - use ValueError instead of ImportError (pull/95) 20 | - fix constructing the auth dialog (pull/93) 21 | - update da.po (pull/102) 22 | - use utf-8 in fdopen() (pull/112) 23 | - Fallback to using LC_CTYPE if LC_MESSAGES is empty and fix _language use (pull/108) 24 | - Update de.po (pull/106) 25 | - Fix TypeError raised by debugprint call (pull/121) 26 | - dbus: remove deprecated at_console statement (pull/123) 27 | - fixed several memory leaks reported by Coverity scan 28 | - temporary fix for error pop up message for IPP2.0+ attributes (issues/122) 29 | - lpd queue names printed on the console (issues/132) 30 | - use proper docstring (pull/130) 31 | - remove deprecated SIGNAL_RUN_LAST (pull/134) 32 | - use remote ppd for CUPS shared queues (pull/137) 33 | - get rid of warnings in applet (Fedora issue #1732890) 34 | - check if we have required parameters in install-printerdriver (Fedora issue #1754204) 35 | 36 | 1.5.10 changes 37 | -------------- 38 | 39 | - printer couldn't be add ( https://bugzilla.redhat.com/show_bug.cgi?id=1419175 ) 40 | - changes from Ubuntu by Till Kamppeter (pull/64) 41 | - .travis.yml: switch from precise to trusty (pull/63) 42 | - Replace icons deprecated by GTK 3.0 by non-deprecated ones (pull/62) 43 | - Add a StartService for systemd based systems (pull/56) 44 | - French translation update (pull/57) 45 | - Spelling fixes (pull/58) 46 | - Syntax fixes (pull/59) 47 | - Python 3.6 invalid escape sequence deprecation fixes (pull/60) 48 | - Adds printer properties dialog vertical expansion (pull/61) 49 | - Replace icons deprecated by GTK 3.0 by non-deprecated ones (pull/62) 50 | - Improvements of discovered devices/conection type lists in new-printer wizard, more debug output (pull/65) 51 | - replace libgnome-keyring by libsecret (issues/51) 52 | - Do not start the applet on GNOME and Cinnamon desktops (pull/41) 53 | - Do not notify on 'cups-waiting-for-job-completed' because it's not an… (pull/71) 54 | - Updated Turkish translation (pull/74) 55 | - Update tr.po (pull/73) 56 | - TypeError on .update call (issues/76) 57 | - build: Install appstream metadata to non-deprecated location #77 (pull/77) 58 | - Addition of some strings for i18n (pull/81) 59 | - Update .po and .pot files 60 | - added GenericName and X-GNOME-FullName to system-config-printer.desktop.in (issues/20) 61 | - removed some deprecated parts of gui 62 | - updated translations by files from Zanata 63 | - s-c-p doesn't react on ALREADY_ENABLED exception from firewalld 64 | - removed deprecated Gtk objects 65 | - another deprecated issues - GLib 66 | - parent attribute in Gtk.Dialog is deprecated - use transient_for 67 | - system-config-printer.py doesn't have program name (issues/53) 68 | - removed macedonian localization because of low rate of translated strings 69 | - don't ship pre-configured scripts 70 | -------------------------------------------------------------------------------- /smburi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## system-config-printer 4 | 5 | ## Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2006, 2007 Florian Festi 7 | ## Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import urllib.parse 24 | 25 | def urlquote (x): 26 | q = urllib.parse.quote (x) 27 | for c in ["/", "@", ":"]: 28 | q = q.replace (c, "%%%02X" % ord (c)) 29 | 30 | return q 31 | 32 | class SMBURI: 33 | def __init__ (self, 34 | uri=None, 35 | group='', host='', share='', user='', password=''): 36 | if uri: 37 | if group or host or share or user or password: 38 | raise RuntimeError 39 | 40 | if uri.startswith ("smb://"): 41 | uri = uri[6:] 42 | 43 | self.uri = uri 44 | else: 45 | self.uri = self._construct (group, host, share, 46 | user=user, password=password) 47 | 48 | def _construct (self, group, host, share, user='', password=''): 49 | uri_password = '' 50 | if password: 51 | uri_password = ':' + urlquote (password) 52 | if user: 53 | uri_password += '@' 54 | uri = "%s%s%s" % (urlquote (user), 55 | uri_password, 56 | urlquote (group)) 57 | if len (group) > 0: 58 | uri += '/' 59 | uri += urlquote (host) 60 | if len (share) > 0: 61 | uri += "/" + urlquote (share) 62 | return uri 63 | 64 | def get_uri (self): 65 | return self.uri 66 | 67 | def sanitize_uri (self): 68 | group, host, share, user, password = self.separate () 69 | return self._construct (group, host, share) 70 | 71 | def separate (self): 72 | uri = self.get_uri () 73 | user = '' 74 | password = '' 75 | auth = uri.find ('@') 76 | if auth != -1: 77 | u = uri[:auth].find(':') 78 | if u != -1: 79 | user = uri[:u] 80 | password = uri[u + 1:auth] 81 | else: 82 | user = uri[:auth] 83 | uri = uri[auth + 1:] 84 | sep = uri.count ('/') 85 | group = '' 86 | if sep == 2: 87 | g = uri.find('/') 88 | group = uri[:g] 89 | uri = uri[g + 1:] 90 | if sep < 1: 91 | host = '' 92 | else: 93 | h = uri.find('/') 94 | host = uri[:h] 95 | uri = uri[h + 1:] 96 | p = host.find(':') 97 | if p != -1: 98 | host = host[:p] 99 | share = uri 100 | return (urllib.parse.unquote (group), urllib.parse.unquote (host), 101 | urllib.parse.unquote (share), 102 | urllib.parse.unquote (user), urllib.parse.unquote (password)) 103 | -------------------------------------------------------------------------------- /test_PhysicalDevice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2015 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import pytest 22 | try: 23 | import cups 24 | from PhysicalDevice import PhysicalDevice 25 | from cupshelpers import cupshelpers 26 | except ImportError: 27 | cups = None 28 | 29 | @pytest.mark.skipif(cups is None, reason="cups module not available") 30 | def test_ordering(): 31 | # See https://bugzilla.redhat.com/show_bug.cgi?id=1154686 32 | device = cupshelpers.Device("dnssd://Abc%20Def%20%5BABCDEF%5D._ipp._tcp.local/", 33 | **{'device-class': "network", 34 | 'device-make-and-model': "Abc Def", 35 | 'device-id': "MFG:Abc;MDL:Def;"}) 36 | phys = PhysicalDevice (device) 37 | 38 | device = cupshelpers.Device("hp:/net/Abc_Def?hostname=ABCDEF", 39 | **{'device-class': "network", 40 | 'device-make-and-model': "Abc Def", 41 | 'device-id': "MFG:Abc;MDL:Def;"}) 42 | phys.add_device (device) 43 | devices = phys.get_devices () 44 | assert devices[0].uri.startswith ("hp:") 45 | 46 | device = cupshelpers.Device("usb://Abc/Def", 47 | **{'device-class': "direct", 48 | 'device-make-and-model': "Abc Def", 49 | 'device-id': "MFG:Abc;MDL:Def;"}) 50 | phys = PhysicalDevice (device) 51 | device = cupshelpers.Device("hp://Abc/Def", 52 | **{'device-class': "direct", 53 | 'device-make-and-model': "Abc Def", 54 | 'device-id': "MFG:Abc;MDL:Def;"}) 55 | phys.add_device (device) 56 | devices = phys.get_devices () 57 | assert devices[0].uri.startswith ("hp") 58 | 59 | dev1 = cupshelpers.Device("hp:/usb/HP_Color_LaserJet_CP3525?serial=CNCTC8G0QX", 60 | **{'device-id':'MFG:Hewlett-Packard;CMD:PJL,MLC,BIDI-ECP,PJL,PCLXL,PCL,POSTSCRIPT,PDF;MDL:HP Color LaserJet CP3525;CLS:PRINTER;DES:Hewlett-Packard Color LaserJet CP3525;', 61 | 'device-make-and-model':'HP Color LaserJet CP3525', 62 | 'device-class':'direct'}) 63 | phys = PhysicalDevice (dev1) 64 | dev2 = cupshelpers.Device('usb://HP/Color%20LaserJet%20CP3525?serial=CNCTC8G0QX', 65 | **{'device-id':'MFG:Hewlett-Packard;CMD:PJL,MLC,BIDI-ECP,PJL,PCLXL,PCL,POSTSCRIPT,PDF;MDL:HP Color LaserJet CP3525;CLS:PRINTER;DES:Hewlett-Packard Color LaserJet CP3525;', 66 | 'device-make-and-model':'HP Color LaserJet CP3525', 67 | 'device-class':'direct'}) 68 | 69 | # hp device should sort < usb device 70 | assert dev1 < dev2 71 | 72 | phys.add_device (dev2) 73 | devices = phys.get_devices () 74 | assert devices[0] < devices[1] 75 | assert devices[0].uri.startswith ("hp") 76 | -------------------------------------------------------------------------------- /troubleshoot/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2010, 2012 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | N_ = lambda x: x 25 | from debug import * 26 | 27 | import config 28 | import gettext 29 | gettext.install(domain=config.PACKAGE, localedir=config.localedir) 30 | 31 | __all__ = [ 'debugprint', 'get_debugging', 'set_debugging', 32 | 'Question', 33 | 'Multichoice', 34 | 'TEXT_start_print_admin_tool' ] 35 | 36 | TEXT_start_print_admin_tool = N_("To start this tool, select " 37 | "System->Administration->Print Settings " 38 | "from the main menu.") 39 | 40 | class Question: 41 | def __init__ (self, troubleshooter, name=None): 42 | self.troubleshooter = troubleshooter 43 | if name: 44 | self.__str__ = lambda: name 45 | 46 | def display (self): 47 | """Returns True if this page should be displayed, or False 48 | if it should be skipped.""" 49 | return True 50 | 51 | def connect_signals (self, handler): 52 | pass 53 | 54 | def disconnect_signals (self): 55 | pass 56 | 57 | def can_click_forward (self): 58 | return True 59 | 60 | def collect_answer (self): 61 | return {} 62 | 63 | def cancel_operation (self): 64 | pass 65 | 66 | ## Helper functions 67 | def initial_vbox (self, title='', text=''): 68 | vbox = Gtk.VBox () 69 | vbox.set_border_width (12) 70 | vbox.set_spacing (12) 71 | if title: 72 | s = '' + title + '\n\n' 73 | else: 74 | s = '' 75 | s += text 76 | label = Gtk.Label(label=s) 77 | label.set_alignment (0, 0) 78 | label.set_line_wrap (True) 79 | label.set_use_markup (True) 80 | vbox.pack_start (label, False, False, 0) 81 | return vbox 82 | 83 | class Multichoice(Question): 84 | def __init__ (self, troubleshooter, question_tag, question_title, 85 | question_text, choices, name=None): 86 | Question.__init__ (self, troubleshooter, name) 87 | page = self.initial_vbox (question_title, question_text) 88 | choice_vbox = Gtk.VBox () 89 | choice_vbox.set_spacing (6) 90 | page.pack_start (choice_vbox, False, False, 0) 91 | self.question_tag = question_tag 92 | self.widgets = [] 93 | button = None 94 | for choice, tag in choices: 95 | if button: 96 | button = Gtk.RadioButton.new_with_label_from_widget(button, choice) 97 | else: 98 | # special case to work around GNOME#635253 99 | button = Gtk.RadioButton.new_with_label([], choice) 100 | choice_vbox.pack_start (button, False, False, 0) 101 | self.widgets.append ((button, tag)) 102 | 103 | troubleshooter.new_page (page, self) 104 | 105 | def collect_answer (self): 106 | for button, answer_tag in self.widgets: 107 | if button.get_active (): 108 | return { self.question_tag: answer_tag } 109 | -------------------------------------------------------------------------------- /mkinstalldirs: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | 4 | scriptversion=2009-04-28.21; # UTC 5 | 6 | # Original author: Noah Friedman 7 | # Created: 1993-05-16 8 | # Public domain. 9 | # 10 | # This file is maintained in Automake, please report 11 | # bugs to or send patches to 12 | # . 13 | 14 | nl=' 15 | ' 16 | IFS=" "" $nl" 17 | errstatus=0 18 | dirmode= 19 | 20 | usage="\ 21 | Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... 22 | 23 | Create each directory DIR (with mode MODE, if specified), including all 24 | leading file name components. 25 | 26 | Report bugs to ." 27 | 28 | # process command line arguments 29 | while test $# -gt 0 ; do 30 | case $1 in 31 | -h | --help | --h*) # -h for help 32 | echo "$usage" 33 | exit $? 34 | ;; 35 | -m) # -m PERM arg 36 | shift 37 | test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } 38 | dirmode=$1 39 | shift 40 | ;; 41 | --version) 42 | echo "$0 $scriptversion" 43 | exit $? 44 | ;; 45 | --) # stop option processing 46 | shift 47 | break 48 | ;; 49 | -*) # unknown option 50 | echo "$usage" 1>&2 51 | exit 1 52 | ;; 53 | *) # first non-opt arg 54 | break 55 | ;; 56 | esac 57 | done 58 | 59 | for file 60 | do 61 | if test -d "$file"; then 62 | shift 63 | else 64 | break 65 | fi 66 | done 67 | 68 | case $# in 69 | 0) exit 0 ;; 70 | esac 71 | 72 | # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and 73 | # mkdir -p a/c at the same time, both will detect that a is missing, 74 | # one will create a, then the other will try to create a and die with 75 | # a "File exists" error. This is a problem when calling mkinstalldirs 76 | # from a parallel make. We use --version in the probe to restrict 77 | # ourselves to GNU mkdir, which is thread-safe. 78 | case $dirmode in 79 | '') 80 | if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then 81 | echo "mkdir -p -- $*" 82 | exec mkdir -p -- "$@" 83 | else 84 | # On NextStep and OpenStep, the 'mkdir' command does not 85 | # recognize any option. It will interpret all options as 86 | # directories to create, and then abort because '.' already 87 | # exists. 88 | test -d ./-p && rmdir ./-p 89 | test -d ./--version && rmdir ./--version 90 | fi 91 | ;; 92 | *) 93 | if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && 94 | test ! -d ./--version; then 95 | echo "mkdir -m $dirmode -p -- $*" 96 | exec mkdir -m "$dirmode" -p -- "$@" 97 | else 98 | # Clean up after NextStep and OpenStep mkdir. 99 | for d in ./-m ./-p ./--version "./$dirmode"; 100 | do 101 | test -d $d && rmdir $d 102 | done 103 | fi 104 | ;; 105 | esac 106 | 107 | for file 108 | do 109 | case $file in 110 | /*) pathcomp=/ ;; 111 | *) pathcomp= ;; 112 | esac 113 | oIFS=$IFS 114 | IFS=/ 115 | set fnord $file 116 | shift 117 | IFS=$oIFS 118 | 119 | for d 120 | do 121 | test "x$d" = x && continue 122 | 123 | pathcomp=$pathcomp$d 124 | case $pathcomp in 125 | -*) pathcomp=./$pathcomp ;; 126 | esac 127 | 128 | if test ! -d "$pathcomp"; then 129 | echo "mkdir $pathcomp" 130 | 131 | mkdir "$pathcomp" || lasterr=$? 132 | 133 | if test ! -d "$pathcomp"; then 134 | errstatus=$lasterr 135 | else 136 | if test ! -z "$dirmode"; then 137 | echo "chmod $dirmode $pathcomp" 138 | lasterr= 139 | chmod "$dirmode" "$pathcomp" || lasterr=$? 140 | 141 | if test ! -z "$lasterr"; then 142 | errstatus=$lasterr 143 | fi 144 | fi 145 | fi 146 | fi 147 | 148 | pathcomp=$pathcomp/ 149 | done 150 | done 151 | 152 | exit $errstatus 153 | 154 | # Local Variables: 155 | # mode: shell-script 156 | # sh-indentation: 2 157 | # eval: (add-hook 'write-file-hooks 'time-stamp) 158 | # time-stamp-start: "scriptversion=" 159 | # time-stamp-format: "%:y-%02m-%02d.%02H" 160 | # time-stamp-time-zone: "UTC" 161 | # time-stamp-end: "; # UTC" 162 | # End: 163 | -------------------------------------------------------------------------------- /ui/NewPrinterName.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | Duplicate Printer 8 | True 9 | center-on-parent 10 | dialog 11 | 12 | 13 | True 14 | False 15 | vertical 16 | 6 17 | 18 | 19 | True 20 | False 21 | end 22 | 23 | 24 | _Cancel 25 | True 26 | True 27 | True 28 | False 29 | 30 | 31 | False 32 | False 33 | 0 34 | 35 | 36 | 37 | 38 | _OK 39 | True 40 | True 41 | True 42 | True 43 | True 44 | False 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | False 55 | True 56 | end 57 | 0 58 | 59 | 60 | 61 | 62 | True 63 | False 64 | New name for the printer 65 | 66 | 67 | False 68 | False 69 | 2 70 | 71 | 72 | 73 | 74 | True 75 | True 76 | True 77 | 78 | 79 | 80 | False 81 | False 82 | 3 83 | 84 | 85 | 86 | 87 | 88 | btnDuplicateCancel 89 | btnDuplicateOk 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /troubleshoot/Shrug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | from .base import * 25 | class Shrug(Question): 26 | def __init__ (self, troubleshooter): 27 | Question.__init__ (self, troubleshooter, "Shrug") 28 | page = self.initial_vbox (_("Sorry!"), 29 | _("There is no obvious solution to this " 30 | "problem. Your answers have been " 31 | "collected together with " 32 | "other useful information. If you " 33 | "would like to report a bug, please " 34 | "include this information.")) 35 | 36 | expander = Gtk.Expander.new(_("Diagnostic Output (Advanced)")) 37 | expander.set_expanded (False) 38 | sw = Gtk.ScrolledWindow () 39 | expander.add (sw) 40 | textview = Gtk.TextView () 41 | textview.set_editable (False) 42 | sw.add (textview) 43 | page.pack_start (expander, True, True, 0) 44 | self.buffer = textview.get_buffer () 45 | 46 | box = Gtk.HButtonBox () 47 | box.set_border_width (0) 48 | box.set_spacing (3) 49 | box.set_layout (Gtk.ButtonBoxStyle.END) 50 | page.pack_start (box, False, False, 0) 51 | 52 | self.save = Gtk.Button.new_from_stock (Gtk.STOCK_SAVE) 53 | box.pack_start (self.save, False, False, 0) 54 | 55 | troubleshooter.new_page (page, self) 56 | 57 | def display (self): 58 | self.buffer.set_text (self.troubleshooter.answers_as_text ()) 59 | return True 60 | 61 | def connect_signals (self, handler): 62 | self.save_sigid = self.save.connect ('clicked', self.on_save_clicked) 63 | 64 | def disconnect_signals (self): 65 | self.save.disconnect (self.save_sigid) 66 | 67 | def on_save_clicked (self, button): 68 | while True: 69 | parent = self.troubleshooter.get_window() 70 | dialog = Gtk.FileChooserDialog (transient_for=parent, 71 | action=Gtk.FileChooserAction.SAVE) 72 | dialog.add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 73 | Gtk.STOCK_SAVE, Gtk.ResponseType.OK) 74 | dialog.set_do_overwrite_confirmation (True) 75 | dialog.set_current_name ("troubleshoot.txt") 76 | dialog.set_default_response (Gtk.ResponseType.OK) 77 | dialog.set_local_only (True) 78 | response = dialog.run () 79 | dialog.hide () 80 | if response != Gtk.ResponseType.OK: 81 | return 82 | 83 | try: 84 | f = open (dialog.get_filename (), "w") 85 | f.write (self.buffer.get_text (start=self.buffer.get_start_iter (), 86 | end=self.buffer.get_end_iter (), 87 | include_hidden_chars=False)) 88 | except IOError as e: 89 | err = Gtk.MessageDialog (parent=parent, 90 | modal=True, destroy_with_parent=True, 91 | message_type=Gtk.MessageType.ERROR, 92 | buttons=Gtk.ButtonsType.CLOSE, 93 | text=_("Error saving file")) 94 | err.format_secondary_text (_("There was an error saving " 95 | "the file:") + "\n" + 96 | e.strerror) 97 | err.run () 98 | err.destroy () 99 | continue 100 | 101 | del f 102 | break 103 | -------------------------------------------------------------------------------- /troubleshoot/PrinterStateReasons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2011 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | 25 | import cups 26 | import ppdcache 27 | import statereason 28 | from timedops import TimedOperation 29 | from .base import * 30 | from functools import reduce 31 | class PrinterStateReasons(Question): 32 | def __init__ (self, troubleshooter): 33 | Question.__init__ (self, troubleshooter, "Printer state reasons") 34 | page = self.initial_vbox (_("Status Messages"), 35 | _("There are status messages associated with " 36 | "this queue.")) 37 | self.label = Gtk.Label () 38 | self.label.set_alignment (0, 0) 39 | self.label.set_line_wrap (True) 40 | page.pack_start (self.label, False, False, 0) 41 | 42 | troubleshooter.new_page (page, self) 43 | 44 | def display (self): 45 | troubleshooter = self.troubleshooter 46 | try: 47 | queue = troubleshooter.answers['cups_queue'] 48 | except KeyError: 49 | return False 50 | 51 | parent = self.troubleshooter.get_window () 52 | cups.setServer ('') 53 | self.op = TimedOperation (cups.Connection, parent=parent) 54 | c = self.op.run () 55 | self.op = TimedOperation (c.getPrinterAttributes, 56 | args=(queue,), 57 | parent=parent) 58 | dict = self.op.run () 59 | 60 | the_ppdcache = ppdcache.PPDCache () 61 | 62 | text = '' 63 | state_message = dict['printer-state-message'] 64 | if state_message: 65 | text += _("The printer's state message is: '%s'.") % state_message 66 | text += '\n\n' 67 | 68 | state_reasons_list = dict['printer-state-reasons'] 69 | if type (state_reasons_list) == str: 70 | state_reasons_list = [state_reasons_list] 71 | 72 | self.state_message = state_message 73 | self.state_reasons = state_reasons_list 74 | 75 | human_readable_errors = [] 76 | human_readable_warnings = [] 77 | for reason in state_reasons_list: 78 | if reason == "none": 79 | continue 80 | 81 | r = statereason.StateReason (queue, reason, the_ppdcache) 82 | (title, description) = r.get_description () 83 | level = r.get_level () 84 | if level == statereason.StateReason.ERROR: 85 | human_readable_errors.append (description) 86 | elif level == statereason.StateReason.WARNING: 87 | human_readable_warnings.append (description) 88 | 89 | if human_readable_errors: 90 | text += _("Errors are listed below:") + '\n' 91 | text += reduce (lambda x, y: x + "\n" + y, human_readable_errors) 92 | text += '\n\n' 93 | 94 | if human_readable_warnings: 95 | text += _("Warnings are listed below:") + '\n' 96 | text += reduce (lambda x, y: x + "\n" + y, human_readable_warnings) 97 | 98 | self.label.set_text (text) 99 | if (state_message == '' and 100 | len (human_readable_errors) == 0 and 101 | len (human_readable_warnings) == 0): 102 | return False 103 | 104 | # If this screen has been show before, don't show it again if 105 | # nothing changed. 106 | if 'printer-state-message' in troubleshooter.answers: 107 | if (troubleshooter.answers['printer-state-message'] == 108 | self.state_message and 109 | troubleshooter.answers['printer-state-reasons'] == 110 | self.state_reasons): 111 | return False 112 | 113 | return True 114 | 115 | def collect_answer (self): 116 | if not self.displayed: 117 | return {} 118 | 119 | return { 'printer-state-message': self.state_message, 120 | 'printer-state-reasons': self.state_reasons } 121 | 122 | def cancel_operation (self): 123 | self.op.cancel () 124 | -------------------------------------------------------------------------------- /man/system-config-printer.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | system-config-printer 7 | 8 | Tim 9 | Waugh 10 | 11 |
twaugh@redhat.com
12 |
13 |
14 |
15 | 16 | 17 | Man pages 18 | 19 | 20 | 21 | system-config-printer 22 | 25 Apr 2013 23 | 24 | 25 | 26 | system-config-printer 27 | 1 28 | 29 | 30 | 31 | system-config-printer 32 | configure a CUPS server 33 | 34 | 35 | 36 | 37 | system-config-printer 38 | 39 | --show-jobs printer 40 | --debug 41 | --help 42 | 43 | 44 | 45 | 46 | 47 | Description 48 | 49 | system-config-printer configures a 50 | CUPS server. It uses the CUPS API (bound to Python with 51 | pycups) to do this. The communication with the server is 52 | performed using IPP. As a result, it is equally able to 53 | configure a remote CUPS server as a local one. 54 | 55 | 56 | 57 | Options 58 | 59 | 60 | 61 | 62 | 63 | Display a short usage message. 64 | 65 | 66 | 67 | 68 | 69 | 70 | Show the named print queue. 71 | 72 | 73 | 74 | 75 | 76 | 77 | Enable debugging output. 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | system-config-printer 87 | 24 Nov 2010 88 | 89 | 90 | 91 | system-config-printer-applet 92 | 1 93 | 94 | 95 | 96 | system-config-printer-applet 97 | print job manager 98 | 99 | 100 | 101 | 102 | system-config-printer-applet 103 | --help 104 | --version 105 | --debug 106 | 107 | 108 | 109 | 110 | Description 111 | 112 | system-config-printer-applet is a 113 | print job manager for CUPS. Normally it will display a 114 | printer icon in the notification area, greyed out when there 115 | are no print jobs for the current user. Clicking on the icon 116 | displays a simple print job manager for cancelling or 117 | reprinting jobs. 118 | 119 | To save memory, the applet waits first of all until the 120 | user has printed a job before putting the icon in the 121 | notification area. To invoke the print job manager before a 122 | job has been printed, run the applet with the 123 | option: a desktop file is 124 | provided for this, so that it should appear in the system 125 | menu. 126 | 127 | As well as displaying a printer icon in the notification 128 | area, the applet also provides a D-BUS server for the 129 | com.redhat.PrintDriverSelection interface, to help configure a 130 | new printer when it is plugged in. 131 | 132 | 133 | 134 | Options 135 | 136 | 137 | 138 | 139 | 140 | Display a short usage message. 141 | 142 | 143 | 144 | 145 | 146 | 147 | Display the version of the applet. 148 | 149 | 150 | 151 | 152 | 153 | 154 | Show debugging information. 155 | 156 | 157 | 158 | 159 | 160 | 161 |
162 | -------------------------------------------------------------------------------- /ui/ConnectingDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 6 8 | Connecting to CUPS server 9 | False 10 | True 11 | center-on-parent 12 | dialog 13 | 14 | 15 | True 16 | False 17 | vertical 18 | 6 19 | 20 | 21 | True 22 | False 23 | end 24 | 25 | 26 | _Cancel 27 | True 28 | True 29 | True 30 | True 31 | False 32 | 33 | 34 | 35 | False 36 | False 37 | 0 38 | 39 | 40 | 41 | 42 | False 43 | True 44 | end 45 | 0 46 | 47 | 48 | 49 | 50 | True 51 | False 52 | 6 53 | vertical 54 | 6 55 | 56 | 57 | True 58 | False 59 | <span weight="bold" size="larger">Connecting to CUPS server</span> 60 | True 61 | 62 | 63 | False 64 | False 65 | 0 66 | 67 | 68 | 69 | 70 | True 71 | False 72 | 0.019999999553000001 73 | 74 | 75 | False 76 | False 77 | 1 78 | 79 | 80 | 81 | 82 | True 83 | False 84 | <i>Opening connection to %s</i> 85 | True 86 | 87 | 88 | False 89 | False 90 | 2 91 | 92 | 93 | 94 | 95 | False 96 | True 97 | 1 98 | 99 | 100 | 101 | 102 | 103 | button17 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /ui/JobsWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 7 | False 8 | Document Print Status 9 | 10 | 11 | 12 | True 13 | False 14 | vertical 15 | 16 | 17 | True 18 | False 19 | 20 | 21 | True 22 | False 23 | True 24 | Refresh job list 25 | _Refresh 26 | True 27 | view-refresh 28 | 29 | 30 | 31 | 32 | False 33 | True 34 | 35 | 36 | 37 | 38 | True 39 | False 40 | True 41 | Show completed jobs 42 | Show _completed jobs 43 | True 44 | gtk-ok 45 | 46 | 47 | 48 | False 49 | True 50 | 51 | 52 | 53 | 54 | True 55 | False 56 | 57 | 58 | False 59 | True 60 | 61 | 62 | 63 | 64 | False 65 | True 66 | 0 67 | 68 | 69 | 70 | 71 | True 72 | True 73 | never 74 | in 75 | 76 | 77 | True 78 | True 79 | True 80 | False 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | True 89 | True 90 | 1 91 | 92 | 93 | 94 | 95 | True 96 | False 97 | 98 | 99 | False 100 | False 101 | 2 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /troubleshoot/Locale.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2008, 2012 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | import locale 23 | 24 | from gi.repository import Gtk 25 | 26 | from .base import * 27 | 28 | class Locale(Question): 29 | def __init__ (self, troubleshooter): 30 | Question.__init__ (self, troubleshooter, "Locale issues") 31 | page = self.initial_vbox (_("Incorrect Page Size"), 32 | _("The page size for the print job was " 33 | "not the printer's default page size. " 34 | "If this is not intentional it may cause " 35 | "alignment problems.")) 36 | grid = Gtk.Grid() 37 | grid.set_row_spacing (6) 38 | grid.set_column_spacing (6) 39 | page.pack_start (grid, False, False, 0) 40 | self.printer_page_size = Gtk.Label () 41 | self.printer_page_size.set_alignment (0, 0) 42 | self.job_page_size = Gtk.Label () 43 | self.job_page_size.set_alignment (0, 0) 44 | label = Gtk.Label(label=_("Print job page size:")) 45 | label.set_alignment (0, 0) 46 | grid.attach (label, 0, 0, 1, 1) 47 | grid.attach (self.job_page_size, 1, 0, 1, 1) 48 | label = Gtk.Label(label=_("Printer page size:")) 49 | label.set_alignment (0, 0) 50 | grid.attach (label, 0, 1, 1, 1) 51 | grid.attach (self.printer_page_size, 1, 1, 1, 1) 52 | troubleshooter.new_page (page, self) 53 | 54 | def display (self): 55 | self.answers = {} 56 | (messages, encoding) = locale.getlocale (locale.LC_MESSAGES) 57 | (ctype, encoding) = locale.getlocale (locale.LC_CTYPE) 58 | self.answers['user_locale_messages'] = messages 59 | self.answers['user_locale_ctype'] = ctype 60 | 61 | try: 62 | system_lang = None 63 | conf = None 64 | for conffile in ["/etc/locale.conf", "/etc/sysconfig/i18n"]: 65 | try: 66 | conf = open (conffile).readlines () 67 | except IOError: 68 | continue 69 | 70 | if conf is not None: 71 | for line in conf: 72 | if line.startswith("LC_PAPER="): 73 | system_lang = line[9:].strip ('\n"') 74 | elif system_lang is None and line.startswith ("LANG="): 75 | system_lang = line[5:].strip ('\n"') 76 | 77 | if system_lang is not None: 78 | dot = system_lang.find ('.') 79 | if dot != -1: 80 | system_lang = system_lang[:dot] 81 | except: 82 | system_lang = None 83 | 84 | self.answers['system_locale_lang'] = system_lang 85 | 86 | printer_page_size = None 87 | try: 88 | ppd_defs = self.troubleshooter.answers['cups_printer_ppd_defaults'] 89 | for group, options in ppd_defs.items (): 90 | if "PageSize" in options: 91 | printer_page_size = options["PageSize"] 92 | break 93 | 94 | except KeyError: 95 | try: 96 | attrs = self.troubleshooter.answers['remote_cups_queue_attributes'] 97 | printer_page_size = attrs["media-default"] 98 | except KeyError: 99 | pass 100 | 101 | try: 102 | job_status = self.troubleshooter.answers["test_page_job_status"] 103 | except KeyError: 104 | job_status = [] 105 | 106 | self.answers['printer_page_size'] = printer_page_size 107 | if printer_page_size is not None: 108 | job_page_size = None 109 | for (test, jobid, printer, doc, status, attrs) in job_status: 110 | if test: 111 | if "PageSize" in attrs: 112 | job_page_size = attrs["PageSize"] 113 | self.answers['job_page_size'] = job_page_size 114 | if job_page_size != printer_page_size: 115 | self.printer_page_size.set_text (printer_page_size) 116 | self.job_page_size.set_text (job_page_size) 117 | return True 118 | 119 | return False 120 | 121 | def collect_answer (self): 122 | return self.answers 123 | 124 | -------------------------------------------------------------------------------- /dnssdresolve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import dbus, re 22 | import urllib.parse 23 | from debug import * 24 | 25 | class DNSSDHostNamesResolver: 26 | def __init__ (self, devices): 27 | self._devices = devices 28 | self._unresolved = len (devices) 29 | self._device_uri_by_name = {} 30 | debugprint ("+%s" % self) 31 | 32 | def __del__ (self): 33 | debugprint ("-%s" % self) 34 | 35 | def resolve (self, reply_handler): 36 | 37 | self._reply_handler = reply_handler 38 | 39 | bus = dbus.SystemBus () 40 | if not bus: 41 | reply_handler ([]) 42 | del self._devices 43 | del self._reply_handler 44 | return 45 | 46 | for uri, device in self._devices.items (): 47 | if not uri.startswith ("dnssd://"): 48 | self._unresolved -= 1 49 | continue 50 | 51 | # We need to resolve the DNS-SD hostname in order to 52 | # compare with other network devices. 53 | result = urllib.parse.urlparse (uri) 54 | hostname = result.netloc 55 | elements = hostname.rsplit (".", 3) 56 | if len (elements) != 4: 57 | self._resolved () 58 | continue 59 | 60 | name, stype, protocol, domain = elements 61 | name = urllib.parse.unquote (name) 62 | stype += "." + protocol # e.g. _printer._tcp 63 | 64 | try: 65 | obj = bus.get_object ("org.freedesktop.Avahi", "/") 66 | server = dbus.Interface (obj, 67 | "org.freedesktop.Avahi.Server") 68 | self._device_uri_by_name[(name, stype, domain)] = uri 69 | debugprint ("Resolving address for %s" % hostname) 70 | server.ResolveService (-1, -1, 71 | name, stype, domain, 72 | -1, 0, 73 | reply_handler=self._reply, 74 | error_handler=lambda e: 75 | self._error (uri, e)) 76 | except dbus.DBusException as e: 77 | debugprint ("Failed to resolve address: %s" % repr (e)) 78 | self._resolved () 79 | 80 | def _resolved (self): 81 | self._unresolved -= 1 82 | if self._unresolved == 0: 83 | debugprint ("All addresses resolved") 84 | self._reply_handler (self._devices) 85 | del self._devices 86 | del self._reply_handler 87 | 88 | def _reply (self, interface, protocol, name, stype, domain, 89 | host, aprotocol, address, port, txt, flags): 90 | uri = self._device_uri_by_name[(name, stype, domain)] 91 | self._devices[uri].address = address 92 | hostname = host 93 | p = hostname.find(".") 94 | if p != -1: 95 | hostname = hostname[:p] 96 | debugprint ("%s is at %s (%s)" % (uri, address, hostname)) 97 | self._devices[uri].hostname = hostname 98 | self._resolved () 99 | 100 | def _error (self, uri, error): 101 | debugprint ("Error resolving %s: %s" % (uri, repr (error))) 102 | self._resolved () 103 | 104 | if __name__ == '__main__': 105 | class Device: 106 | def __repr__ (self): 107 | try: 108 | return "" % self.address 109 | except: 110 | return "" 111 | 112 | devices = {"dnssd://dlk-08E206-P1._printer._tcp.local/": Device(), 113 | "dnssd://foo._printer._tcp.local/": Device()} 114 | from dbus.glib import DBusGMainLoop 115 | DBusGMainLoop (set_as_default=True) 116 | 117 | class Test: 118 | def __init__ (self, loop, devices): 119 | self._loop = loop 120 | self._devices = devices 121 | 122 | def run (self): 123 | r = DNSSDHostNamesResolver (self._devices) 124 | r.resolve (reply_handler=self.reply) 125 | return False 126 | 127 | def reply (self, *args): 128 | print(args) 129 | self._loop.quit () 130 | 131 | from gi.repository import GObject 132 | from gi.repository import GLib 133 | loop = GObject.MainLoop () 134 | set_debugging (True) 135 | GLib.idle_add (Test (loop, devices).run) 136 | loop.run () 137 | -------------------------------------------------------------------------------- /gtkinklevel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2009, 2010, 2012 Red Hat, Inc. 4 | ## Authors: 5 | ## Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | from gi.repository import Gdk 22 | from gi.repository import Gtk 23 | import cairo 24 | 25 | class GtkInkLevel (Gtk.DrawingArea): 26 | def __init__ (self, color, level=0): 27 | Gtk.DrawingArea.__init__ (self) 28 | self.connect ('draw', self.draw) 29 | self._level = level 30 | self._color = None 31 | if color: 32 | self._color = Gdk.color_parse (color) 33 | if not self._color: 34 | self._color = Gdk.color_parse ('#cccccc') 35 | 36 | self.set_size_request (30, 45) 37 | 38 | def set_level (self, level): 39 | self._level = level 40 | self.queue_resize () 41 | 42 | def get_level (self): 43 | return self._level 44 | 45 | def draw (self, widget, ctx): 46 | w = widget.get_allocated_width () 47 | h = widget.get_allocated_height () 48 | ratio = 1.0 * h / w 49 | if ratio < 1.5: 50 | w = h * 2.0 / 3.0 51 | else: 52 | h = w * 3.0 / 2.0 53 | thickness = 1 54 | ctx.translate (thickness, thickness) 55 | ctx.scale (w - 2 * thickness, h - 2 * thickness) 56 | thickness = max (ctx.device_to_user_distance (thickness, thickness)) 57 | 58 | r = self._color.red / 65535.0 59 | g = self._color.green / 65535.0 60 | b = self._color.blue / 65535.0 61 | fill_point = self._level / 100.0 62 | 63 | ctx.move_to (0.5, 0.0) 64 | ctx.curve_to (0.5, 0.33, 1.0, 0.5, 1.0, 0.67) 65 | ctx.curve_to (1.0, 0.85, 0.85, 1.0, 0.5, 1.0) 66 | ctx.curve_to (0.15, 1.0, 0.0, 0.85, 0.0, 0.67) 67 | ctx.curve_to (0.0, 0.5, 0.1, 0.2, 0.5, 0.0) 68 | ctx.close_path () 69 | ctx.set_source_rgb (r, g, b) 70 | ctx.set_line_width (thickness) 71 | ctx.stroke_preserve () 72 | if fill_point > 0.0: 73 | grad_width = 0.10 74 | grad_start = fill_point - (grad_width / 2) 75 | if grad_start < 0: 76 | grad_start = 0 77 | 78 | pat = cairo.LinearGradient (0, 1, 0, 0) 79 | pat.add_color_stop_rgba (0, r, g, b, 1) 80 | pat.add_color_stop_rgba ((self._level - 5) / 100.0, r, g, b, 1) 81 | pat.add_color_stop_rgba ((self._level + 5)/ 100.0, 1, 1, 1, 1) 82 | pat.add_color_stop_rgba (1.0, 1, 1, 1, 1) 83 | ctx.set_source (pat) 84 | ctx.fill () 85 | else: 86 | ctx.set_source_rgb (1, 1, 1) 87 | ctx.fill () 88 | 89 | ctx.set_line_width (thickness / 2) 90 | 91 | ctx.move_to (0.5, 0.0) 92 | ctx.line_to (0.5, 1.0) 93 | ctx.set_source_rgb (r, g, b) 94 | ctx.stroke () 95 | 96 | # 50% marker 97 | ctx.move_to (0.4, 0.5) 98 | ctx.line_to (0.6, 0.5) 99 | ctx.set_source_rgb (r, g, b) 100 | ctx.stroke () 101 | 102 | # 25% marker 103 | ctx.move_to (0.45, 0.75) 104 | ctx.line_to (0.55, 0.75) 105 | ctx.set_source_rgb (r, g, b) 106 | ctx.stroke () 107 | 108 | # 75% marker 109 | ctx.move_to (0.45, 0.25) 110 | ctx.line_to (0.55, 0.25) 111 | ctx.set_source_rgb (r, g, b) 112 | ctx.stroke () 113 | 114 | if __name__ == '__main__': 115 | # Try it out. 116 | from gi.repository import GLib 117 | import time 118 | def adjust_level (level): 119 | Gdk.threads_enter () 120 | l = level.get_level () 121 | l += 1 122 | if l > 100: 123 | l = 0 124 | level.set_level (l) 125 | Gdk.threads_leave () 126 | return True 127 | 128 | w = Gtk.Window () 129 | w.set_border_width (12) 130 | vbox = Gtk.VBox (spacing=6) 131 | w.add (vbox) 132 | hbox = Gtk.HBox (spacing=6) 133 | vbox.pack_start (hbox, False, False, 0) 134 | klevel = GtkInkLevel ("black", level=100) 135 | clevel = GtkInkLevel ("cyan", level=60) 136 | mlevel = GtkInkLevel ("magenta", level=30) 137 | ylevel = GtkInkLevel ("yellow", level=100) 138 | hbox.pack_start (klevel, False, False, 0) 139 | hbox.pack_start (clevel, False, False, 0) 140 | hbox.pack_start (mlevel, False, False, 0) 141 | hbox.pack_start (ylevel, False, False, 0) 142 | GLib.timeout_add (10, adjust_level, klevel) 143 | GLib.timeout_add (10, adjust_level, clevel) 144 | GLib.timeout_add (10, adjust_level, mlevel) 145 | GLib.timeout_add (10, adjust_level, ylevel) 146 | w.show_all () 147 | w.connect ('delete_event', Gtk.main_quit) 148 | Gdk.threads_init () 149 | Gtk.main () 150 | -------------------------------------------------------------------------------- /ui/SMBBrowseDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | SMB Browser 8 | True 9 | center-on-parent 10 | 11 | 12 | True 13 | False 14 | vertical 15 | 16 | 17 | 200 18 | True 19 | True 20 | 6 21 | in 22 | 23 | 24 | True 25 | True 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | True 37 | True 38 | 0 39 | 40 | 41 | 42 | 43 | True 44 | False 45 | 46 | 47 | False 48 | False 49 | 1 50 | 51 | 52 | 53 | 54 | True 55 | False 56 | end 57 | 6 58 | True 59 | 60 | 61 | _Refresh 62 | True 63 | True 64 | True 65 | True 66 | False 67 | 68 | 69 | 70 | False 71 | True 72 | 0 73 | 74 | 75 | 76 | 77 | _Cancel 78 | True 79 | True 80 | True 81 | True 82 | False 83 | 84 | 85 | 86 | False 87 | True 88 | 1 89 | 90 | 91 | 92 | 93 | _OK 94 | True 95 | True 96 | True 97 | True 98 | False 99 | 100 | 101 | 102 | False 103 | True 104 | 2 105 | 106 | 107 | 108 | 109 | False 110 | False 111 | 2 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /gitlog-to-changelog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Convert git log output to ChangeLog format. 3 | 4 | my $VERSION = '2009-06-04 08:53'; # UTC 5 | # The definition above must lie within the first 8 lines in order 6 | # for the Emacs time-stamp write hook (at end) to update it. 7 | # If you change this file with Emacs, please let the write hook 8 | # do its job. Otherwise, update this string manually. 9 | 10 | # Copyright (C) 2008, 2009 Free Software Foundation, Inc. 11 | 12 | # This program is free software: you can redistribute it and/or modify 13 | # it under the terms of the GNU General Public License as published by 14 | # the Free Software Foundation, either version 3 of the License, or 15 | # (at your option) any later version. 16 | 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | 25 | # Written by Jim Meyering 26 | 27 | use strict; 28 | use warnings; 29 | use Getopt::Long; 30 | use POSIX qw(strftime); 31 | 32 | (my $ME = $0) =~ s|.*/||; 33 | 34 | # use File::Coda; # http://meyering.net/code/Coda/ 35 | END { 36 | defined fileno STDOUT or return; 37 | close STDOUT and return; 38 | warn "$ME: failed to close standard output: $!\n"; 39 | $? ||= 1; 40 | } 41 | 42 | sub usage ($) 43 | { 44 | my ($exit_code) = @_; 45 | my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); 46 | if ($exit_code != 0) 47 | { 48 | print $STREAM "Try `$ME --help' for more information.\n"; 49 | } 50 | else 51 | { 52 | print $STREAM < ChangeLog 70 | $ME -- -n 5 foo > last-5-commits-to-branch-foo 71 | 72 | EOF 73 | } 74 | exit $exit_code; 75 | } 76 | 77 | # If the string $S is a well-behaved file name, simply return it. 78 | # If it contains white space, quotes, etc., quote it, and return the new string. 79 | sub shell_quote($) 80 | { 81 | my ($s) = @_; 82 | if ($s =~ m![^\w+/.,-]!) 83 | { 84 | # Convert each single quote to '\'' 85 | $s =~ s/\'/\'\\\'\'/g; 86 | # Then single quote the string. 87 | $s = "'$s'"; 88 | } 89 | return $s; 90 | } 91 | 92 | sub quoted_cmd(@) 93 | { 94 | return join (' ', map {shell_quote $_} @_); 95 | } 96 | 97 | { 98 | my $since_date = '1970-01-01 UTC'; 99 | GetOptions 100 | ( 101 | help => sub { usage 0 }, 102 | version => sub { print "$ME version $VERSION\n"; exit }, 103 | 'since=s' => \$since_date, 104 | ) or usage 1; 105 | 106 | my @cmd = (qw (git log --log-size), "--since=$since_date", 107 | '--pretty=format:%ct %an <%ae>%n%n%s%n%b%n', @ARGV); 108 | open PIPE, '-|', @cmd 109 | or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" 110 | . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); 111 | 112 | my $prev_date_line = ''; 113 | while (1) 114 | { 115 | defined (my $in = ) 116 | or last; 117 | $in =~ /^log size (\d+)$/ 118 | or die "$ME:$.: Invalid line (expected log size):\n$in"; 119 | my $log_nbytes = $1; 120 | 121 | my $log; 122 | my $n_read = read PIPE, $log, $log_nbytes; 123 | $n_read == $log_nbytes 124 | or die "$ME:$.: unexpected EOF\n"; 125 | 126 | my @line = split "\n", $log; 127 | my $author_line = shift @line; 128 | defined $author_line 129 | or die "$ME:$.: unexpected EOF\n"; 130 | $author_line =~ /^(\d+) (.*>)$/ 131 | or die "$ME:$.: Invalid line " 132 | . "(expected date/author/email):\n$author_line\n"; 133 | 134 | my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); 135 | # If this line would be the same as the previous date/name/email 136 | # line, then arrange not to print it. 137 | if ($date_line ne $prev_date_line) 138 | { 139 | $prev_date_line eq '' 140 | or print "\n"; 141 | print $date_line; 142 | } 143 | $prev_date_line = $date_line; 144 | 145 | # Omit "Signed-off-by..." lines. 146 | @line = grep !/^Signed-off-by: .*>$/, @line; 147 | 148 | # If there were any lines 149 | if (@line == 0) 150 | { 151 | warn "$ME: warning: empty commit message:\n $date_line\n"; 152 | } 153 | else 154 | { 155 | # Remove leading and trailing blank lines. 156 | while ($line[0] =~ /^\s*$/) { shift @line; } 157 | while ($line[$#line] =~ /^\s*$/) { pop @line; } 158 | 159 | # Prefix each non-empty line with a TAB. 160 | @line = map { length $_ ? "\t$_" : '' } @line; 161 | 162 | print "\n", join ("\n", @line), "\n"; 163 | } 164 | 165 | defined ($in = ) 166 | or last; 167 | $in ne "\n" 168 | and die "$ME:$.: unexpected line:\n$in"; 169 | } 170 | 171 | close PIPE 172 | or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; 173 | # FIXME-someday: include $PROCESS_STATUS in the diagnostic 174 | } 175 | 176 | # Local Variables: 177 | # indent-tabs-mode: nil 178 | # eval: (add-hook 'write-file-hooks 'time-stamp) 179 | # time-stamp-start: "my $VERSION = '" 180 | # time-stamp-format: "%:y-%02m-%02d %02H:%02M" 181 | # time-stamp-time-zone: "UTC" 182 | # time-stamp-end: "'; # UTC" 183 | # End: 184 | -------------------------------------------------------------------------------- /test/test-cups-driver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- python -*- 3 | 4 | ## Copyright (C) 2008, 2014 Red Hat, Inc. 5 | ## Copyright (C) 2008 Tim Waugh 6 | 7 | ## This program is free software; you can redistribute it and/or modify 8 | ## it under the terms of the GNU General Public License as published by 9 | ## the Free Software Foundation; either version 2 of the License, or 10 | ## (at your option) any later version. 11 | 12 | ## This program is distributed in the hope that it will be useful, 13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ## GNU General Public License for more details. 16 | 17 | ## You should have received a copy of the GNU General Public License 18 | ## along with this program; if not, write to the Free Software 19 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | import sys 22 | 23 | import cups 24 | try: 25 | from cupshelpers import missingPackagesAndExecutables 26 | except ImportError: 27 | sys.path.append ('..') 28 | from cupshelpers import missingPackagesAndExecutables 29 | 30 | from getopt import getopt 31 | import os 32 | import posix 33 | import re 34 | import shlex 35 | import signal 36 | import subprocess 37 | import tempfile 38 | 39 | class TimedOut(Exception): 40 | def __init__ (self): 41 | Exception.__init__ (self, "Timed out") 42 | 43 | class MissingExecutables(Exception): 44 | def __init__ (self): 45 | Exception.__init__ (self, "Missing executables") 46 | 47 | class Driver: 48 | def __init__ (self, driver): 49 | self.exe = "/usr/lib/cups/driver/%s" % driver 50 | self.ppds = None 51 | self.files = {} 52 | signal.signal (signal.SIGALRM, self._alarm) 53 | 54 | def _alarm (self, sig, stack): 55 | raise TimedOut 56 | 57 | def list (self): 58 | if self.ppds: 59 | return self.ppds 60 | 61 | signal.alarm (60) 62 | p = subprocess.Popen ([self.exe, "list"], 63 | stdout=subprocess.PIPE, 64 | stderr=subprocess.PIPE) 65 | try: 66 | (stdout, stderr) = p.communicate () 67 | signal.alarm (0) 68 | except TimedOut: 69 | posix.kill (p.pid, signal.SIGKILL) 70 | raise 71 | 72 | if stderr: 73 | print(stderr.decode (), file=sys.stderr) 74 | 75 | ppds = [] 76 | lines = stdout.decode ().split ('\n') 77 | for line in lines: 78 | l = shlex.split (line) 79 | if len (l) < 1: 80 | continue 81 | ppds.append (l[0]) 82 | 83 | self.ppds = ppds 84 | return ppds 85 | 86 | def cat (self, name): 87 | try: 88 | return self.files[name] 89 | except KeyError: 90 | signal.alarm (10) 91 | p = subprocess.Popen ([self.exe, "cat", name], 92 | stdout=subprocess.PIPE, 93 | stderr=subprocess.PIPE) 94 | try: 95 | (stdout, stderr) = p.communicate () 96 | signal.alarm (0) 97 | except TimedOut: 98 | posix.kill (p.pid, signal.SIGKILL) 99 | raise 100 | 101 | if stderr: 102 | print(stderr.decode (), file=sys.stderr) 103 | 104 | self.files[name] = stdout.decode () 105 | return self.files[name] 106 | 107 | opts, args = getopt (sys.argv[1:], "m:") 108 | if len (args) != 1: 109 | print ("Syntax: test-cups-driver [-m REGEXP] DRIVER") 110 | sys.exit (1) 111 | 112 | match = None 113 | for opt, arg in opts: 114 | if opt == '-m': 115 | match = arg 116 | break 117 | 118 | bad = [] 119 | ids = set() 120 | d = Driver (args[0]) 121 | list = d.list () 122 | 123 | if match: 124 | exp = re.compile (match) 125 | list = [x for x in list if exp.match (x)] 126 | 127 | n = len (list) 128 | i = 0 129 | for name in list: 130 | i += 1 131 | try: 132 | ppd = d.cat (name) 133 | (fd, fname) = tempfile.mkstemp () 134 | f = os.fdopen (fd, "w") 135 | f.write (ppd) 136 | del f 137 | try: 138 | PPD = cups.PPD (fname) 139 | except: 140 | os.unlink (fname) 141 | raise 142 | os.unlink (fname) 143 | 144 | (pkgs, exes) = missingPackagesAndExecutables (PPD) 145 | if pkgs or exes: 146 | raise MissingExecutables 147 | 148 | attr = PPD.findAttr ('1284DeviceID') 149 | if attr: 150 | pieces = attr.value.split (';') 151 | mfg = mdl = None 152 | for piece in pieces: 153 | s = piece.split (':', 1) 154 | if len (s) < 2: 155 | continue 156 | key, value = s 157 | key = key.upper () 158 | if key in ["MFG", "MANUFACTURER"]: 159 | mfg = value 160 | elif key in ["MDL", "MODEL"]: 161 | mdl = value 162 | if mfg and mdl: 163 | id = "MFG:%s;MDL:%s;" % (mfg, mdl) 164 | ids.add (id) 165 | sys.stderr.write ("%3d%%\r" % (100 * i / n)) 166 | sys.stderr.flush () 167 | except KeyboardInterrupt: 168 | print ("Keyboard interrupt\n") 169 | break 170 | except TimedOut as e: 171 | bad.append ((name, e)) 172 | print ("Timed out fetching %s" % name) 173 | except Exception as e: 174 | bad.append ((name, e)) 175 | print ("Exception fetching %s: %s" % (name, e)) 176 | 177 | sys.stdout.flush () 178 | 179 | if len (bad) > 0: 180 | print ("Bad PPDs:") 181 | for each in bad: 182 | print (" %s (%s)" % each) 183 | print 184 | 185 | if len (ids) > 0: 186 | print ("IEEE 1284 Device IDs:") 187 | for each in ids: 188 | print (" %s" % each) 189 | print 190 | -------------------------------------------------------------------------------- /install-printerdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import gi 4 | gi.require_version('PackageKitGlib', '1.0') 5 | from gi.repository import GLib, PackageKitGlib 6 | import sys 7 | from debug import * 8 | 9 | # progress callback 10 | # http://www.packagekit.org/gtk-doc/PkProgress.html 11 | def progress(progress, type, user_data): 12 | if (type.value_name == "PK_PROGRESS_TYPE_PERCENTAGE" and 13 | progress.props.package is not None): 14 | sys.stdout.write ("P%d\n" % progress.props.percentage) 15 | sys.stdout.flush () 16 | else: 17 | sys.stdout.write ("P%d\n" % -10) 18 | sys.stdout.flush () 19 | 20 | set_debugging (True) 21 | 22 | try: 23 | package = sys.argv[1] 24 | except: 25 | debugprint("Missing package name to install.") 26 | sys.exit(1) 27 | 28 | try: 29 | repo = sys.argv[2] 30 | except: 31 | debugprint("Missing name of repo.") 32 | sys.exit(1) 33 | 34 | try: 35 | repo_gpg_id = sys.argv[3] 36 | except: 37 | repo_gpg_id = None 38 | 39 | # get PackageKit client 40 | pk = PackageKitGlib.Client() 41 | 42 | refresh_cache_needed = False 43 | 44 | # install repository key 45 | if repo_gpg_id: 46 | debugprint("Signature key supplied") 47 | debugprint("pk.install_signature") 48 | try: 49 | res = pk.install_signature(PackageKitGlib.SigTypeEnum.GPG, repo_gpg_id, 50 | '', None, progress, None) 51 | refresh_cache_needed = True 52 | debugprint("pk.install_signature succeeded") 53 | except GLib.GError: 54 | debugprint("pk.install_signature failed") 55 | sys.exit(1) 56 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 57 | debugprint("pk.install_signature errored") 58 | sys.exit(1) 59 | 60 | # check if we already have the package installed or available 61 | debugprint("pk.resolve") 62 | try: 63 | res = pk.resolve(PackageKitGlib.FilterEnum.NONE, [package], 64 | None, progress, None) 65 | repo_enable_needed = False 66 | debugprint("pk.resolve succeeded") 67 | except GLib.GError: 68 | repo_enable_needed = True 69 | debugprint("pk.resolve failed") 70 | package_ids = res.get_package_array() 71 | if len(package_ids) <= 0: 72 | debugprint("res.get_package_array() failed") 73 | repo_enable_needed = True 74 | 75 | if repo_enable_needed: 76 | # Cannot resolve, so we need to install the repo 77 | # add repository; see 78 | # http://www.packagekit.org/gtk-doc/PackageKit-pk-client-sync.html#pk-client-repo-enable 79 | debugprint("pk.repo_enable") 80 | try: 81 | res = pk.repo_enable(repo, True, None, progress, None) 82 | refresh_cache_needed = True 83 | debugprint("pk.repo_enable succeeded") 84 | except GLib.GError: 85 | debugprint("pk.repo_enable failed") 86 | sys.exit(1) 87 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 88 | debugprint("pk.repo_enable errored") 89 | sys.exit(1) 90 | 91 | if refresh_cache_needed: 92 | # download/update the indexes 93 | debugprint("pk.refresh_cache") 94 | try: 95 | res = pk.refresh_cache(False, None, progress, None) 96 | debugprint("pk.refresh_cache succeeded") 97 | except GLib.GError: 98 | debugprint("pk.refresh_cache failed") 99 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 100 | debugprint("pk.refresh_cache errored") 101 | 102 | # map package name to PackageKit ID; do not print progress here, it's fast 103 | debugprint("pk.resolve") 104 | try: 105 | res = pk.resolve(PackageKitGlib.FilterEnum.NONE, [package], 106 | None, progress, None) 107 | debugprint("pk.resolve succeeded") 108 | except GLib.GError: 109 | debugprint("pk.resolve failed") 110 | sys.exit(1) 111 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 112 | debugprint("pk.resolve errored") 113 | sys.exit(1) 114 | package_ids = res.get_package_array() 115 | if len(package_ids) <= 0: 116 | debugprint("res.get_package_array() failed") 117 | sys.exit(1) 118 | package_id = package_ids[0].get_id() 119 | debugprint("package_id: %s" % package_id) 120 | 121 | # install the first match, unless already installed 122 | if package_ids[0].get_info() & PackageKitGlib.InfoEnum.INSTALLED == 0: 123 | debugprint("package not installed") 124 | debugprint("pk.install_packages") 125 | # install package 126 | if repo_gpg_id: 127 | debugprint("Signature key supplied") 128 | repo_gpg_id_supplied = True 129 | else: 130 | debugprint("Signature key not supplied") 131 | repo_gpg_id_supplied = False 132 | try: 133 | res = pk.install_packages(repo_gpg_id_supplied, [package_id], None, 134 | progress, None) 135 | debugprint("pk.install_packages succeeded") 136 | except GLib.GError: 137 | debugprint("pk.install_packages failed, retrying with modified package ID") 138 | # See aptdaemon Ubuntu bug #1397750. 139 | try: 140 | # Remove last element of the package ID, after the last ";" 141 | package_id_mod = package_id[:package_id.rfind(";")+1] 142 | res = pk.install_packages(repo_gpg_id_supplied, [package_id_mod], 143 | None, progress, None) 144 | debugprint("pk.install_packages succeeded") 145 | except GLib.GError: 146 | debugprint("pk.install_packages failed") 147 | sys.exit(1) 148 | if res.get_exit_code() != PackageKitGlib.ExitEnum.SUCCESS: 149 | debugprint("pk.install_packages errored") 150 | sys.exit(1) 151 | 152 | debugprint("Package successfully installed") 153 | # If we reach this point, the requested package is on the system, either 154 | # because we have installed it now or because it was already there 155 | 156 | # Return the list of files contained in the package 157 | try: 158 | res = pk.get_files([package_id], None, progress, None) 159 | except GLib.GError: 160 | pass 161 | 162 | files = res.get_files_array() 163 | if files: 164 | for f in files[0].get_property('files'): 165 | print(f) 166 | 167 | # Tell the caller that we are done 168 | print("done") 169 | -------------------------------------------------------------------------------- /troubleshoot/CheckPrinterSanity.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2012, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | from gi.repository import Gtk 24 | 25 | import cups 26 | import os 27 | import smburi 28 | import subprocess 29 | from timedops import TimedOperation, TimedSubprocess 30 | import urllib.parse 31 | from .base import * 32 | class CheckPrinterSanity(Question): 33 | def __init__ (self, troubleshooter): 34 | Question.__init__ (self, troubleshooter, "Check printer sanity") 35 | troubleshooter.new_page (Gtk.Label (), self) 36 | self.troubleshooter = troubleshooter 37 | 38 | def display (self): 39 | # Collect information useful for the various checks. 40 | 41 | self.answers = {} 42 | 43 | answers = self.troubleshooter.answers 44 | if not answers['cups_queue_listed']: 45 | return False 46 | 47 | name = answers['cups_queue'] 48 | 49 | parent = self.troubleshooter.get_window () 50 | 51 | # Find out if this is a printer or a class. 52 | try: 53 | cups.setServer ('') 54 | c = TimedOperation (cups.Connection, parent=parent).run () 55 | printers = TimedOperation (c.getPrinters, parent=parent).run () 56 | if name in printers: 57 | self.answers['is_cups_class'] = False 58 | queue = printers[name] 59 | self.answers['cups_printer_dict'] = queue 60 | else: 61 | self.answers['is_cups_class'] = True 62 | classes = TimedOperation (c.getClasses, parent=parent).run () 63 | queue = classes[name] 64 | self.answers['cups_class_dict'] = queue 65 | 66 | attrs = TimedOperation (c.getPrinterAttributes, (name,), 67 | parent=parent).run () 68 | self.answers['local_cups_queue_attributes'] = attrs 69 | except: 70 | pass 71 | 72 | if 'cups_printer_dict' in self.answers: 73 | cups_printer_dict = self.answers['cups_printer_dict'] 74 | uri = cups_printer_dict['device-uri'] 75 | (scheme, rest) = urllib.parse.splittype (uri) 76 | self.answers['cups_device_uri_scheme'] = scheme 77 | if scheme in ["ipp", "http", "https"]: 78 | (hostport, rest) = urllib.parse.splithost (rest) 79 | (host, port) = urllib.parse.splitnport (hostport, defport=631) 80 | self.answers['remote_server_name'] = host 81 | self.answers['remote_server_port'] = port 82 | elif scheme == "smb": 83 | u = smburi.SMBURI (uri) 84 | (group, host, share, user, password) = u.separate () 85 | new_environ = os.environ.copy() 86 | new_environ['LC_ALL'] = "C" 87 | if group: 88 | args = ["nmblookup", "-W", group, host] 89 | else: 90 | args = ["nmblookup", host] 91 | try: 92 | p = TimedSubprocess (parent=parent, 93 | timeout=5000, 94 | args=args, 95 | env=new_environ, 96 | close_fds=True, 97 | stdin=subprocess.DEVNULL, 98 | stdout=subprocess.PIPE, 99 | stderr=subprocess.PIPE) 100 | result = p.run () 101 | self.answers['nmblookup_output'] = result 102 | for line in result[0]: 103 | if line.startswith ("querying"): 104 | continue 105 | spc = line.find (' ') 106 | if (spc != -1 and 107 | not line[spc:].startswith (" failed ")): 108 | # Remember the IP address. 109 | self.answers['remote_server_name'] = line[:spc] 110 | break 111 | except OSError: 112 | # Problem executing command. 113 | pass 114 | elif scheme == "hp": 115 | new_environ = os.environ.copy() 116 | new_environ['LC_ALL'] = "C" 117 | new_environ['DISPLAY'] = "" 118 | try: 119 | p = TimedSubprocess (parent=parent, 120 | timeout=3000, 121 | args=["hp-info", "-d" + uri], 122 | close_fds=True, 123 | env=new_environ, 124 | stdin=subprocess.DEVNULL, 125 | stdout=subprocess.PIPE, 126 | stderr=subprocess.PIPE) 127 | self.answers['hplip_output'] = p.run () 128 | except OSError: 129 | # Problem executing command. 130 | pass 131 | 132 | r = cups_printer_dict['printer-type'] & cups.CUPS_PRINTER_REMOTE 133 | self.answers['cups_printer_remote'] = (r != 0) 134 | return False 135 | 136 | def collect_answer (self): 137 | return self.answers 138 | -------------------------------------------------------------------------------- /troubleshoot/ChoosePrinter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2012 Red Hat, Inc. 6 | ## Copyright (C) 2008 Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | class NoPrinter: 25 | pass 26 | 27 | NotListed = NoPrinter() 28 | 29 | import cups 30 | from gi.repository import GObject 31 | from timedops import TimedOperation 32 | from .base import * 33 | class ChoosePrinter(Question): 34 | def __init__ (self, troubleshooter): 35 | # First question: which printer? (page 1) 36 | Question.__init__ (self, troubleshooter, "Choose printer") 37 | page1 = self.initial_vbox (_("Choose Printer"), 38 | _("Please select the printer you are " 39 | "trying to use from the list below. " 40 | "If it does not appear in the list, " 41 | "select 'Not listed'.")) 42 | tv = Gtk.TreeView () 43 | name = Gtk.TreeViewColumn (_("Name"), 44 | Gtk.CellRendererText (), text=0) 45 | location = Gtk.TreeViewColumn (_("Location"), 46 | Gtk.CellRendererText (), text=1) 47 | info = Gtk.TreeViewColumn (_("Information"), 48 | Gtk.CellRendererText (), text=2) 49 | name.set_property ("resizable", True) 50 | location.set_property ("resizable", True) 51 | info.set_property ("resizable", True) 52 | tv.append_column (name) 53 | tv.append_column (location) 54 | tv.append_column (info) 55 | tv.set_rules_hint (True) 56 | sw = Gtk.ScrolledWindow () 57 | sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) 58 | sw.set_shadow_type (Gtk.ShadowType.IN) 59 | sw.add (tv) 60 | page1.pack_start (sw, True, True, 0) 61 | self.treeview = tv 62 | troubleshooter.new_page (page1, self) 63 | 64 | def display (self): 65 | model = Gtk.ListStore (str, 66 | str, 67 | str, 68 | GObject.TYPE_PYOBJECT) 69 | self.treeview.set_model (model) 70 | iter = model.append (None) 71 | model.set (iter, 0, _("Not listed"), 1, '', 2, '', 3, NotListed) 72 | 73 | parent = self.troubleshooter.get_window () 74 | try: 75 | cups.setServer ('') 76 | c = self.timedop (cups.Connection, parent=parent).run () 77 | dests = self.timedop (c.getDests, parent=parent).run () 78 | printers = None 79 | dests_list = [] 80 | for (name, instance), dest in dests.items (): 81 | if name is None: 82 | continue 83 | 84 | if instance is not None: 85 | queue = "%s/%s" % (name, instance) 86 | else: 87 | queue = name 88 | 89 | if printers is None: 90 | printers = self.timedop (c.getPrinters, 91 | parent=parent).run () 92 | 93 | if name not in printers: 94 | info = _("Unknown") 95 | location = _("Unknown") 96 | else: 97 | printer = printers[name] 98 | info = printer.get('printer-info', _("Unknown")) 99 | location = printer.get('printer-location', _("Unknown")) 100 | 101 | dests_list.append ((queue, location, info, dest)) 102 | 103 | dests_list.sort (key=lambda x: x[0]) 104 | for queue, location, info, dest in dests_list: 105 | iter = model.append (None) 106 | model.set (iter, 0, queue, 1, location, 2, info, 3, dest) 107 | 108 | except cups.HTTPError: 109 | pass 110 | except cups.IPPError: 111 | pass 112 | except RuntimeError: 113 | pass 114 | 115 | return True 116 | 117 | def connect_signals (self, handler): 118 | self.signal_id = self.treeview.connect ("cursor-changed", handler) 119 | 120 | def disconnect_signals (self): 121 | self.treeview.disconnect (self.signal_id) 122 | 123 | def can_click_forward (self): 124 | model, iter = self.treeview.get_selection ().get_selected () 125 | if iter is None: 126 | return False 127 | return True 128 | 129 | def collect_answer (self): 130 | model, iter = self.treeview.get_selection ().get_selected () 131 | dest = model.get_value (iter, 3) 132 | if dest == NotListed: 133 | class enum_dests: 134 | def __init__ (self, model): 135 | self.dests = [] 136 | model.foreach (self.each, None) 137 | 138 | def each (self, model, path, iter, user_data): 139 | dest = model.get_value (iter, 3) 140 | if dest != NotListed: 141 | self.dests.append ((dest.name, 142 | dest.instance)) 143 | 144 | return { 'cups_queue_listed': False, 145 | 'cups_dests_available': enum_dests (model).dests } 146 | else: 147 | return { 'cups_queue_listed': True, 148 | 'cups_dest': dest, 149 | 'cups_queue': dest.name, 150 | 'cups_instance': dest.instance } 151 | 152 | def cancel_operation (self): 153 | self.op.cancel () 154 | 155 | def timedop (self, *args, **kwargs): 156 | self.op = TimedOperation (*args, **kwargs) 157 | return self.op 158 | -------------------------------------------------------------------------------- /ui/InstallDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | False 8 | True 9 | center-on-parent 10 | dialog 11 | 12 | 13 | True 14 | False 15 | vertical 16 | 12 17 | 18 | 19 | True 20 | False 21 | end 22 | 23 | 24 | Close 25 | True 26 | True 27 | True 28 | True 29 | False 30 | 31 | 32 | False 33 | False 34 | 0 35 | 36 | 37 | 38 | 39 | True 40 | True 41 | True 42 | False 43 | 44 | 45 | True 46 | False 47 | 2 48 | 49 | 50 | True 51 | False 52 | gtk-yes 53 | 54 | 55 | False 56 | False 57 | 0 58 | 59 | 60 | 61 | 62 | True 63 | False 64 | _Install 65 | True 66 | 67 | 68 | False 69 | False 70 | 1 71 | 72 | 73 | 74 | 75 | 76 | 77 | False 78 | False 79 | 1 80 | 81 | 82 | 83 | 84 | False 85 | True 86 | end 87 | 0 88 | 89 | 90 | 91 | 92 | True 93 | False 94 | 6 95 | 12 96 | 97 | 98 | True 99 | False 100 | dialog-information 101 | 6 102 | 103 | 104 | True 105 | True 106 | 0 107 | 108 | 109 | 110 | 111 | True 112 | False 113 | label181 114 | True 115 | True 116 | 117 | 118 | False 119 | False 120 | 1 121 | 122 | 123 | 124 | 125 | False 126 | True 127 | 1 128 | 129 | 130 | 131 | 132 | 133 | button18 134 | button19 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /troubleshoot/CheckUSBPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2010, 2014 Red Hat, Inc. 6 | ## Authors: 7 | ## Tim Waugh 8 | 9 | ## This program is free software; you can redistribute it and/or modify 10 | ## it under the terms of the GNU General Public License as published by 11 | ## the Free Software Foundation; either version 2 of the License, or 12 | ## (at your option) any later version. 13 | 14 | ## This program is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU General Public License for more details. 18 | 19 | ## You should have received a copy of the GNU General Public License 20 | ## along with this program; if not, write to the Free Software 21 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import glob 24 | import os 25 | import subprocess 26 | from timedops import TimedSubprocess 27 | import urllib.parse 28 | from .base import * 29 | from gi.repository import Gtk 30 | 31 | class CheckUSBPermissions(Question): 32 | def __init__ (self, troubleshooter): 33 | Question.__init__ (self, troubleshooter, "Check USB permissions") 34 | troubleshooter.new_page (Gtk.Label (), self) 35 | 36 | def display (self): 37 | self.answers = {} 38 | answers = self.troubleshooter.answers 39 | if answers['cups_queue_listed']: 40 | if answers['is_cups_class']: 41 | return False 42 | 43 | cups_printer_dict = answers['cups_printer_dict'] 44 | device_uri = cups_printer_dict['device-uri'] 45 | elif answers.get ('cups_device_listed', False): 46 | device_uri = answers['cups_device_uri'] 47 | else: 48 | return False 49 | 50 | (scheme, rest) = urllib.parse.splittype (device_uri) 51 | if scheme not in ['hp', 'hpfax', 'usb', 'hal']: 52 | return False 53 | 54 | LSUSB = "/sbin/lsusb" 55 | if not os.access (LSUSB, os.X_OK): 56 | return False 57 | 58 | GETFACL = "/usr/bin/getfacl" 59 | if not os.access (GETFACL, os.X_OK): 60 | return False 61 | 62 | new_environ = os.environ.copy() 63 | new_environ['LC_ALL'] = "C" 64 | 65 | # Run lsusb 66 | parent = self.troubleshooter.get_window () 67 | try: 68 | self.op = TimedSubprocess (parent=parent, 69 | args=[LSUSB, "-v"], 70 | close_fds=True, 71 | env=new_environ, 72 | stdin=subprocess.DEVNULL, 73 | stdout=subprocess.PIPE, 74 | stderr=subprocess.PIPE) 75 | (lsusb_stdout, lsusb_stderr, result) = self.op.run () 76 | except: 77 | # Problem executing command. 78 | return False 79 | 80 | # Now parse it. 81 | dev_by_id = {} 82 | this_dev = None 83 | for line in lsusb_stdout: 84 | if (this_dev is not None and 85 | ((line.find ("bInterfaceClass") != -1 and 86 | line.find ("7 Printer") != -1) or 87 | (line.find ("bInterfaceSubClass") != -1 and 88 | line.find ("1 Printer") != -1))): 89 | mfr = dev_by_id.get (this_mfr_id, {}) 90 | mdl = mfr.get (this_mdl_id, []) 91 | mdl.append (this_dev) 92 | mfr[this_mdl_id] = mdl 93 | dev_by_id[this_mfr_id] = mfr 94 | this_dev = None 95 | continue 96 | 97 | separators = [ ('Bus ', 3), 98 | (' Device ', 3), 99 | (': ID ', 4), 100 | (':', 4), 101 | (' ', -1)] 102 | fields = [] 103 | i = 0 104 | p = line 105 | while i < len (separators): 106 | (sep, length) = separators[i] 107 | if not p.startswith (sep): 108 | break 109 | start = len (sep) 110 | if length == -1: 111 | end = len (p) 112 | fields.append (p[start:]) 113 | else: 114 | end = start + length 115 | fields.append (p[start:end]) 116 | 117 | p = p[end:] 118 | i += 1 119 | 120 | if i < len (separators): 121 | continue 122 | 123 | if not scheme.startswith ('hp') and fields[2] != '03f0': 124 | # Skip non-HP printers if we know we're using HPLIP. 125 | continue 126 | 127 | this_dev = { 'bus': fields[0], 128 | 'dev': fields[1], 129 | 'name': fields[4], 130 | 'full': line } 131 | this_mfr_id = fields[2] 132 | this_mdl_id = fields[3] 133 | 134 | infos = {} 135 | paths = [] 136 | if not scheme.startswith ('hp'): 137 | paths.extend (glob.glob ("/dev/usb/lp?")) 138 | for mfr_id, mdls in dev_by_id.items (): 139 | for mdl_id, devs in mdls.items (): 140 | for dev in devs: 141 | path = "/dev/bus/usb/%s/%s" % (dev['bus'], dev['dev']) 142 | paths.append (path) 143 | infos[path] = dev['full'] 144 | 145 | perms = [] 146 | for path in paths: 147 | try: 148 | self.op = TimedSubprocess (parent=parent, 149 | args=[GETFACL, path], 150 | close_fds=True, 151 | env=new_environ, 152 | stdin=subprocess.DEVNULL, 153 | stdout=subprocess.PIPE, 154 | stderr=subprocess.PIPE) 155 | (getfacl_stdout, getfacl_stderr, result) = self.op.run () 156 | output = [x for x in getfacl_stdout if len (x) > 0] 157 | except: 158 | # Problem executing command. 159 | output = [] 160 | 161 | info = infos.get (path, path) 162 | perms.append ((info, output)) 163 | 164 | self.answers['getfacl_output'] = perms 165 | 166 | # Don't actually display anything, just collect information. 167 | return False 168 | 169 | def collect_answer (self): 170 | return self.answers 171 | 172 | def cancel_operation (self): 173 | self.op.cancel () 174 | -------------------------------------------------------------------------------- /troubleshoot/ChooseNetworkPrinter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Printing troubleshooter 4 | 5 | ## Copyright (C) 2008, 2009, 2011, 2012 Red Hat, Inc. 6 | ## Author: Tim Waugh 7 | 8 | ## This program is free software; you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation; either version 2 of the License, or 11 | ## (at your option) any later version. 12 | 13 | ## This program is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with this program; if not, write to the Free Software 20 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | from gi.repository import Gtk 23 | 24 | import cups 25 | from gi.repository import GObject 26 | from timedops import TimedOperation 27 | from .base import * 28 | class ChooseNetworkPrinter(Question): 29 | def __init__ (self, troubleshooter): 30 | Question.__init__ (self, troubleshooter, "Choose network printer") 31 | page1 = self.initial_vbox (_("Choose Network Printer"), 32 | _("Please select the network printer you " 33 | "are trying to use from the list below. " 34 | "If it does not appear in the list, " 35 | "select 'Not listed'.")) 36 | tv = Gtk.TreeView () 37 | name = Gtk.TreeViewColumn (_("Name"), 38 | Gtk.CellRendererText (), text=0) 39 | location = Gtk.TreeViewColumn (_("Location"), 40 | Gtk.CellRendererText (), text=1) 41 | info = Gtk.TreeViewColumn (_("Information"), 42 | Gtk.CellRendererText (), text=2) 43 | name.set_property ("resizable", True) 44 | location.set_property ("resizable", True) 45 | info.set_property ("resizable", True) 46 | tv.append_column (name) 47 | tv.append_column (location) 48 | tv.append_column (info) 49 | tv.set_rules_hint (True) 50 | sw = Gtk.ScrolledWindow () 51 | sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) 52 | sw.set_shadow_type (Gtk.ShadowType.IN) 53 | sw.add (tv) 54 | page1.pack_start (sw, True, True, 0) 55 | self.treeview = tv 56 | troubleshooter.new_page (page1, self) 57 | 58 | def display (self): 59 | answers = self.troubleshooter.answers 60 | if answers['cups_queue_listed']: 61 | return False 62 | 63 | if not answers.get ('remote_server_cups', False): 64 | return False 65 | 66 | server = answers['remote_server_try_connect'] 67 | 68 | model = Gtk.ListStore (str, 69 | str, 70 | str, 71 | GObject.TYPE_PYOBJECT) 72 | self.model = model 73 | self.treeview.set_model (model) 74 | iter = model.append (None) 75 | model.set (iter, 0, _("Not listed"), 1, '', 2, '', 3, 0) 76 | 77 | parent = self.troubleshooter.get_window () 78 | 79 | try: 80 | self.op = TimedOperation (cups.Connection, 81 | kwargs={"host": server}, 82 | parent=parent) 83 | c = self.op.run () 84 | self.op = TimedOperation (c.getDests, parent=parent) 85 | dests = self.op.run () 86 | printers = None 87 | dests_list = [] 88 | for (name, instance), dest in dests.items (): 89 | if name is None: 90 | continue 91 | 92 | if instance is not None: 93 | queue = "%s/%s" % (name, instance) 94 | else: 95 | queue = name 96 | 97 | if printers is None: 98 | self.op = TimedOperation (c.getPrinters) 99 | printers = self.op.run () 100 | 101 | if name not in printers: 102 | info = _("Unknown") 103 | location = _("Unknown") 104 | else: 105 | printer = printers[name] 106 | info = printer.get('printer-info', _("Unknown")) 107 | location = printer.get('printer-location', _("Unknown")) 108 | 109 | dests_list.append ((queue, location, info, dest)) 110 | 111 | dests_list.sort (key=lambda x: x[0]) 112 | for queue, location, info, dest in dests_list: 113 | iter = model.append (None) 114 | model.set (iter, 0, queue, 1, location, 2, info, 3, dest) 115 | 116 | except cups.HTTPError: 117 | pass 118 | except cups.IPPError: 119 | pass 120 | except RuntimeError: 121 | pass 122 | 123 | return True 124 | 125 | def connect_signals (self, handler): 126 | self.signal_id = self.treeview.connect ("cursor-changed", handler) 127 | 128 | def disconnect_signals (self): 129 | self.treeview.disconnect (self.signal_id) 130 | 131 | def can_click_forward (self): 132 | model, iter = self.treeview.get_selection ().get_selected () 133 | if iter is None: 134 | return False 135 | return True 136 | 137 | def collect_answer (self): 138 | if not self.troubleshooter.answers.get ('remote_server_cups', False): 139 | return {} 140 | 141 | model, iter = self.treeview.get_selection ().get_selected () 142 | if not model: 143 | return {} 144 | 145 | dest = model.get_value (iter, 3) 146 | if dest == 0: 147 | class enum_dests: 148 | def __init__ (self, model): 149 | self.dests = [] 150 | model.foreach (self.each, None) 151 | 152 | def each (self, model, path, iter, user_data): 153 | dest = model.get_value (iter, 3) 154 | if dest: 155 | self.dests.append ((dest.name, dest.instance)) 156 | 157 | return { 'remote_cups_queue_listed': False, 158 | 'remote_cups_dests_available': enum_dests (model).dests } 159 | else: 160 | return { 'remote_cups_queue_listed': True, 161 | 'remote_cups_dest': dest, 162 | 'remote_cups_queue': dest.name, 163 | 'remote_cups_instance': dest.instance } 164 | 165 | def cancel_operation (self): 166 | self.op.cancel () 167 | -------------------------------------------------------------------------------- /userdefault.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ## Copyright (C) 2006, 2007, 2008, 2010, 2012, 2014 Red Hat, Inc. 4 | ## Author: Tim Waugh 5 | 6 | ## This program is free software; you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation; either version 2 of the License, or 9 | ## (at your option) any later version. 10 | 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program; if not, write to the Free Software 18 | ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | from gi.repository import Gtk 21 | import os 22 | import subprocess 23 | 24 | class UserDefaultPrinter: 25 | def __init__ (self): 26 | try: 27 | lpoptions = os.environ["HOME"] 28 | except KeyError: 29 | try: 30 | lpoptions = "/home/" + os.environ["USER"] 31 | except KeyError: 32 | lpoptions = None 33 | 34 | if lpoptions: 35 | lpoptions += "/.cups/lpoptions" 36 | 37 | self.lpoptions = lpoptions 38 | 39 | def clear (self): 40 | if not self.lpoptions: 41 | return 42 | 43 | try: 44 | opt_file = open(self.lpoptions) 45 | opts = opt_file.readlines () 46 | except IOError: 47 | return 48 | 49 | for i in range (len (opts)): 50 | if opts[i].startswith ("Default "): 51 | opts[i] = "Dest " + opts[i][8:] 52 | open (self.lpoptions, "w").writelines (opts) 53 | 54 | def get (self): 55 | if not self.lpoptions: 56 | return None 57 | 58 | try: 59 | opt_file = open(self.lpoptions) 60 | opts = opt_file.readlines () 61 | except IOError: 62 | return None 63 | 64 | for i in range (len (opts)): 65 | if opts[i].startswith ("Default "): 66 | rest = opts[i][8:] 67 | slash = rest.find ("/") 68 | if slash != -1: 69 | space = rest[:slash].find (" ") 70 | else: 71 | space = rest.find (" ") 72 | return rest[:space] 73 | return None 74 | 75 | def set (self, default): 76 | p = subprocess.Popen ([ "lpoptions", "-d", default ], 77 | close_fds=True, 78 | stdin=subprocess.DEVNULL, 79 | stdout=subprocess.DEVNULL, 80 | stderr=subprocess.PIPE) 81 | (stdout, stderr) = p.communicate () 82 | exitcode = p.wait () 83 | if exitcode != 0: 84 | raise RuntimeError (exitcode, stderr.decode ().strip ()) 85 | return 86 | 87 | def __repr__ (self): 88 | return "" % repr (self.get ()) 89 | 90 | class UserDefaultPrompt: 91 | def __init__ (self, 92 | set_default_fn, 93 | refresh_fn, 94 | name, 95 | title, 96 | parent, 97 | primarylabel, 98 | systemwidelabel, 99 | clearpersonallabel, 100 | personallabel): 101 | self.set_default_fn = set_default_fn 102 | self.refresh_fn = refresh_fn 103 | self.name = name 104 | dialog = Gtk.Dialog (title=title, 105 | transient_for=parent, 106 | modal=True, 107 | destroy_with_parent=True) 108 | dialog.add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 109 | Gtk.STOCK_OK, Gtk.ResponseType.OK) 110 | dialog.set_default_response (Gtk.ResponseType.OK) 111 | dialog.set_border_width (6) 112 | dialog.set_resizable (False) 113 | hbox = Gtk.HBox.new (False, 12) 114 | hbox.set_border_width (6) 115 | image = Gtk.Image () 116 | image.set_from_stock (Gtk.STOCK_DIALOG_QUESTION, Gtk.IconSize.DIALOG) 117 | image.set_alignment (0.0, 0.0) 118 | hbox.pack_start (image, False, False, 0) 119 | vboxouter = Gtk.VBox.new (False, 6) 120 | primary = Gtk.Label () 121 | primary.set_markup ('' + 122 | primarylabel + '') 123 | primary.set_line_wrap (True) 124 | primary.set_alignment (0.0, 0.0) 125 | vboxouter.pack_start (primary, False, False, 0) 126 | vboxradio = Gtk.VBox.new (False, 0) 127 | systemwide = Gtk.RadioButton.new_with_mnemonic (None, systemwidelabel) 128 | vboxradio.pack_start (systemwide, False, False, 0) 129 | clearpersonal = Gtk.CheckButton.new_with_mnemonic (clearpersonallabel) 130 | alignment = Gtk.Alignment.new (0, 0, 0, 0) 131 | alignment.set_padding (0, 0, 12, 0) 132 | alignment.add (clearpersonal) 133 | vboxradio.pack_start (alignment, False, False, 0) 134 | vboxouter.pack_start (vboxradio, False, False, 0) 135 | personal = Gtk.RadioButton.new_with_mnemonic_from_widget(systemwide, 136 | personallabel) 137 | vboxouter.pack_start (personal, False, False, 0) 138 | hbox.pack_start (vboxouter, False, False, 0) 139 | dialog.vbox.pack_start (hbox, False, False, 0) 140 | systemwide.set_active (True) 141 | clearpersonal.set_active (True) 142 | self.userdef = UserDefaultPrinter () 143 | clearpersonal.set_sensitive (self.userdef.get () is not None) 144 | 145 | self.systemwide = systemwide 146 | self.clearpersonal = clearpersonal 147 | self.personal = personal 148 | systemwide.connect ("toggled", self.on_toggled) 149 | dialog.connect ("response", self.on_response) 150 | dialog.show_all () 151 | 152 | def on_toggled (self, button): 153 | self.clearpersonal.set_sensitive (self.userdef.get () is not None and 154 | self.systemwide.get_active ()) 155 | 156 | def on_response (self, dialog, response_id): 157 | if response_id != Gtk.ResponseType.OK: 158 | dialog.destroy () 159 | return 160 | 161 | if self.systemwide.get_active (): 162 | if self.clearpersonal.get_active (): 163 | self.userdef.clear () 164 | self.set_default_fn (self.name) 165 | else: 166 | try: 167 | self.userdef.set (self.name) 168 | except Exception as e: 169 | print("Error setting default: %s" % repr (e)) 170 | 171 | self.refresh_fn () 172 | 173 | dialog.destroy () 174 | --------------------------------------------------------------------------------