pax_global_header00006660000000000000000000000064141134460640014515gustar00rootroot0000000000000052 comment=a097520d3f019a9f0464cd26252dc63215ab1a1e mate-dock-applet-21.10.0/000077500000000000000000000000001411344606400150055ustar00rootroot00000000000000mate-dock-applet-21.10.0/.github/000077500000000000000000000000001411344606400163455ustar00rootroot00000000000000mate-dock-applet-21.10.0/.github/FUNDING.yml000066400000000000000000000007101411344606400201600ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: ubuntu_mate open_collective: # Replace with a single Open Collective username ko_fi: ubuntumate tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry custom: https://ubuntu-mate.org/donate/ mate-dock-applet-21.10.0/.gitignore000066400000000000000000000004531411344606400167770ustar00rootroot00000000000000.*.*~ *.*~ *~ *.py *.swp *.pyc __pycache__ *.valid autom4te.cache configure configure.ac INSTALL install.sh Makefile Makefile.in missing py-compile .idea .tags .tags1 .vscode ABOUT-NLS po/Makefile.in.in po/POTFILES po/Makevars.template po/Rules-quot po/*.gmo po/*.header po/*.sed po/*.sin po/*.pot mate-dock-applet-21.10.0/AUTHORS000066400000000000000000000000171411344606400160530ustar00rootroot00000000000000Robin Thompson mate-dock-applet-21.10.0/COPYING000066400000000000000000001045131411344606400160440ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mate-dock-applet-21.10.0/ChangeLog000066400000000000000000000507271411344606400165720ustar00rootroot00000000000000V0.88 Added Unity and Unity Flat looks to the dock Changed the way in which the dock appearance is set in the preferences dialog, so that preset 'themes' or a custom mix of icon background and indicators can be applied. Fix for issue #167 - Unpinned applications do not minimize to their own button Fix for issue #166 - MATE's Sticky Notes show up in the Dock Fix for issue #126 - Version 0.83 adds extra space on the right side of the panel. Fix for issue #113 - Unity-like icons backlight + gloss Fix for issue #101 - Double click needed, if panel not expaned Fix for an untracked issue where apps whose .desktop file specified a full path and filename for their desktop icon would not load the icon correctly. Also closed several historic and no longer relevant issues relating to older versions of the applet. V0.87 Fix for issue #158 - Odd Icon Behavior when Minimizing and then unminimizing. Fix for issue #139 - LibreOffice Writer not showing in dock Fix for issue #160 - Unused evenFixt parameter removed Fix for issue #ccccc159 - Use dark variant when defined by theme Fix for issue #154 - Added a 'bring all windows forward' option Fix for issue #156 - AttributeError in do_window_scroll() Fix for issue #152 - Icons of wine applications aren't displayed Fix for issue #153 - All actions defined in a .desktop file are now displayed in the right click or popup menus rather. Previously a maximum of 4 only would be displayed. Fix for issue #146 - unable to restore some minimized windows Fix for issue #144 - blurry icons on hidpi displays Note : Many thanks go to github user @vkareh for the fixes to issues #144, #159 and #160 V0.86 Fix for issue #130, Icon stays highlighted with all windows closed Fix for issue #131 - dock sizing options appearing in the preferences dialog when not needed Fix for issue #132 - Program activation by dragging doesn't work Fix for issues #136 and #140 - icons for windows which the applet should have been ignoring were appearing in the dock. Fix for issue #137 - Telegram icon not found; shows as generic cogs Fix for issue #138 - dragging app icons not always working Fix for issue #142 - Certain function keys cause the keyboard listener to stop working (thanks to @nschulzke on github for this) Potential fix for issue #144 - Blurry app icons. Untested due to lack of hidpi monitor. Fixed window previews with Compiz which were broken in V0.81 When an app's window is closed from the window list, the window list is now hidden V0.85 Contains a workaround for Launchpad bug #1755835 which affected Mutineers who switched panel layouts and found their dock settings had not been carried forward. V0.84 The workaround introduced in V0.83 has been adjusted following the removal of the workspace swticher applet from the Mutiny panel layout V0.83 Contains a temporary workaround for a mate panel issue (https://github.com/mate-desktop/mate-panel/issues/745) This workaround will be removed when the issue is resolved. V0.82 With MATE 1.20 app icon scrolling is automatically enabled when it is needed - i.e. when the applet runs out of space on the panel to expand into - and automatically disabled when not needed e.g. when the applet is given more space by being moved, or when another applet it removed from the panel. When the applet is moved around the panel, it will now also resize larger or smaller to fit into the available space (in the same way as the window list applet does) down to a minimum size of 4 app icons. Because of this, the dock size configuration options introduced in V0.81 are not needed with Mate 1.20 and do not appear in the preferences dialog. If this new version of the applet is used with versions of MATE < 1.20 it will behave as V0.81 did, auto configuring scrolling when the Mutiny panel layout is used, and otherwise allowing the user to configure scrolling via the preferences dialog. V0.81 Removed built in app matching code and replaced it with use of the bamf library The dock can now scroll app icons. This feature is automatically enabled when using the Mutiny layout of Mate Tweak, and can also be manually configured by using a new preferences item. Action lists and window lists no longer steal focus from other windows. Solid filled active app backgrounds have been adjusted to provide better contrast with the panel. V0.80 With Gtk3 and Python GObject bindings 3.26.0 or greater, window lists and action lists now have rounded corners and point to their app's icon in the dock. Fix for Issues #52 and #53 Fix for Issue #84 - app icons continually flashing The delay before action lists appear when the mouse hovers over a dock icon can now be set in the preferences dialog Apps can now be pinned to specific workspaces, in other words their app icons only appear in the dock when a particular workspace is active. This allows users to customise the dock for each workspace they use. Fix for Issue #87 - the applet right click menu no longer contains actions for the currently active app when popup action lists are enabled When unpinning an app a notification is now displayed which allows the operation to be undone and re-pins the app to the dock. The appearance of progress bars on dock icons has been improved. V0.79 The applet no longer swallows key presses. This means it now works happily alongside other apps that also use the key, e.g. the Brisk menu, the Advanced Mate Menu or Albert. Fixed crashes relating to Pango when window list and action_list contents needed to be ellipsized. V0.78 Added five new types of indicators Added a new option to specify the amount of space between icons in the dock Added a new option to specify how a dock icon reacts when an app requires attention. The icon can now either flash (the default) or display an exclamation mark over the icon. V0.77 Extended drag and support in Gtk3 version of the applet: Apps can be added to the dock by dragging them off menu applets (Main Menu, Menu Bar, Advanced Menu, Brisk Menu) and onto the applet (Gtk3 only) If data is dragged from an app onto another running app's icon the new app will be made active, allowing the dragged data to dropped onto it (Gtk3 only) Added keyboard shortcuts to select and acticate apps in the dock: 1-0 for the first 10 apps in the dock 1-0 for apps 10-20 in the dock For the 5th app, it would be necessary to hold down the Super key (i.e. the Windows) key and press 5. For the 12th app, it would be necessary to hold bown both the Super key and the Alt key and press 2. The effect of these shortcuts is as follows: If the app is not running, it will be started. If the app is running and only has a single window open, the window will cycled between minimised and activated states. If the app is running and has multiple windows open, each keypress will activate and display each window in turn. Updated the About window to provide details of the new drag and drop and keyboard shortcuut features. Fixed a bug which prevented window and action lists from appearing New dependencies - keybinder-0.0 (gir1.2-keybinder-0.0 (ubuntu) for gtk2) keybinder-3.0 (gir1.2-keybinder-3.0 (ubuntu) for gtk3) libkeybinder-3.0 (Arch) V0.76 Added support for startup notification when launching apps. Added new indicator type - a single solid bar. On Gtk3 this uses the active theme's highlight colour. This is not possible with Gtk2, therefore the applet will draw a gray bar, although the user can override this colour and choose a new one to fit in with whichever theme they are using Added new types of indicators and active icon backgrounds, and reworked code so that adding new types in the future will be easier. Added new preferences options to allow the select the type of indicator and active icon background, along with with a live preview. Added a new preference item allowing the user to select a colour to use when drawing bar indicators. About dialog reworked to be hopefully less ugly.... V0.75 Fixed bug that caused window lists to sometimes span monitors on multi monitor systems Added Compiz support - the dock can now display window previews (via the Scale and DBus plugins) when switching between app windows rather than using the built in window list. Mouse clicks on an app's dock icon now work differently: If the app is not running, or if the Shift key is pressed while clicking, the app will be started / a new instance will be started. If the app has only 1 window open, the window will be activated. If the app has multiple windows open, the window list will be shown or the Compiz scale plugin will be activated, as appropriate. The configuration option to restore all of app's windows or only the previously active one on a mouse click has been removed, and replace with a new one which allows to user to select between using the built in window list, or Compiz window previews. The window list no longer contains app actions, e.g. Pin/Unpin, or e.g. 'New Document' for LibreOffice Writer). App actions now appear in a separate popup window in the same way that the window list used to, by hovering the mouse over the dock icon. The actions are also available by right clicking on the app icon and selecting them from the panel popup menu. A new configuration option has been added to prevent the popup windows appearing in case users want to select actions from the right click menu only. GTK3 only - The colour of window lists and action popups now match the panel which contains the applet, whether the panel is set to use a colour from the current theme or a custom colour. V0.74 Fix for improved matching of binary packaged apps on Gentoo . Amended README.me to include the availability of an overlay for the applet on Gentoo. Corrected position of window lists on non-expanded panels. Fix for window list flickering on bottom aligned panels on MATE Gtk3 Increased the delay before window lists are shown when the mouse hovers over an app icon. It was 0.5 seconds, and is now 1 second. Shortened pin/unpin window list text. It now says 'Pin ' rather than 'Pin to the dock', and the Unpin text is similarly shortened. Fixed a bug that would cause Pin/Unpin actions to act upon previously highlighted app icons, rather than the one that is currently highlighted. When starting to drag an app icon, the window list is now hidden. The applet can now display progress bars and counts on app icons for apps which support this e.g. the Ubuntu software updater. V0.73 Added drag and drop rearranging of dock icons (Gtk3 only). Window list reworked and prettified. V0.72 The applet now works on and can be built for both GTK2 and GTK3 versions of MATE. Aside from the changes to layout containers (i.e. GTK2 VBox & HBoxes become GTK3 Boxes/Grids), underlying differences between the two toolkits meant that the code to calculate window minimise positions and window list popup positions had to be reworked. A GTK2 verison of the applet can be produced by running './configure --prefix=/usr' during the build process, while running './configure --prefix=/usr --with-gtk3' will produce a Gtk3 version. V0.71 More improvements to matching apps with their .desktop files. In particular this relates to Ubuntu 16.04 and Vivaldi, PlayOnLinux, Aptik, Dia, Tor Browser, Gnome Software, Gnome Disks, Bazaar Explorer. App icons can now be sourced from the 'hicolor' directory The window list now displays the title of the active window in bold and italicised text. Removed the options to display pinned apps from all or only the current workspace as these were based on a misunderstanding of a feature request. They have been replaced with a new option to only show indicators and window list items for windows which are on the current workspace and this provides the requested functionality. Added a 'Hints and Tips' window accessible from the About dialog as a place to list useful keyboard shortcuts etc. V0.70 Settings from previous instances of the applet are now imported silently (previously the user was presented with a dialog asking the user if they wanted to import the settings). The change is to prevent problems when switching to the Mutiny desktop layout via Mate Tweak in Ubuntu Mate 16.04 When saving custom launchers the ~/.local/share/applications directory will be created if it doesn't already exist v0.69 Added code to allow new instances of apps to be started by middle clicking their dock icon (see https://bugs.launchpad.net/ubuntu-mate/+bug/1554128) Fixed bug that would prevent apps from launching if they were in a directory structure which contained a space character e.g. ~/Downloads/PopCorn Time/ Fixed bug which associated newly opened windows with incorrect apps and which occurred when the wm_class_name of the window was not set. Fix for https://bugs.launchpad.net/ubuntu-mate/+bug/1555324 V0.68 Fix for Launchpad bug 1550392 (https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1550392) V0.67 Panel colour changing now occurs smoothly over over 0.5s rather than abruptly. Big cleanup of git repository! V0.66 Improved matching of apps with their .desktop files, in particular Opera and Chrome Dock icons now pulse when a new window is opened for a running app Minimize targets are now recalculated when the applet is moved or changes panel, so that windows always minimize to the correct place on the dock Added option to change MATE panel colour according to the current desktop wallpaper. (Note: this works for images only, not slideshows, gradients, or solid colour backgrounds). The colour can be applied to all panels or just the panel containing the dock. The applet now depends on the Python Imaging Library and SciPy packages because of this change Added new preferences to options to both activate panel colour changing and to limit it to the dock panel panel only Added option to not display indicators under running apps Added option to not display in the dock running pinned apps which are not on the current workspace Using the mouse wheel on a dock icon to scroll through an app's windows will now change workspace if the new window is not on the current workspace Selecting an app's window from the pop-up window list will now change workspace if the window is not on the current workspace Prefs dialog reworked because of new options added in this version V0.65 Dock icons now blink when an app needs attention Change to window activation code so that the applet works with MATE 1.12 V0.64 Fixed bug that would sometimes prevent a window from being focused when a dock icon was clicked Many changes to Improve detection of .desktop files from running apps and linking to dock icons Right click options (e.g. 'Open new Window' and Open new incognito window with Chrome) are now read from .desktop files and appear on the dock icon right click menu Custom launchers now set the Type field of the .desktop files they create to 'Application' and also set the NoDisplay field to 'true' so that the launcher is not displayed in the MATE menu Customer launchers now write .desktop files that do not contain spaces in the filename, as per the GNOME developer docs App icons can now be sourced from ~/.local/share/icons (e.g popcorn-time puts its icon here) V0.63 Removed the tooltip that appears when the mouse hovers over a docked app and replaced it with a list of the app's open windows. For each window, the list displays the app icon, an indicator showing which window is currently active, the window title, and a close icon. Clicking the close icon closes the window, clicking anywhere else makes the window active. Removed the list of app windows from the applet right click menu as they are no longer required. Changed the way the applet works when a running app's dock icon is clicked. This no longer minimizes/maximises all windows, but simply activates the app's last active window. Using the mouse scroll wheel over a running app's dock icon now scrolls through each of the app's open windows, unminimizing them and activating them as necessary .desktop files located in the user's home directory now take precedence over those located elsewhere in the filesystem. This allows users to create their own .desktop files (e.g. to customize an app's icon) and have them recognized by the applet Changed factory service file to explicitly invoke applet with python 3 The applet now saves its settings in ~/.config/mate_dock_applet.conf as well as in dconf. On first being added to a panel, the applet checks to see if this file exists and if it does it offers to use this configuration. This allows e.g. an easy way to restore the applet after an accidental deletion from the panel, and also a way to move applet configurations from one computer to another. V0.62 Fixed app icon drawing on non-composited displays. For apps which the applet does not recognise (their names or icons are incorrect) added a new right click menu option on the dock to create a custom launcher for the app. This displays a dialog (like the one for the MATE panel) allowing the app's command line, name, and icon to be specified. For user convenience, the applet will automatically fill in as many of these details as it can. Once the new launcher has been created, the app needs to be closed and reopened for it to be recognised by the dock. Typically, this option will only be needed for apps which have not been installed into the usual locations within the Linux filesystem. When an app's windows are minimised by clicking on the app's dock icon and then maximised by clicking it again, the app window that was previously active is made active again. V0.61 improved the way in which the windows owned by apps are detected Fixed the function that calculates the average colour of icons (for use when drawing highligts on the dock). It works now.... Fixed launching of Caja on linux mint Fixed docked_app.setup_from_wnck so that it passes '/' terminated versions of all directories to be searched for .desktop file to get_desktop_from_app_info Shift-clicking the icon of running applications now opens a new window of the app Added an option for the dock to only display unpinned apps from the current workspace.Pinned apps are always displayed no matter what workspace is active so that the user always has quick access to them) V0.60 - various bugfixes and minor additions including: Added an option in the preferences dialog to display multiple indicators for each open window an app has. The maximum number of indicators has been limited to 4 because on small panels (<32 pixels) there just isn't room for any more changed the app icon drawing code so that most drawing is done off screen and only copied to the panel at the very end Improved detection of app icons and app windows. In general this is a good thing, and in particular it means that the applet now works correctly with guvcview on Ubuntu Mate 15.04 Fixed a bug on Ubuntu Mate 15.04 where terminals started from the applet would have their working directory set as / instead of the home directory Updated the readme with instructions on how to compile the applet from source V0.59 - initial commit to git. Honour panel transparency and background settings Pin and unpin apps to the dock Rearrange application icons on the dock Works when panel is aligned to any side of the screen Launch apps by clicking on their icons in the dock Minimize/unminimize running app windows by clicking the app's dock icon Detect changes in the current icon theme and update the dock accordinly Use an indicator by each app to show when it is running Allow the user to specify whether a light or dark indicator is used so that it can always be seen no matter what colour the panel is Provide an About dialog mate-dock-applet-21.10.0/INSTALL000066400000000000000000000013161411344606400160370ustar00rootroot00000000000000 First install the required dependencies: * Python3 * gir1.2-wnck-1.0 * libglib2-dev * Python Imaging Library * SciPy then cd to the directory containing all of the development files and run: aclocal automake --add-missing autoreconf ./configure --prefix=/usr make sudo make install Installation on Ubuntu Mate on a Pi 2 This is a little more involved. First download gir1.2-wnck-1.0 for arm architechure from [here](http://launchpadlibrarian.net/160438738/gir1.2-wnck-1.0_2.30.7-0ubuntu4_armhf.deb) and install it with sudo dpkg -i. Then install other dependencies - sudo apt-get install git autoreconf libglib2.0-dev From this point the instructions above for compiling from source should be followed. mate-dock-applet-21.10.0/Makefile.am000066400000000000000000000000521411344606400170360ustar00rootroot00000000000000SUBDIRS = src po ACLOCAL_AMFLAGS = -I m4 mate-dock-applet-21.10.0/NEWS000066400000000000000000000025141411344606400155060ustar00rootroot0000000000000015 Jun 2015 - V0.62 uploaded to github. This new version fixes issues relating to non-composited displays, allows custom launchers to be added to the dock, and fixes a bug when minimising and then maximising an app's windows. Full details are in the ChangeLog 15 Oct 2015 - V0.64 uploaded to github 8 Jan 2016 - V0.66 uploaded to github This new version adds a feature shamlessly copied from the Unity Desktop - the ability of the applet to change the color of MATE panels to (hopefully) match the current desktop wallpaper. The change can be applied to all panels or just the panel containing the dock. As a result of this new feature, the applet has two new dependencies, the Python Imaging Library and SciPy. On Arch Linux they can be installed with: sudo pacman -S python-pillow python-scipy Whereas on Ubuntu 15.10 they can be installed with: sudo apt-get install python3-pil python3-scipy For other distributions the commands and package names will obviously vary. The new version also contains a couple of minor new features and a few bugfixes. Full details are in the Changelog. mate-dock-applet-21.10.0/README000066400000000000000000000013261411344606400156670ustar00rootroot00000000000000An application dock applet for the MATE panel The applet allows you to: Place a dock on any MATE panel, of any size, on any side of the desktop you desire. Pin and unpin apps to the dock Rearrange application icons on the dock Launch apps by clicking on their icons in the dock Minimize/unminimize running app windows by clicking the app's dock icon Detect changes in the current icon theme and update the dock accordingly Use an indicator by each app to show when it is running Optionally, use multiple indicators for each window an app has open Use either a light or dark indicator that it can always be seen no matter what colour the panel is For installation instructions and screenshots, see README.md mate-dock-applet-21.10.0/README.md000066400000000000000000000131551411344606400162710ustar00rootroot00000000000000# An application dock applet for the MATE panel ### Mate dock applet V0.88 on Ubuntu MATE 18.10 ![V0.88 on Ubuntu MATE 18.10](https://github.com/robint99/screenshots/blob/master/dock%20applet%20V0.88.png) The applet works with both GTK2 and GTK3 versions of MATE and allows you to: * Place a dock on any MATE panel, of any size, on any side of the desktop you desire. * Pin and unpin apps to the dock. Pinned apps can be shown in the dock on all workspaces or only the workspace where they were pinned (allowing the dock to be customised for each particular workspace). * Rearrange application icons on the dock * Launch apps by clicking on their icons in the dock * Minimize/unminimize running app windows by clicking the app's dock icon * Detect changes in the current icon theme and update the dock accordingly * Use an indicator by each app to show when it is running * Optionally, use multiple indicators for each window an app has open * Use different styles of indicators, or turn indicators off altogether * Change the colour of MATE panels to the dominant colour (i.e. the most common colour) of the desktop wallpaper. The colour can be applied to all panels or just the panel containing the dock. ## Installation ### Debian The applet is available in Debian testing (currently GTK2 only): `apt-get install mate-dock-applet` ### Ubuntu MATE 16.04 and later The applet is included by default in Ubuntu MATE 16.04. It can be used by selecting the 'Mutiny' desktop layout in the MATE Tweak application, or by simply adding it to any panel. Note: when upgrading from Ubuntu Mate 15.10 to 16.04 any previously installed version of the applet will be replaced with the one from the distribution's respositories. ### Linux Mint 19 The applet is not installed by default - `apt-get install mate-dock-applet` will do the trick... ### Linux Mint 18.2 and 18.3 The applet is included in the repositories but is compiled for Gtk2, rather than Gtk3. Therefore it will not work with the version of MATE desktop supplied with Linux Mint. Currently, the only solution is to manually compile and install the applet from source - instructions are further below. Note: the latest version of the applet which will work with the version of Gtk3 used in Linux Mint is V0.80 - souce code available [here](https://github.com/robint99/mate-dock-applet/archive/V0.81.tar.gz). ### Ubuntu MATE 15.10 and Linux Mint 18.1 Users of Ubuntu MATE 15.10 and earlier, or of Linux Mint 18.1 or earlier, can install the applet from the PPA kindly provided by [webupd8](http://www.webupd8.org/2015/05/dock-applet-icon-only-window-list-for.html) Note: this is currently GTK2 only ### Arch Linux For Arch users the GTK 3 version is available as a [package in the repositories](https://archlinux.org/packages/community/any/mate-applet-dock/). ### Gentoo based distributions An ebuild is available via the [mate-de-gentoo](https://github.com/oz123/mate-de-gentoo) ### Other distributions Users of other distros will need to install from source, so first install the required dependencies. Note, the package names below are for Ubuntu/Linux Mint/Debian - the name of the packages will vary on other distros. * Python3 * Python wnck bindings (gir1.2-wnck-1.0 for Gtk2 versions of the applet, gir1.2-wnck-3.0 for Gtk3) (`gnome-python2-libwnck` in Fedora) * Python implementation of Xlib - python-xlib (python3-xlib in Ubuntu based distributions and Fedora) * GLib development files (libglib2.0-dev) (`glib2-devel` in Fedora) * Python Imaging Library (python3-pil) (`python3-pillow` in Fedora) * Python 3 Cairo bindings (python3-cairo) * Bamf (bamfdaemon, libbamf and gir1.2-bamf) (`bamf-daemon` and `bamf` in Fedora) * Python Distro (python3-distro) then cd to the directory containing all of the development files and run: ``` aclocal automake --add-missing autoreconf ``` To build a GTK2 version of the applet: ``` ./configure --prefix=/usr ``` To build a GTK3 version: ``` ./configure --prefix=/usr --with-gtk3 ``` Then enter the following commands: ``` make sudo make install ``` ### Installation on Ubuntu MATE on a Pi 2 This is a little more involved. First download gir1.2-wnck-1.0 for arm architechure from [here](http://launchpadlibrarian.net/160438738/gir1.2-wnck-1.0_2.30.7-0ubuntu4_armhf.deb) and install it with sudo dpkg -i. Then install other dependencies - sudo apt-get install git autoreconf libglib2.0-dev From this point the instructions above for compiling from source should be followed. ### Note for Compiz Users In order for window minimizing and maximizing to work correctly under Compiz, the Focus Prevention Level setting must be set to off in CompizConfig Settings Manager (General Options, Focus and Raise Behaviour) ### Obligatory screen shots V0.76 of the applet running on Ubuntu MATE 16.10, showing the new indicator style and active icon background. Note: the Gtk3 theme is Arc Darker (hence the blue indicators), and the icon theme is [La Capitaine](https://www.gnome-look.org/p/1148695/) ![New indicators and icon backgrounds](https://github.com/robint99/screenshots/raw/master/new%20indicators%20and%20icon%20background.png) GTK3 version of the applet running on Ubuntu MATE 16.10 Alpha 1 ![GTK3 Ubunbtu Mate](https://github.com/robint99/screenshots/raw/master/16.10%20win-list.png) Running on Arch with a Unity style layout ![Arch screenshot](https://github.com/robint99/screenshots/raw/master/arch_V0.6_ss.png) Running on Ubuntu with a Windows 7 style layout ![Ubuntu screenshot](https://github.com/robint99/screenshots/raw/master/Ubuntu_V0.6_ss.png) Running on a Raspberry Pi 2 with Ubuntu MATE ![Pi2 screenshot](https://github.com/robint99/screenshots/raw/master/pi2_mate_V0.62_ss.png) mate-dock-applet-21.10.0/configure.ac000066400000000000000000000020271411344606400172740ustar00rootroot00000000000000AC_INIT([Dock Applet], [20.04.0]) AM_INIT_AUTOMAKE AM_PATH_PYTHON([3.0]) GLIB_GSETTINGS AC_ARG_WITH([gtk3], [AS_HELP_STRING([--with-gtk3], [Build for Gtk3 instead of Gtk2])], [], [with_gtk3=no]) AS_IF([test "x$with_gtk3" != xno], [ AM_CONDITIONAL(WITH_GTK3_SET, true)], [ AM_CONDITIONAL(WITH_GTK3_SET, false) ]) #IT_PROG_INTLTOOL([0.40.0]) #GETTEXT_PACKAGE=dock-applet #AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETEX_PACKAGE"], [Define the gettext package to be used]) #AC_SUBST(GETTEXT_PACKAGE) AM_GNU_GETTEXT_VERSION([0.19]) AM_GNU_GETTEXT([external]) #m4_pattern_allow([AM_V_GEN])dnl Make autoconf not complain about the rule below #PANEL_INTLTOOL_MATE_PANEL_APPLET_RULE='%.mate-panel-applet: %.mate-panel-applet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(AM_V_GEN) LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' #AC_SUBST([PANEL_INTLTOOL_MATE_PANEL_APPLET_RULE]) AC_CONFIG_FILES([Makefile src/Makefile po/Makefile.in]) AC_OUTPUT mate-dock-applet-21.10.0/po/000077500000000000000000000000001411344606400154235ustar00rootroot00000000000000mate-dock-applet-21.10.0/po/LINGUAS000066400000000000000000000000061411344606400164440ustar00rootroot00000000000000es fr mate-dock-applet-21.10.0/po/Makevars000066400000000000000000000047631411344606400171310ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. # However, the package name doesn't have the "mate" prefix but we want # to avoid catalog name clashes so we add it here. DOMAIN = mate-dock-applet # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --from-code=utf-8 # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = Free Software Foundation, Inc. # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = https://github.com/ubuntu-mate/mate-dock-applet/issues # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = no # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = mate-dock-applet-21.10.0/po/POTFILES.in000066400000000000000000000005651411344606400172060ustar00rootroot00000000000000# List of source files which contain translatable strings. src/dock.py src/dock_about.py src/dock_action_list.py src/dock_applet.py src/dock_color_changer.py src/dock_custom_launcher.py src/dock_info.py src/dock_popup.py src/dock_prefs.py src/dock_win_list.py src/dock_xml.py src/docked_app.py src/docked_app_helpers.py src/dom_color.py src/log_it.py src/window_control.py mate-dock-applet-21.10.0/po/es.po000066400000000000000000000522641411344606400164030ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: 0.80\n" "Report-Msgid-Bugs-To: https://github.com/ubuntu-mate/mate-dock-applet/" "issues\n" "POT-Creation-Date: 2021-03-24 12:28+0100\n" "PO-Revision-Date: 2017-12-14 13:03-0600\n" "Last-Translator: Joel Barrios \n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/dock.py:496 msgid "Mate Dock Applet" msgstr "Dock Applet de Mate" #: src/dock.py:1353 #, python-format msgid "Pin %s" msgstr "Prender %s" #: src/dock.py:1355 #, python-format msgid "Unpin %s" msgstr "Desprender %s" #: src/dock.py:1372 #, python-format msgid "Move %s up the dock" msgstr "Mover %s arriba" #: src/dock.py:1373 #, python-format msgid "Move %s down the dock" msgstr "Mover %s abajo" #: src/dock.py:1379 #, python-format msgid "Move %s to the left on the dock" msgstr "Mover %s a la izquierda" #: src/dock.py:1380 #, python-format msgid "Move %s to the right on the dock" msgstr "Mover %s a la derecha" #: src/dock.py:1386 #, python-format msgid "Close %s" msgstr "Cerrar %s" #: src/dock.py:1388 msgid "Close all windows" msgstr "Cerrar todas las ventanas" #: src/dock.py:1427 msgid "_Pin app to the dock" msgstr "_Prender aplicación" #: src/dock.py:1427 src/dock.py:1496 msgid "Pin app to the dock" msgstr "Prender aplicación" #: src/dock.py:1430 src/dock.py:1499 msgid "_Unpin app from the dock" msgstr "_Desprender aplicación" #: src/dock.py:1430 src/dock.py:1499 msgid "Unpin app from the dock" msgstr "Desprender aplicación" #: src/dock.py:1433 msgid "Move app _up the dock" msgstr "Mover _arriba" #: src/dock.py:1433 msgid "Move app up the dock" msgstr "Mover arriba" #: src/dock.py:1436 msgid "Move app _down the dock" msgstr "Mover _debajo" #: src/dock.py:1436 msgid "Move app down the dock" msgstr "Mover debajo" #: src/dock.py:1439 msgid "Move app _left in the dock" msgstr "Mover a _la izquierda" #: src/dock.py:1439 msgid "Move app left in the dock" msgstr "Mover a la izquierda" #: src/dock.py:1442 msgid "Move app _right in the dock" msgstr "Mover a la de_recha" #: src/dock.py:1442 msgid "Move app right in the dock" msgstr "Mover a las derecha" #: src/dock.py:1445 msgid "Dock P_references" msgstr "P_referencias" #: src/dock.py:1445 msgid "Dock Preferences" msgstr "Preferencias de Dock Applet" #: src/dock.py:1448 msgid "Create custo_m launcher for this app" msgstr "Crear _lanzador personalizado para esta aplicación" #: src/dock.py:1448 msgid "Create custom launcher for this app" msgstr "Crear lanzador personalizado para esta aplicación" #: src/dock.py:1451 msgid "About..." msgstr "Acerca de..." #: src/dock.py:1454 msgid "_Close" msgstr "_Cerrar" #: src/dock.py:1454 src/dock_about.py:63 src/dock_color_changer.py:316 #: src/dock_info.py:70 msgid "Close" msgstr "Cerrar" #: src/dock.py:1474 msgid "Pin" msgstr "Prender" #: src/dock.py:1475 msgid "Unpin" msgstr "Desprender" #: src/dock.py:1477 src/dock_prefs.py:123 msgid "Preferences" msgstr "Preferencias" #: src/dock.py:1478 #, fuzzy msgid "Create custom launcher" msgstr "Crear lanzador personalizado" #: src/dock.py:1479 msgid "About" msgstr "Acerca de" #: src/dock.py:1614 #, python-format msgid "%s unpinned" msgstr "%s desprendido" #: src/dock.py:1616 msgid "Undo" msgstr "Deshacer" #: src/dock.py:1654 #, python-format msgid "%s re-pinned" msgstr "Se ha vuelto a prender %s" #: src/dock.py:2175 msgid "The name of the launcher has not been set" msgstr "Nombre del lanzador sin establecer" #: src/dock.py:2177 msgid "The command of the launcher has not been set" msgstr "Orden del lanzador sin establecer" #: src/dock.py:2179 msgid "The icon of the launcher has not been set" msgstr "Icono del lanzador sin establecer" #: src/dock.py:2188 msgid "Cannot create launcher" msgstr "Imposible crear lanzador" #: src/dock_about.py:58 msgid "About Dock Applet" msgstr "Acerca de Dock Applet" #: src/dock_about.py:65 msgid "Hints & Tips" msgstr "Consejos y Trucos" #: src/dock_about.py:67 msgid "License" msgstr "Licencia" #: src/dock_about.py:121 msgid "MATE Dock Applet" msgstr "Dock Applet de MATE" #: src/dock_about.py:131 msgid "" "MATE Dock Applet is free software; you can redistribute it and/or modify it " "under the terms of the GNU General Public Licence as published by the Free " "Software Foundation; either version 3 of the Licence, or (at your option) " "any later version. \n" "\n" "MATE Dock Applet 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 Licence for " "more details.\n" "\n" "You should have received a copy of the GNU General Public Licence along with " "the applet; if not, write to the Free Software Foundation, Inc., 51 Franklin " "St, Fifth Floor, Boston, MA 02110-1301 USA\n" msgstr "" "MATE Dock Applet es un software libre, puedes redistribuirlo y/o modificarlo " "bajo los términos de la Licencia Pública General de GNU publicada por la " "Fundación de Software Libre, ya sea la versión 3 de la licencia o (a su " "elección) cualquier versión posterior.\n" "\n" "MATE Dock Applet se distribuye con la esperanza de que sea útil, pero SIN " "NINGUNA GARANTÍA, incluso sin la garantía implícita de COMERCIABILIDAD o " "APTITUD PARA UN PROPÓSITO PARTICULAR. Consulte la Licencia Pública General " "de GNU para más detalles. \n" "\n" "Debes haber recibido una copia de la Licencia Pública General de GNU junto " "con Dock Applet; si no, escribe a Free Software Foundation, Inc., 51 " "Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" #: src/dock_about.py:151 msgid "A dock applet for the MATE desktop" msgstr "Una mini aplicación lanzadores para el escritorio de MATE" #: src/dock_about.py:153 msgid "This program comes with ABSOLUTELY NO WARRANTY" msgstr "Este programa viene ABSOLUTAMENTE SIN GARANTÍA" #: src/dock_about.py:154 msgid "and is distributed under the GNU General" msgstr "y distribuido bajo los términos de la" #: src/dock_about.py:155 msgid "Public License, version 3 or later" msgstr "Licencia Pública General de GNU, versión 3 o posterior" #: src/dock_about.py:164 msgid "For details click" msgstr "Para detalles, haga clic" #: src/dock_about.py:167 msgid "here" msgstr "aquí" #: src/dock_about.py:225 msgid "Drag and drop data between applications" msgstr "Arrastre y suelte datos entre aplicaciones" #: src/dock_about.py:227 msgid "" "To easily drag and drop data from one application to another, drag the data " "from the first application onto the dock icon of the second application. The " "dock will activate the second application's window, allowing the data to be " "dragged onto it. Note: the second application must already be running." msgstr "" "Para arrastrar y soltar fácilemente datos de una aplicación a otra, arrastre " "los datos desde la primera aplicación hacia el icono en Dock Applet de la " "segunda aplicación. Dock Applet activará la ventana de la segunda aplicación " "Permitiendo que los datos sean arastrados hacia ésta. Nota: la segunda " "aplicación debe estar en ejecución." #: src/dock_about.py:234 msgid "Adding new applications to the dock" msgstr "Añadir nuevas aplicaciones a Dock Applet" #: src/dock_about.py:236 msgid "" "Applications can be dragged and dropped from any menu applet (i.e. the Main " "Menu, Menu Bar, Advanced Menu, or Brisk Menu) directly onto the dock." msgstr "" "Las aplicaciones pueden ser arrastradas y soltadas desde la mini-aplicación " "de menú (ejemplo: Main Menu, Advanced Menu o Brisk Menu) directamente sobre " "Dock Applet." #: src/dock_about.py:242 msgid "Activating apps with keyboard shortcuts" msgstr "Activando aplicaciones con atajos de teclado" #: src/dock_about.py:244 msgid "" "Holding down the (i.e. Windows) key and pressing a number key will " "activate an app in the dock. For example, pressing 1 will activate the first " "app, 2 the second etc. Pressing 0 will activate the tenth app. To activate " "apps 11 to 20, hold down the key as well as ." msgstr "" "Sosteniendo la tecla (logo de Windows) y presionado una tecla de " "número activará una aplicación en Dock Applet. Por ejemplo, presionado 1 " "activará la primera aplicación, 2 la segunda, etc. Presionar 0 activará la " "décima aplicación. Para activar aplicaciones de la 11 a 20, sostenga la " "tecla junto con " #: src/dock_about.py:252 msgid "Opening a new instance of a running application" msgstr "Abrir una nueva instancia de una aplicación en ejecución" #: src/dock_about.py:254 src/dock_info.py:123 msgid "" "To quickly open a new instance of a running application either hold down the " " key while clicking the application's dock icon, or middle click on " "the icon.\n" "\n" "Note: this works for most, but not all, apps." msgstr "" "Para abrir rápidamente una nueva instancia de una aplicación en " "ejecuciónpulse y sostenga la tecla mientras mientras hace clic " "sobreel icono de ésta o bien haga clic central sobre el icono.\n" "Nota: ésto funciona para la mayoría —pero no todas— las aplicaciones." #: src/dock_about.py:261 src/dock_info.py:129 msgid "Window switching using the mouse wheel" msgstr "Intercambio de ventana utilizando la rueda del ratón" #: src/dock_about.py:264 src/dock_info.py:132 msgid "" "To quickly switch between an application's open windows, move the mouse " "cursor over the apps's dock icon and use the mouse scroll wheel. This will " "activate and display each window in turn, changing workspaces as necessary." msgstr "" "Para cambiar rápidamente entre ventanas abiertas de una aplicación, mueva el " "puntero del ratón sobre el icono de ésta y utilice la rueda del ratón. Ésto " "activará y mostrará cada ventana en turno, incluyendo espacios de trabajo." #: src/dock_about.py:271 src/dock_info.py:138 msgid "Panel colour changing" msgstr "Cambio del color del panel" #: src/dock_about.py:273 src/dock_info.py:140 msgid "" "When the applet sets the panel colour for the first time, the result may not " "look exactly as expected. This is because the default opacity of custom " "coloured MATE panels is set extremely low, so that the panel appears almost " "transparent.\n" "\n" "To remedy this, simply right click the panel, select Properties and adjust " "the panel opacity as required." msgstr "" "Cuando la mini-aplicación establece el color del panel por primera vez, el " "resultado pudiera verse distinto al esperado. Ésto es debido a que la " "opacidad predeterminada de paneles de MATE personalizados es muy baja, de " "modo que el panel aparece casi transparente.\n" "\n" "Para solucionar ésto, simplemente haga clic derecho sobre el panel, " "seleccione Propiedades y ajuste la opacidad de panel requerida." #: src/dock_color_changer.py:314 msgid "Dock color changer test" msgstr "" #: src/dock_color_changer.py:343 src/dock_color_changer.py:372 msgid "red:" msgstr "" #: src/dock_color_changer.py:344 src/dock_color_changer.py:373 msgid "green:" msgstr "" #: src/dock_color_changer.py:345 src/dock_color_changer.py:374 msgid "blue:" msgstr "" #: src/dock_custom_launcher.py:79 msgid "Create Launcher" msgstr "Crear lanzador" #: src/dock_custom_launcher.py:101 msgid "Help" msgstr "Ayuda" #: src/dock_custom_launcher.py:104 src/dock_prefs.py:157 msgid "Cancel" msgstr "Cancelar" #: src/dock_custom_launcher.py:107 src/dock_prefs.py:161 msgid "Ok" msgstr "De acuerdo" #: src/dock_custom_launcher.py:141 msgid "Click to select an icon" msgstr "Haga clic para elegir un icono" #: src/dock_custom_launcher.py:163 msgid "Name:" msgstr "Nombre:" #: src/dock_custom_launcher.py:178 msgid "Command:" msgstr "Orden:" #: src/dock_custom_launcher.py:183 msgid "Browse..." msgstr "Examinar..." #: src/dock_custom_launcher.py:190 msgid "Run in terminal:" msgstr "Ejecutar en terminal" #: src/dock_custom_launcher.py:198 msgid "Comment:" msgstr "Comentario:" #: src/dock_custom_launcher.py:204 msgid "Window Class" msgstr "Clase de Ventana" #: src/dock_custom_launcher.py:337 src/dock_custom_launcher.py:387 msgid "Choose an application..." msgstr "Escoja una aplicación" #: src/dock_custom_launcher.py:391 msgid "Image files" msgstr "Archivos de imagen" #: src/dock_custom_launcher.py:502 msgid "Custom Launchers" msgstr "Lanzadores personalizados" #: src/dock_custom_launcher.py:503 msgid "" "Custom launchers are an advanced feature meant to be used only with apps " "that the dock does not recognise (i.e. they display the wrong name or " "icon). \n" "\n" "Normally, this will only happen when the apps have been installed to a non " "standard location within the file system, so for the vast majority of apps " "this feature is not needed.\n" "\n" "Note: if an app is running when a custom launcher is created for it, the app " "will need to be closed and restarted for the dock to recognise it." msgstr "" "Los lanzadores personalizados son una función avanzada que debe ser usada " "sólo con aplicaciones que Dock Applet no reconozca (ej. cuando muestran " "nombre o icono equivocado).\n" "\n" "Normalmente ocurre sólo la aplicación se instaló fuera de un lugar estándar " "en el sistema de archivos, así que esta función es innecesaria para la " "mayoría de las aplicaciones.\n" "\n" "Nota: si está ejecutando la aplicación mientras se crea el lanzador " "personalizado, es necesario reiniciar ésta para ser reconocida por Dock " "Applet." #: src/dock_info.py:66 msgid "Dock Applet hints, tips, and information" msgstr "Consejos, información y trucos para Dock Applet" #: src/dock_info.py:149 msgid "" "Click the 'Hints & Tips' button on the applet 'About' dialog box to see this " "information again." msgstr "" "Clic sobre el botón 'Consejos y Trucos' en el diálogo de la ventana 'Acerca " "de' para ver esta información de nuevo." #: src/dock_prefs.py:185 msgid "Theme" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:1026 src/dock_prefs.py:1043 #, fuzzy msgid "Default" msgstr "Defecto" #: src/dock_prefs.py:191 src/dock_prefs.py:255 src/dock_prefs.py:997 #: src/dock_prefs.py:1013 src/dock_prefs.py:1028 src/dock_prefs.py:1045 msgid "Unity" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:255 src/dock_prefs.py:999 #: src/dock_prefs.py:1030 src/dock_prefs.py:1047 msgid "Unity Flat" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:200 src/dock_prefs.py:938 #: src/dock_prefs.py:971 src/dock_prefs.py:1032 src/dock_prefs.py:1049 msgid "Subway" msgstr "Subterráneo" #: src/dock_prefs.py:191 src/dock_prefs.py:1034 msgid "Custom" msgstr "" #: src/dock_prefs.py:197 msgid "Indicator Type" msgstr "Tipo de Indicador" #: src/dock_prefs.py:198 src/dock_prefs.py:924 src/dock_prefs.py:950 msgid "Default light" msgstr "Claro por defecto" #: src/dock_prefs.py:198 src/dock_prefs.py:926 src/dock_prefs.py:953 msgid "Default dark" msgstr "Oscuro por defecto" #: src/dock_prefs.py:198 src/dock_prefs.py:928 src/dock_prefs.py:956 msgid "Single bar" msgstr "Barra única" #: src/dock_prefs.py:199 src/dock_prefs.py:930 src/dock_prefs.py:959 msgid "Circle" msgstr "Círculo" #: src/dock_prefs.py:199 src/dock_prefs.py:932 src/dock_prefs.py:962 msgid "Square" msgstr "Cuadrado" #: src/dock_prefs.py:199 src/dock_prefs.py:934 src/dock_prefs.py:965 msgid "Triangle" msgstr "Triángulo" #: src/dock_prefs.py:199 src/dock_prefs.py:936 src/dock_prefs.py:968 msgid "Diamond" msgstr "Diamante" #: src/dock_prefs.py:200 src/dock_prefs.py:974 msgid "None" msgstr "Ninguno" #: src/dock_prefs.py:204 msgid "Preview" msgstr "" #: src/dock_prefs.py:229 msgid "Display an indicator for each open window" msgstr "Mostrar un indicador para cada ventana abierta" #: src/dock_prefs.py:230 msgid "Display an indicator (max 4) for each open window" msgstr "Mostrar un indicador (máximo 4) para cada ventana abierta" #: src/dock_prefs.py:254 #, fuzzy msgid "Icon Background" msgstr "Fondo de icono" #: src/dock_prefs.py:255 src/dock_prefs.py:993 src/dock_prefs.py:1009 msgid "Gradient fill" msgstr "Relleno en gradiente" #: src/dock_prefs.py:255 src/dock_prefs.py:995 src/dock_prefs.py:1011 msgid "Solid fill" msgstr "Rellenar sólido" #: src/dock_prefs.py:292 msgid "Pinned application dock icons" msgstr "Iconos de aplicaciones prendidas" #: src/dock_prefs.py:293 msgid "Display on all workspaces" msgstr "Mostrar en todos los espacios de trabajo" #: src/dock_prefs.py:294 msgid "Display only on the workspace the app was pinned" msgstr "" "Mostrar sólo en espacios de trabajo donde la aplicación ha sido prendida" #: src/dock_prefs.py:325 msgid "Unpinned application dock icons" msgstr "Iconos de aplicaciones desprendidas" #: src/dock_prefs.py:326 msgid "Display unpinned apps from all workspaces" msgstr "Mostrar aplicaciones desprendidas en todos los espacios de trabajo" #: src/dock_prefs.py:328 msgid "Display unpinned apps only from current workspace" msgstr "Mostrar aplicaciones desprendidas sólo en el espacio de trabajo actual" #: src/dock_prefs.py:358 msgid "Display indicators/window list items for current workspace only" msgstr "" "Mostrar lista de elementos de indicadores/ventanas sólo en espacio de " "trabajo actual" #: src/dock_prefs.py:371 msgid "" "Note: when displaying pinned apps only on the workspace where they were " "created, it is a good idea to also select the 'Display unpinned apps' and " "'Display indicators/window list' items for the current workspace only " "options." msgstr "" "Nota: al aplicaciones prendidas en el espacio de trabajo donde fueron " "creadas, es buena idea seleccionar también opciones 'Mostrar aplicaciones " "desprendidas' y 'Mostrar lista de indicadores/ventanas' para el espacio de " "trabajo actual" #: src/dock_prefs.py:383 msgid "Left clicking a running app's icon will:" msgstr "" #: src/dock_prefs.py:384 #, fuzzy msgid "Display a list of the app's windows" msgstr "Mostrar un indicador para cada ventana abierta" #: src/dock_prefs.py:386 msgid "Show thumbnail previews of the app's windows" msgstr "" #: src/dock_prefs.py:388 msgid "Minimize/restore all of the app's windows" msgstr "" #: src/dock_prefs.py:407 msgid "Unity-style behaviour (always focus app on first click)" msgstr "" #: src/dock_prefs.py:417 msgid "" "\n" "Notes:\n" "If an app has only a single window open, a window list will not be " "displayed. Instead the window will be minimized/restored.\n" "Window thumbnail previews require Compiz" msgstr "" #: src/dock_prefs.py:434 msgid "App spacing" msgstr "Espacio entre apps" #: src/dock_prefs.py:452 msgid "Panel colour" msgstr "Color del panel" #: src/dock_prefs.py:453 msgid "Change panel colour to match wallpaper" msgstr "Cambiar el color del panel para coincidir con fondo de pantalla" #: src/dock_prefs.py:455 msgid "Change colour of dock's panel only" msgstr "Cambiar sólo el color del panel de Dock Applet" #: src/dock_prefs.py:488 msgid "Dock size" msgstr "" #: src/dock_prefs.py:489 msgid "Variable - expand or contract as necessary" msgstr "" #: src/dock_prefs.py:491 msgid "Fixed" msgstr "" #: src/dock_prefs.py:493 msgid "Display up to " msgstr "" #: src/dock_prefs.py:501 #, fuzzy msgid " app icons" msgstr " icono de aplicación" #: src/dock_prefs.py:525 msgid "" "Disable popup action list and show app actions\n" "on panel right click menu only" msgstr "" "Desactivar ventana emergente de lista de acciones y mostrar\n" "acciones de aplicación sólo en el menú de clic derecho del panel" #: src/dock_prefs.py:541 msgid "Fallback bar indicator colour" msgstr "Color de repliegue de barra indicadora" #: src/dock_prefs.py:542 msgid "Colour" msgstr "Color" #: src/dock_prefs.py:544 msgid "" "Colour used for drawing bar indicators when theme colour cannot be " "determined or when using Gtk2" msgstr "" "Color utilizado para dibujar barras indicadoras cuando el color del temano " "puede ser determinado o cuando se utiliza Gtk2" #: src/dock_prefs.py:556 msgid "Action when apps need attention" msgstr "Acción a realizar cuando las aplicaciones requieren atención" #: src/dock_prefs.py:557 msgid "Blink the app icon" msgstr "Parpadear el icono de aplicación" #: src/dock_prefs.py:558 msgid "Show a badge on the app icon" msgstr "Mostrar insignia en el icono de aplicación" #: src/dock_prefs.py:591 msgid "Popup Delay(s)" msgstr "Demora de ventana(s) emergente(s)" #: src/dock_prefs.py:618 msgid "Appearance" msgstr "Apariencia" #: src/dock_prefs.py:619 msgid "Workspaces" msgstr "Espacios de trabajo" #: src/dock_prefs.py:620 msgid "Behaviour" msgstr "" #: src/dock_prefs.py:622 msgid "Panel Options" msgstr "Opciones de panel" #: src/dock_prefs.py:623 msgid "Misc" msgstr "Varios" #~ msgid "My test launcher" #~ msgstr "Mi lanzador de prueba" #~ msgid "This is a comment" #~ msgstr "Ésto es un comentario" #~ msgid "Window selection" #~ msgstr "Selección de ventana" #~ msgid "From applet's window list" #~ msgstr "Desde la lista de ventanas" #~ msgid "From window thumbnail previews (requires Compiz)" #~ msgstr "Desde vistas previas de " #~ msgid "Windows" #~ msgstr "Ventanas" #~ msgid "Must be implemented by Indicator subclasses" #~ msgstr "Debe se implementado por las subclases del Indicador" #~ msgid "Must be implemented by ActiveBackgroundDrawer descendents" #~ msgstr "" #~ "Debe ser implementado por los descendientes de ActiveBackgroundDrawer" mate-dock-applet-21.10.0/po/fr.po000066400000000000000000000522371411344606400164030ustar00rootroot00000000000000# Mate Dock Applet. # Copyright (C) 2015-2019, Robin Thompson # This file is distributed under the same license as the Mate Dock Applet package. # Robin Thompson , 2019. # msgid "" msgstr "" "Project-Id-Version: 20.04.0\n" "Report-Msgid-Bugs-To: https://github.com/ubuntu-mate/mate-dock-applet/" "issues\n" "POT-Creation-Date: 2021-03-24 12:28+0100\n" "PO-Revision-Date: 2021-02-20 13:44+0100\n" "Last-Translator: \n" "Language-Team: French\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: src/dock.py:496 msgid "Mate Dock Applet" msgstr "Appliquette de Dock MATE" #: src/dock.py:1353 #, python-format msgid "Pin %s" msgstr "%s (non-épinglé)" #: src/dock.py:1355 #, python-format msgid "Unpin %s" msgstr "%s (épinglé)" #: src/dock.py:1372 #, python-format msgid "Move %s up the dock" msgstr "Déplacer %s vers le haut dans le Dock" #: src/dock.py:1373 #, python-format msgid "Move %s down the dock" msgstr "Déplacer %s vers le bas dans le Dock" #: src/dock.py:1379 #, python-format msgid "Move %s to the left on the dock" msgstr "Déplacer %s vers la gauche dans le Dock" #: src/dock.py:1380 #, python-format msgid "Move %s to the right on the dock" msgstr "Déplacer %s vers la droite dans le Dock" #: src/dock.py:1386 #, python-format msgid "Close %s" msgstr "Fermer %s" #: src/dock.py:1388 msgid "Close all windows" msgstr "Fermer toutes les fenêtres" #: src/dock.py:1427 msgid "_Pin app to the dock" msgstr "É_pingler ce programme dans le Dock" #: src/dock.py:1427 src/dock.py:1496 msgid "Pin app to the dock" msgstr "Épingler ce programme dans le Dock" #: src/dock.py:1430 src/dock.py:1499 msgid "_Unpin app from the dock" msgstr "_Dés-épingler ce programme du Dock" #: src/dock.py:1430 src/dock.py:1499 msgid "Unpin app from the dock" msgstr "Dés-épingler ce programme du Dock" #: src/dock.py:1433 msgid "Move app _up the dock" msgstr "Déplacer ce programme vers le _haut" #: src/dock.py:1433 msgid "Move app up the dock" msgstr "Déplacer ce programme vers le haut" #: src/dock.py:1436 msgid "Move app _down the dock" msgstr "Déplacer ce programme vers le _bas" #: src/dock.py:1436 msgid "Move app down the dock" msgstr "Déplacer ce programme vers le bas" #: src/dock.py:1439 msgid "Move app _left in the dock" msgstr "Déplacer ce programme vers la _gauche" #: src/dock.py:1439 msgid "Move app left in the dock" msgstr "Déplacer ce programme vers la gauche" #: src/dock.py:1442 msgid "Move app _right in the dock" msgstr "Déplacer ce programme vers la _droite" #: src/dock.py:1442 msgid "Move app right in the dock" msgstr "Déplacer ce programme vers la droite" #: src/dock.py:1445 msgid "Dock P_references" msgstr "_Préférences du Dock" #: src/dock.py:1445 msgid "Dock Preferences" msgstr "Préférences du Dock" #: src/dock.py:1448 msgid "Create custo_m launcher for this app" msgstr "Créer un _lanceur personnalisé pour ce programme" #: src/dock.py:1448 msgid "Create custom launcher for this app" msgstr "Créer un lanceur personnalisé pour ce programme" #: src/dock.py:1451 msgid "About..." msgstr "À propos…" #: src/dock.py:1454 msgid "_Close" msgstr "_Fermer" #: src/dock.py:1454 src/dock_about.py:63 src/dock_color_changer.py:316 #: src/dock_info.py:70 msgid "Close" msgstr "Fermer" #: src/dock.py:1474 msgid "Pin" msgstr "Épingler" #: src/dock.py:1475 msgid "Unpin" msgstr "Dés-épingler" #: src/dock.py:1477 src/dock_prefs.py:123 msgid "Preferences" msgstr "Réglages" #: src/dock.py:1478 msgid "Create custom launcher" msgstr "Créer un lanceur personnalisé" #: src/dock.py:1479 msgid "About" msgstr "À propos" #: src/dock.py:1614 #, python-format msgid "%s unpinned" msgstr "%s a été dés-épinglé" #: src/dock.py:1616 msgid "Undo" msgstr "Annuler" #: src/dock.py:1654 #, python-format msgid "%s re-pinned" msgstr "%s a été ré-épinglé" #: src/dock.py:2175 msgid "The name of the launcher has not been set" msgstr "Aucun nom n’a été fourni pour ce lanceur" #: src/dock.py:2177 msgid "The command of the launcher has not been set" msgstr "Aucune commande n’a été fournie pour ce lanceur" #: src/dock.py:2179 msgid "The icon of the launcher has not been set" msgstr "Aucune icône n’a été fournie pour ce lanceur" #: src/dock.py:2188 msgid "Cannot create launcher" msgstr "Impossible de créer un lanceur" #: src/dock_about.py:58 msgid "About Dock Applet" msgstr "À propos de l’appliquette du Dock" #: src/dock_about.py:65 msgid "Hints & Tips" msgstr "Trucs et astuces" #: src/dock_about.py:67 msgid "License" msgstr "Licence" #: src/dock_about.py:121 msgid "MATE Dock Applet" msgstr "Appliquette de Dock pour MATE" #: src/dock_about.py:131 msgid "" "MATE Dock Applet is free software; you can redistribute it and/or modify it " "under the terms of the GNU General Public Licence as published by the Free " "Software Foundation; either version 3 of the Licence, or (at your option) " "any later version. \n" "\n" "MATE Dock Applet 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 Licence for " "more details.\n" "\n" "You should have received a copy of the GNU General Public Licence along with " "the applet; if not, write to the Free Software Foundation, Inc., 51 Franklin " "St, Fifth Floor, Boston, MA 02110-1301 USA\n" msgstr "" "L’appliquette de Dock pour MATE est un logiciel libre ; vous pouvez la " "redistribuer ou la modifier suivant les termes de la Licence publique " "générale GNU telle que publiée par la Free Software Foundation, soit la " "version 3 de cette License, soit une version ultérieure.\n" "\n" "Ce programme est distribué dans l’espoir qu’il sera utile, mais sans aucune " "garantie, pas même la garantie implicite de commercialisabilité ni celle " "d’adéquation à un besoin particulier. Consultez la GNU General Public " "License pour plus de détails.\n" "\n" #: src/dock_about.py:151 msgid "A dock applet for the MATE desktop" msgstr "Une appliquette de Dock pour le bureau MATE" #: src/dock_about.py:153 msgid "This program comes with ABSOLUTELY NO WARRANTY" msgstr "Ce programme est distribué sans aucune garantie," #: src/dock_about.py:154 msgid "and is distributed under the GNU General" msgstr "sous la Licence publique générale GNU," #: src/dock_about.py:155 msgid "Public License, version 3 or later" msgstr "version 3 ou ultérieure." #: src/dock_about.py:164 msgid "For details click" msgstr "Pour plus de détails, cliquer" #: src/dock_about.py:167 msgid "here" msgstr "ici" #: src/dock_about.py:225 msgid "Drag and drop data between applications" msgstr "Glisser et déposer des objets d’un programme à l’autre" #: src/dock_about.py:227 msgid "" "To easily drag and drop data from one application to another, drag the data " "from the first application onto the dock icon of the second application. The " "dock will activate the second application's window, allowing the data to be " "dragged onto it. Note: the second application must already be running." msgstr "" "Pour glisser et déposer facilement des objets d’un programme à un autre, " "amener le pointeur sur l’icône d’un programme dans le Dock sans relâcher le " "bouton de la souris. La fenêtre de ce programme reviendra alors au premier " "plan, et l’objet pourra y être déposé. N.B. : il faut que le deuxième " "programme soit déjà lancé." #: src/dock_about.py:234 msgid "Adding new applications to the dock" msgstr "Ajouter de nouveaux programmes dans le Dock" #: src/dock_about.py:236 msgid "" "Applications can be dragged and dropped from any menu applet (i.e. the Main " "Menu, Menu Bar, Advanced Menu, or Brisk Menu) directly onto the dock." msgstr "" "Des icônes peuvent être déposées directement dans le Dock depuis n’importe " "quelle appliquette de menu (le menu principal, la barre de menu ou le menu " "Brisk)." #: src/dock_about.py:242 msgid "Activating apps with keyboard shortcuts" msgstr "Activer des programmes avec des raccourcis clavier" #: src/dock_about.py:244 msgid "" "Holding down the (i.e. Windows) key and pressing a number key will " "activate an app in the dock. For example, pressing 1 will activate the first " "app, 2 the second etc. Pressing 0 will activate the tenth app. To activate " "apps 11 to 20, hold down the key as well as ." msgstr "" "Maintenir (la « touche Windows ») et presser une touche de chiffre " "permet d’ouvrir le programme correspondant dans le Dock. Ainsi, 1 active le " "premier programme, 2 le deuxième etc. ; 0 correspond au dixième programme. " "Pour les programmes de 11 à 20, il est nécessaire d’appuyer sur en " "plus de ." #: src/dock_about.py:252 msgid "Opening a new instance of a running application" msgstr "Ouvrir une nouvelle instance du programme déjà ouvert." #: src/dock_about.py:254 src/dock_info.py:123 msgid "" "To quickly open a new instance of a running application either hold down the " " key while clicking the application's dock icon, or middle click on " "the icon.\n" "\n" "Note: this works for most, but not all, apps." msgstr "" "Pour ouvrir rapidement une nouvelle instance d’un programme en cours, " "cliquer en appuyant sur ou faire un clic du milieu sur l’icône.\n" "\n" "N.B. : cela fonctionne pour la plupart des programmes, mais pas tous." #: src/dock_about.py:261 src/dock_info.py:129 msgid "Window switching using the mouse wheel" msgstr "Passer d’une fenêtre à l’autre avec la molette de la souris" #: src/dock_about.py:264 src/dock_info.py:132 msgid "" "To quickly switch between an application's open windows, move the mouse " "cursor over the apps's dock icon and use the mouse scroll wheel. This will " "activate and display each window in turn, changing workspaces as necessary." msgstr "" "Pour passer rapidement d’une fenêtre à l’autre d’un même programme, survoler " "son icône dans le Dock et utiliser la molette de la souris. Cela fera " "défiler les fenêtres successivement, y compris sur les autres espaces de " "travail." #: src/dock_about.py:271 src/dock_info.py:138 msgid "Panel colour changing" msgstr "Changer la couleur du tableau de bord" #: src/dock_about.py:273 src/dock_info.py:140 msgid "" "When the applet sets the panel colour for the first time, the result may not " "look exactly as expected. This is because the default opacity of custom " "coloured MATE panels is set extremely low, so that the panel appears almost " "transparent.\n" "\n" "To remedy this, simply right click the panel, select Properties and adjust " "the panel opacity as required." msgstr "" "Lorsque l’appliquette définit la couleur du tableau de bord pour la première " "fois, le résultat peut être quelque peu inattendu, du fait de la faible " "opacité de certains tableaux de bords dans MATE, qui sont presqu’entièrement " "transparents.\n" "\n" "Pour y remédier, faire un clic droit sur le tableau de bord, puis dans " "Propriétés, ajuster le niveau de transparence." #: src/dock_color_changer.py:314 msgid "Dock color changer test" msgstr "Test de couleur pour le Dock" #: src/dock_color_changer.py:343 src/dock_color_changer.py:372 msgid "red:" msgstr "rouge :" #: src/dock_color_changer.py:344 src/dock_color_changer.py:373 msgid "green:" msgstr "vert :" #: src/dock_color_changer.py:345 src/dock_color_changer.py:374 msgid "blue:" msgstr "bleu :" #: src/dock_custom_launcher.py:79 msgid "Create Launcher" msgstr "Créer un lanceur" #: src/dock_custom_launcher.py:101 msgid "Help" msgstr "Aide" #: src/dock_custom_launcher.py:104 src/dock_prefs.py:157 msgid "Cancel" msgstr "Annuler" #: src/dock_custom_launcher.py:107 src/dock_prefs.py:161 msgid "Ok" msgstr "OK" #: src/dock_custom_launcher.py:141 msgid "Click to select an icon" msgstr "Cliquer pour sélectionner une icône" #: src/dock_custom_launcher.py:163 msgid "Name:" msgstr "Nom :" #: src/dock_custom_launcher.py:178 msgid "Command:" msgstr "Commande :" #: src/dock_custom_launcher.py:183 msgid "Browse..." msgstr "Parcourir…" #: src/dock_custom_launcher.py:190 msgid "Run in terminal:" msgstr "Exécuter dans un terminal :" #: src/dock_custom_launcher.py:198 msgid "Comment:" msgstr "Commentaire :" #: src/dock_custom_launcher.py:204 msgid "Window Class" msgstr " Classe de fenêtre" #: src/dock_custom_launcher.py:337 src/dock_custom_launcher.py:387 msgid "Choose an application..." msgstr "Choisir un programme…" #: src/dock_custom_launcher.py:391 msgid "Image files" msgstr "Fichiers d’images" #: src/dock_custom_launcher.py:502 msgid "Custom Launchers" msgstr "Lanceurs personnalisés" #: src/dock_custom_launcher.py:503 msgid "" "Custom launchers are an advanced feature meant to be used only with apps " "that the dock does not recognise (i.e. they display the wrong name or " "icon). \n" "\n" "Normally, this will only happen when the apps have been installed to a non " "standard location within the file system, so for the vast majority of apps " "this feature is not needed.\n" "\n" "Note: if an app is running when a custom launcher is created for it, the app " "will need to be closed and restarted for the dock to recognise it." msgstr "" "Les lanceurs personnalisés sont destinés à des programmes que le Dock ne " "reconnaît pas (par exemple si leur intitulé ou leur icône ne correspond " "pas).\n" "\n" "En général, cela se produit lorsqu’un programme n’a pas été installé à son " "emplacement habituel dans le système de fichiers. Aussi cette fonction n’est-" "elle pas nécessaire dans la grande majorité des cas." #: src/dock_info.py:66 msgid "Dock Applet hints, tips, and information" msgstr "Trucs et astuces pour l’appliquette du Dock." #: src/dock_info.py:149 msgid "" "Click the 'Hints & Tips' button on the applet 'About' dialog box to see this " "information again." msgstr "" "Ouvrir les «Trucs et astuces» dans la boîte de dialogue «À propos» pour " "afficher à nouveau ces informations." #: src/dock_prefs.py:185 msgid "Theme" msgstr "Thème" #: src/dock_prefs.py:191 src/dock_prefs.py:1026 src/dock_prefs.py:1043 msgid "Default" msgstr "Par défaut" #: src/dock_prefs.py:191 src/dock_prefs.py:255 src/dock_prefs.py:997 #: src/dock_prefs.py:1013 src/dock_prefs.py:1028 src/dock_prefs.py:1045 msgid "Unity" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:255 src/dock_prefs.py:999 #: src/dock_prefs.py:1030 src/dock_prefs.py:1047 msgid "Unity Flat" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:200 src/dock_prefs.py:938 #: src/dock_prefs.py:971 src/dock_prefs.py:1032 src/dock_prefs.py:1049 msgid "Subway" msgstr "" #: src/dock_prefs.py:191 src/dock_prefs.py:1034 msgid "Custom" msgstr "Personnalisé" #: src/dock_prefs.py:197 msgid "Indicator Type" msgstr "Style d’indicateur" #: src/dock_prefs.py:198 src/dock_prefs.py:924 src/dock_prefs.py:950 msgid "Default light" msgstr "Par défaut (clair)" #: src/dock_prefs.py:198 src/dock_prefs.py:926 src/dock_prefs.py:953 msgid "Default dark" msgstr "Par défaut (sombre)" #: src/dock_prefs.py:198 src/dock_prefs.py:928 src/dock_prefs.py:956 msgid "Single bar" msgstr "Barre simple" #: src/dock_prefs.py:199 src/dock_prefs.py:930 src/dock_prefs.py:959 msgid "Circle" msgstr "Cercle" #: src/dock_prefs.py:199 src/dock_prefs.py:932 src/dock_prefs.py:962 msgid "Square" msgstr "Carré" #: src/dock_prefs.py:199 src/dock_prefs.py:934 src/dock_prefs.py:965 msgid "Triangle" msgstr "Triangle" #: src/dock_prefs.py:199 src/dock_prefs.py:936 src/dock_prefs.py:968 msgid "Diamond" msgstr "Losange" #: src/dock_prefs.py:200 src/dock_prefs.py:974 msgid "None" msgstr "Aucun" #: src/dock_prefs.py:204 msgid "Preview" msgstr "Aperçu" #: src/dock_prefs.py:229 msgid "Display an indicator for each open window" msgstr "Afficher un indicateur pour chaque fenêtre ouverte" #: src/dock_prefs.py:230 msgid "Display an indicator (max 4) for each open window" msgstr "Afficher un indicateur pour chaque fenêtre ouverte (4 au maximum)." #: src/dock_prefs.py:254 msgid "Icon Background" msgstr "Arrière-plan des icônes" #: src/dock_prefs.py:255 src/dock_prefs.py:993 src/dock_prefs.py:1009 msgid "Gradient fill" msgstr "Remplissage en dégradé" #: src/dock_prefs.py:255 src/dock_prefs.py:995 src/dock_prefs.py:1011 msgid "Solid fill" msgstr "Remplissage en aplat" #: src/dock_prefs.py:292 msgid "Pinned application dock icons" msgstr "Icônes épinglées dans le Dock" #: src/dock_prefs.py:293 msgid "Display on all workspaces" msgstr "Afficher sur tous les espaces de travail" #: src/dock_prefs.py:294 msgid "Display only on the workspace the app was pinned" msgstr "Afficher seulement là où l’icône a été épinglée" #: src/dock_prefs.py:325 msgid "Unpinned application dock icons" msgstr "Icônes dés-épinglées du Dock" #: src/dock_prefs.py:326 msgid "Display unpinned apps from all workspaces" msgstr "Afficher les programmes dés-épinglés de tous les espaces de travail" #: src/dock_prefs.py:328 msgid "Display unpinned apps only from current workspace" msgstr "Afficher les programmes dés-épinglés seulement de l’espace actif" #: src/dock_prefs.py:358 msgid "Display indicators/window list items for current workspace only" msgstr "" "Afficher les indicateurs et listes de fenêtre seulement pour l’espace actif" #: src/dock_prefs.py:371 msgid "" "Note: when displaying pinned apps only on the workspace where they were " "created, it is a good idea to also select the 'Display unpinned apps' and " "'Display indicators/window list' items for the current workspace only " "options." msgstr "" "N.B. : lorsqu’on affiche les programmes épinglés uniquement sur l’espace de " "travail actif, il peut être préférable de choisir aussi d’«Afficher les " "programmes dés-épinglés» et d’«Afficher les indicateurs et listes de " "fenêtres» pour l’espace de travail actif." #: src/dock_prefs.py:383 msgid "Left clicking a running app's icon will:" msgstr "Le clic gauche sur l’icône d’un programme ouvert sert à :" #: src/dock_prefs.py:384 msgid "Display a list of the app's windows" msgstr "Afficher une liste de toutes les fenêtres de ce programme" #: src/dock_prefs.py:386 msgid "Show thumbnail previews of the app's windows" msgstr "Afficher des aperçus miniatures des fenêtres de ce programme" #: src/dock_prefs.py:388 msgid "Minimize/restore all of the app's windows" msgstr "Réduire ou rétablir toutes les fenêtres de ce programme" #: src/dock_prefs.py:407 msgid "Unity-style behaviour (always focus app on first click)" msgstr "" "À la façon de Unity : amener le focus sur le programme au premier clic." #: src/dock_prefs.py:417 msgid "" "\n" "Notes:\n" "If an app has only a single window open, a window list will not be " "displayed. Instead the window will be minimized/restored.\n" "Window thumbnail previews require Compiz" msgstr "" "\n" "N.B. : Si un programme n’a qu’une seule fenêtre d’ouverte, aucune liste de " "fenêtres ne sera affichée ; l’icône servira seulement à minimiser ou " "rétablir la fenêtre.\n" "\n" "N.B. : Les aperçus miniatures des fenêtres nécessitent Compiz." #: src/dock_prefs.py:434 msgid "App spacing" msgstr "Espacement des icônes" #: src/dock_prefs.py:452 msgid "Panel colour" msgstr "Couleur du tableau de bord" #: src/dock_prefs.py:453 msgid "Change panel colour to match wallpaper" msgstr "Assortir la couleur du tableau de bord au fond d’écran" #: src/dock_prefs.py:455 msgid "Change colour of dock's panel only" msgstr "Ne changer la couleur que du tableau de bord où est le Dock" #: src/dock_prefs.py:488 msgid "Dock size" msgstr "Taille du Dock" #: src/dock_prefs.py:489 msgid "Variable - expand or contract as necessary" msgstr "Variable : étendre ou rétrécir selon les besoins" #: src/dock_prefs.py:491 msgid "Fixed" msgstr "Fixe" #: src/dock_prefs.py:493 msgid "Display up to " msgstr "Afficher jusqu’à " #: src/dock_prefs.py:501 msgid " app icons" msgstr "icônes de programmes" #: src/dock_prefs.py:525 msgid "" "Disable popup action list and show app actions\n" "on panel right click menu only" msgstr "" "Au clic droit, ne pas afficher une liste d’actions\n" "mais plutôt les options du programme en question" #: src/dock_prefs.py:541 msgid "Fallback bar indicator colour" msgstr "Couleur par défaut des indicateurs" #: src/dock_prefs.py:542 msgid "Colour" msgstr "Couleur" #: src/dock_prefs.py:544 msgid "" "Colour used for drawing bar indicators when theme colour cannot be " "determined or when using Gtk2" msgstr "" "Couleur à utiliser dans GTK2, ou lorsque la couleur du thème ne peut pas " "être détectée." #: src/dock_prefs.py:556 msgid "Action when apps need attention" msgstr "Comportement lorsqu’un programme signale quelque chose" #: src/dock_prefs.py:557 msgid "Blink the app icon" msgstr "Faire clignoter son icône" #: src/dock_prefs.py:558 msgid "Show a badge on the app icon" msgstr "Ajouter un badge à son icône" #: src/dock_prefs.py:591 msgid "Popup Delay(s)" msgstr "Durée de la notification" #: src/dock_prefs.py:618 msgid "Appearance" msgstr "Apparence" #: src/dock_prefs.py:619 msgid "Workspaces" msgstr "Espaces de travail" #: src/dock_prefs.py:620 msgid "Behaviour" msgstr "Comportement" #: src/dock_prefs.py:622 msgid "Panel Options" msgstr "Options du tableau de bord" #: src/dock_prefs.py:623 msgid "Misc" msgstr "Autres" mate-dock-applet-21.10.0/src/000077500000000000000000000000001411344606400155745ustar00rootroot00000000000000mate-dock-applet-21.10.0/src/Makefile.am000066400000000000000000000144071411344606400176360ustar00rootroot00000000000000 SUBDIRS = applet_SCRIPTS = dock_applet.py dock.py docked_app.py dock_prefs.py dock_about.py log_it.py dock_custom_launcher.py dock_win_list.py \ dock_action_list.py dock_xml.py dock_color_changer.py dom_color.py dock_info.py dock_popup.py docked_app_helpers.py window_control.py appletdir = $(libdir)/mate-applets/mate-dock-applet/ APPLET_LOCATION = $(appletdir)dock_applet.py appletsdir = $(datadir)/mate-panel/applets applets_in_files = org.mate.panel.DockApplet.mate-panel-applet applets_DATA = $(applets_in_files:.mate-panel-applet.in=.mate-panel-applet) #@echo "This is a test" #if WITH_GTK2_SET # $(info ************** Legacy Mode - Building for Gtk2 **************) #else # $(info ************** Building for Gtk3 **************) #endif if WITH_GTK3_SET do_substitution = $(AM_V_GEN)sed \ -e 's,[@]pythondir[@],$(pythondir),g' \ -e 's,[@]PACKAGE[@],$(PACKAGE),g' \ -e 's,[@]VERSION[@],$(VERSION),g' \ -e 's,[@]LOCATION[@],$(appletdir),g' \ -e 's,[@]localedir[@],$(datadir)/locale,g' else do_substitution = $(AM_V_GEN)sed \ -e 's,[@]pythondir[@],$(pythondir),g' \ -e 's,[@]PACKAGE[@],$(PACKAGE),g' \ -e 's,[@]VERSION[@],$(VERSION),g' \ -e 's,[@]LOCATION[@],$(appletdir),g' \ -e 's,[@]localedir[@],$(datadir)/locale,g' \ -e 's,build_gtk2 = False,build_gtk2 = True,g' endif #$(applets_in_files): $(applets_in_files).in Makefile # $(AM_V_GEN)sed \ # -e "s|\@LOCATION\@|$(appletdir)|" \ # -e "s|\@pythondir\@|$(pythondir)|" \ # -e "s|\@PACKAGE\@|$(PACKAGE)|" \ # -e "s|\@VERSION\@|$(VERSION)|" \ # $< > $@ #%.mate-panel-applet.in: %.mate-panel-applet.in.in Makefile # $(AM_V_GEN)sed \ # -e "s|\@LOCATION\@|$(appletdir)|" \ # -e "s|\@pythondir\@|$(pythondir)|" \ # -e "s|\@PACKAGE\@|$(PACKAGE)|" \ # -e "s|\@VERSION\@|$(VERSION)|" \ # -e "s|\@localdir\@|$(datadir)//locale|" \ # $< > $@ #@PANEL_INTLTOOL_MATE_PANEL_APPLET_RULE@ servicedir = $(datadir)/dbus-1/services service_in_files = org.mate.panel.applet.DockAppletFactory.service.in service_DATA = $(service_in_files:.service.in=.service) xmldir =$(appletdir) xml_DATA = app_match.xml data_assetsdir = $(appletdir)/assets data_assets_DATA = assets/unity_dock_bg_56.svg \ assets/unity_dock_edge_56.svg \ assets/unity_dock_shine_56.svg \ assets/unity_dock_bg_168.svg \ assets/unity_dock_edge_168.svg \ assets/unity_dock_shine_168.svg #%.gschema.xml.in: %.gschema.xml.in.in Makefile # $(AM_V_GEN)sed -e 's^\@GETTEXT_PACKAGE\@^$(GETTEXT_PACKAGE)^g' < $< > $@ # gsettings_SCHEMAS = org.mate.panel.applet.dock.gschema.xml # #@INTLTOOL_XML_NOMERGE_RULE@ #@GSETTINGS_RULES@ #$(gsettings_SCHEMAS).in: $(gsettings_SCHEMAS).in.in Makefile # $(AM_V_GEN)sed -e 's^\@GETTEXT_PACKAGE\@^$(GETTEXT_PACKAGE)^g' \ # -e "s|\@LOCATION\@|$(libexecdir)|" \ # < $< > $@ org.mate.panel.applet.DockAppletFactory.service: $(service_in_files) $(AM_V_GEN)sed \ -e "s|\@LOCATION\@|$(APPLET_LOCATION)|" \ $< > $@ org.mate.panel.DockApplet.mate-panel-applet: $(applet_in_files) $(do_substitution) < $(srcdir)/org.mate.panel.DockApplet.mate-panel-applet.in > org.mate.panel.DockApplet.mate-panel-applet dock_applet.py: dock_applet.in Makefile $(do_substitution) < $(srcdir)/dock_applet.in > dock_applet.py chmod +x dock_applet.py dock_about.py: dock_about.in Makefile $(do_substitution) < $(srcdir)/dock_about.in > dock_about.py chmod +x dock_about.py dock_prefs.py: dock_prefs.in Makefile $(do_substitution) < $(srcdir)/dock_prefs.in > dock_prefs.py chmod +x dock_prefs.py dock_custom_launcher.py: dock_custom_launcher.in Makefile $(do_substitution) < $(srcdir)/dock_custom_launcher.in > dock_custom_launcher.py chmod +x dock_custom_launcher.py dock.py: dock.in Makefile $(do_substitution) < $(srcdir)/dock.in > dock.py chmod +x dock.py docked_app.py: docked_app.in Makefile $(do_substitution) < $(srcdir)/docked_app.in > docked_app.py chmod +x docked_app.py log_it.py: log_it.in Makefile $(do_substitution) < $(srcdir)/log_it.in > log_it.py chmod +x log_it.py dock_win_list.py: dock_win_list.in Makefile $(do_substitution) < $(srcdir)/dock_win_list.in > dock_win_list.py chmod +x dock_win_list.py dock_action_list.py: dock_action_list.in Makefile $(do_substitution) < $(srcdir)/dock_action_list.in > dock_action_list.py chmod +x dock_action_list.py dock_xml.py: dock_xml.in Makefile $(do_substitution) < $(srcdir)/dock_xml.in > dock_xml.py chmod +x dock_xml.py dock_color_changer.py: dock_color_changer.in Makefile $(do_substitution) < $(srcdir)/dock_color_changer.in > dock_color_changer.py chmod +x dock_color_changer.py dom_color.py: dom_color.in Makefile $(do_substitution) < $(srcdir)/dom_color.in > dom_color.py chmod +x dom_color.py dock_info.py: dock_info.in Makefile $(do_substitution) < $(srcdir)/dock_info.in > dock_info.py chmod +x dock_info.py dock_popup.py: dock_popup.in Makefile $(do_substitution) < $(srcdir)/dock_popup.in > dock_popup.py chmod +x dock_popup.py docked_app_helpers.py: docked_app_helpers.in Makefile $(do_substitution) < $(srcdir)/docked_app_helpers.in > docked_app_helpers.py chmod +x docked_app_helpers.py window_control.py: window_control.in Makefile $(do_substitution) < $(srcdir)/window_control.in > window_control.py chmod +x window_control.py applet_PYTHON = \ dock_applet.py \ dock_about.py \ dock_prefs.py \ dock.py \ docked_app.py \ dock_custom_launcher.py \ log_it.py \ dock_win_list.py \ dock_action_list.py \ dock_xml.py \ dock_color_changer.py \ dom_color.py \ dock_popup.py \ dock_info.in \ docked_app_helpers.in \ window_control.in CLEANFILES = $(applet_SCRIPTS) \ org.mate.panel.applet.DockAppletFactory.service \ org.mate.panel.DockApplet.mate-panel-applet EXTRA_DIST = dock_applet.in \ dock_about.in \ dock_prefs.in \ dock.in \ docked_app.in \ dock_custom_launcher.in \ log_it.in \ dock_win_list.in \ dock_action_list.in \ dock_xml.in \ dock_color_changer.in \ dom_color.in \ dock_info.in \ dock_popup.in \ docked_app_helpers.in \ window_control.in \ $(gsettings_SCHEMAS) \ $(applets_in_files) \ $(service_in_files) \ $(xml_DATA) \ $(data_assets_DATA) mate-dock-applet-21.10.0/src/app_match.xml000066400000000000000000000071771411344606400202660ustar00rootroot00000000000000 Ubuntu 16.04 vivaldi-preview vivaldi-beta Ubuntu 16.04 Mainwindow.py PlayOnLinux arch Mainwindow.py playonlinux Antergos Linux Mainwindow.py playonlinux Ubuntu 16.04 dia-gnome Dia-gnome dia Ubuntu 16.04 Tor Browser Tor Browser tor-browser-en Ubuntu 16.04 Bzr Bzr bzr-explorer Ubuntu 16.04 aptik-gtk Aptik-gtk aptik Ubuntu 16.04 gnome-software Gnome-software gnome-software-local-file Ubuntu 16.04 gnome-disks Gnome-disks org.gnome.DiskUtility arch sublime_text sublime-text Antergos Linux sublime_text sublime-text mate-dock-applet-21.10.0/src/assets/000077500000000000000000000000001411344606400170765ustar00rootroot00000000000000mate-dock-applet-21.10.0/src/assets/unity_dock_bg_168.svg000066400000000000000000000035721411344606400230440ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/assets/unity_dock_bg_56.svg000066400000000000000000000035221411344606400227530ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/assets/unity_dock_edge_168.svg000066400000000000000000000143031411344606400233520ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/assets/unity_dock_edge_56.svg000066400000000000000000000141251411344606400232700ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/assets/unity_dock_shine_168.svg000066400000000000000000000056331411344606400235620ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/assets/unity_dock_shine_56.svg000066400000000000000000000056461411344606400235020ustar00rootroot00000000000000 image/svg+xml mate-dock-applet-21.10.0/src/dock.in000066400000000000000000005560701411344606400170610ustar00rootroot00000000000000#!/usr/bin/env python3 """Provide functionality relating to an application dock Manage a list of pinned and non-pinned dock apps. Handle all mouse ui events for docked apps Respond to window opening and closing events from libwnck Respond to changes to the Gtk icon theme and update all docked apps Load and save dock settings (e.g. pinned apps and indicator type) Respond to Unity API DBus messages so that apps can display counts and progress meters on their icons respond to selections made in the applet right click menu, specifically : allow apps to be pinned to the dock : allow apps to unpinned from the dock : allow app icons be to moved to a different position on the dock : disply an About dialog : display a Preferences dialog """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") gi.require_version("Notify", "0.7") gi.require_version("Bamf", "3") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import MatePanelApplet from gi.repository import GObject from gi.repository import Wnck from gi.repository import GdkPixbuf from gi.repository import Gio from gi.repository import GLib from gi.repository import Notify from gi.repository import Bamf import os import os.path import sys import subprocess from time import sleep import dbus from dbus.mainloop.glib import DBusGMainLoop import gettext import docked_app import dock_prefs import dock_about import dock_custom_launcher import dock_win_list import dock_action_list import dock_xml import dock_color_changer import docked_app_helpers import window_control from log_it import log_it as log_it gettext.install('mate-dock-applet', '@localedir@') class DragMotionTimer(object): """Timer to allow us to track mouse motion during a drag and drop operation. Required because: we don't get drag-motion events when dragging app icons within the applet and we need to track the mouse x,y so that we can rearrange dock icons on the fly Instantiates a timer which periodically gets the root x,y position of the mouse and translates these to applet x,y coordinate Attributes: dragee : the docked app which is being dragged drag-ended : the drag and drop operation has finished timer_id = the id of the timer that is instantiated mouse = a Gdk.device we can query for the mouse position """ def __init__(self, dragee, the_dock): """Init for the DragMotionTimer class. Sets everything up by creating the timer and setting a reference to the DockedApp being dragged Arguments: dragee : the DockedApp that is being dragged the_dock : the dock... """ self.dragee = dragee self.drag_ended = False self.the_dock = the_dock self.old_drag_pos = -1 # call the timer function 50 times per second self.timer_id = GObject.timeout_add(20, self.do_timer) # get the mouse device display = Gdk.Display.get_default() manager = display.get_device_manager() self.mouse = manager.get_client_pointer() def do_timer(self): """The timer function. If the drag operation has ended, delete the timer Move dock icons about etc.... """ # has the drag and drop ended? if self.drag_ended is True: GObject.source_remove(self.timer_id) return False none, x, y = self.mouse.get_position() dx, dy = self.the_dock.get_dock_root_coords() x = x - dx y = y - dy orient = self.the_dock.applet.get_orient() app_with_mouse = self.the_dock.get_app_at_mouse(x, y) if app_with_mouse is not None: app_name = app_with_mouse.app_name else: # we're not on the dock, so just record the current mouse x # or y and exit if (orient == MatePanelApplet.AppletOrient.UP) or \ (orient == MatePanelApplet.AppletOrient.DOWN): self.old_drag_pos = x else: self.old_drag_pos = y return True if app_with_mouse == self.dragee: return True # get the coordinates within the applet of the app that has the # mouse ax, ay = self.the_dock.get_app_root_coords(app_with_mouse) ax = ax - dx ay = ay - dy # if the dock is scrolling we need to add the scrolled window position # to the x and y coords if self.the_dock.scrolling: if (orient == MatePanelApplet.AppletOrient.UP) or \ (orient == MatePanelApplet.AppletOrient.DOWN): ax -= self.the_dock.scrolled_win.get_hadjustment().get_value() else: ay -= self.the_dock.scrolled_win.get_vadjustment().get_value() orient = self.the_dock.applet.get_orient() if (orient == MatePanelApplet.AppletOrient.UP) or \ (orient == MatePanelApplet.AppletOrient.DOWN): if self.old_drag_pos == -1: self.old_drag_pos = x return True if x > self.old_drag_pos: # we're moving left to right on a new app, so we need to # trigger an icon move at 40% of the icon width trig_val = ax + (self.dragee.drawing_area_size * 40) / 100 if (self.old_drag_pos < trig_val) and (x >= trig_val): new_pos = self.the_dock.app_list.index(app_with_mouse) self.the_dock.move_app(self.dragee, new_pos) else: # moving right to left trig_val = ax + self.dragee.drawing_area_size - (self.dragee.drawing_area_size * 40) / 100 if (self.old_drag_pos > trig_val) and (x <= trig_val): new_pos = self.the_dock.get_app_position_in_dock(app_with_mouse) self.the_dock.move_app(self.dragee, new_pos) self.old_drag_pos = x else: if self.old_drag_pos == -1: self.old_drag_pos = y return True if y > self.old_drag_pos: # we're moving top to bottom on a new app, so we need to # trigger an icon move at 40% of the icon height trig_val = ay + (self.dragee.drawing_area_size * 40) / 100 if (self.old_drag_pos < trig_val) and (y >= trig_val): new_pos = self.the_dock.app_list.index(app_with_mouse) self.the_dock.move_app(self.dragee, new_pos) else: # moving bottom to top trig_val = ay + self.dragee.drawing_area_size - (self.dragee.drawing_area_size * 40) / 100 if (self.old_drag_pos > trig_val) and (y <= trig_val): new_pos = self.the_dock.get_app_position_in_dock(app_with_mouse) self.the_dock.move_app(self.dragee, new_pos) self.old_drag_pos = y return True class DragActivateTimer(object): """ Timer used when something other than a .desktop file is dragged onto the dock Instantiates a timer which will wait for a short interval and then activate a specified app's window. This will allow apps to drag data onto the dock, activate an app's window and then drop the data there. The short delay between the user dragging data onto the dock and the app activating provides a better user experience .... Attributes: the_app : the app whose window is to be acticvated timer_id = the id of the timer that is instantiated """ def __init__(self, the_dock, the_app): """Init for the DragActionTimer class. Sets everything up by creating the timer and setting a reference to the DockedApp to be activated Arguments: the_dock : the dock... the_app : the app to be activated """ self.the_app = the_app self.the_dock = the_dock # wait .3 of a second ... self.timer_id = GObject.timeout_add(333, self.do_timer) def do_timer(self): """ Activate the app """ if (self.the_app is not None) and (self.the_app.is_running()): if not self.the_app.is_active: self.the_dock.minimize_or_restore_windows(self.the_app) self.timer_id = 0 return False # we only run once... class ScrollAnimator(object): """ Class to animate the scrolling of the dock Allow scrolling from a start point to an end point in a scrolled window over a number of frames with a specified interval. Also allow a specified callback to be called when the animation is finished Attributes: __scrolled_win : the window we're interested in __start_pos : the starting position of the scroll __end_pos : the ending position __orient : the panel orientation (e.g. "top") - so we know whether to scroll horizontally or vertically __num_frames : the number of frames __interval : the interval between frames in ms __callback : the callback for when the animation is finished __current_frame : the current animation frame number """ def __init__(self, sw, sp, ep, orient, nf, int, cb): self.__scrolled_win = sw self.__start_pos = sp self.__orient = orient self.__end_pos = ep self.__num_frames = nf self.__interval = int self.__callback = cb self.__current_frame = 0 # set the initial position of the scrolled window self.set_scroll_pos(self.__start_pos) # create a timer to periodically set the scrolled window position self.timer_id = GObject.timeout_add(self.__interval, self.do_timer) def set_scroll_pos(self, pos): """Set the current scroll position """ if self.__orient in ["top", "bottom"]: self.__scrolled_win.get_hadjustment().set_value(pos) else: self.__scrolled_win.get_vadjustment().set_value(pos) def do_timer(self): """ Update the scrolled window position adjustment = (self.__end_pos - self.__start_pos) / self.__num_frames """ self.__current_frame += 1 if self.__current_frame == self.__num_frames: # end of the animation - set the final position of the window and stop the # timer self.set_scroll_pos(self.__end_pos) if self.__callback is not None: self.__callback() return False else: new_pos = self.__start_pos + ((self.__end_pos - self.__start_pos) / self.__num_frames) * self.__current_frame self.set_scroll_pos(new_pos) return True class Dock(object): """The main application dock class Attributes: applet : the MATE panel applet wnck_screen : the currently active wnck_screen. Assumed to be the default wnck_screen on applet start up window : app_list : the list of DockedApp objects. Will contain running/non-running pinned apps and running unpinned apps box : A Gtk2 HBox or VBox (depending on the applet orientation) or Gtk3 Grid containing the drawing areas of each of the apps in app_list scrolled_win : a scrolled window too hold self.box (gtk3 only, in gtk2 self.box is added directly to the applet) sw_hadj : a Gtk.Adjustment - used when there isn't enough panel space to fully display the dock and we need to horizontally scroll it sw_vadj : as above, but for vertically scrolling app_spacing : the amount of space (in pixels) between icons on the dock icontheme : used to load application icons and detect changes in the icon theme about_win : the about window prefs_win : the preferences window ccl_win : the create custom launcher window app_with_mouse : the DockedApp that the mouse is currently over active_app : the DockedApp that is currently the foreground app right_clicked_app: the app that was most recently right clicked settings_path : the GIO.Settings path for the applet settings : GIO.Settings - the settings for the applet indicator : the indicator type (e.g. light, dark, bar or None) attention_type : the attention_type e.g. blink fallback_bar_col : a list of the r,g,b elements of the colour to be used when drawing e,g, bar indicators and the current theme highlight colour can't be determined (will be mainly of use in gtk2) active_bg : the type of background to use for the currently active app's icon background (e.g. gradient fill or solid fill) theme : the theme used by the dock. Can indicate a custom themes in which case the indicator and background types can be individually specified multi_ind : whether or not multiple indicators are to be used show_all_apps : whether or not unpinned apps from all workspaces are displayed in the dock win_from_cur_ws_only : whether indicators and window list items are to be shown for the current workspace only win_switch_unity_style : whether to switch to the last focused window of an app or to show a window list (on first click) change_panel_color : whether or not the color of MATE panels are to be changed to the dominant colour of the desktop wallpaper change_dock_color_only : whether or not all MATE panels are to have their colour changed or just the panel containing the dock panel_id : the toplevel id of the panel the applet is on dock_action_group : Gtk Action group containing all of the actions for the applet right click menu app_win_list : a replacement for the default tooltip - a window which lists the currently highlighted app's open windows and allows the user to select one app_act_list : a popup window which shows actions relating to the currently highlighted app e.g. Pin/Unpin/Open new window act_list_timer : timer object used when popping up the action list panel_cc : used to change the colour of the MATE panel(s) to the dominant color of the desktop wallpaper (if enabled in the applet settings) panel_orient: i.e. 'top', 'bottom', 'left', 'right' - obtained from dconf panel_x : the panel's x position on screen panel_y : the panel's y position on screen panel_size : the size of the panel panel_expand : whether or not the panel is set to expand applet_pos : the position of the applet(in pixels) in the panel dm_timer : timer to monitor mouse movement during app icon drag and drop da_timer : timer to provide a short delay before activating an app when data other than a .desktop file is dragged on the applet popup_delay : the delay (in ms) before an action list appears when the mouse hovers over a docked app pa_configs : list of pinned app configurations. each item in the list is a tuple containing the following: string : the config name string : the name of the workspace the config is associated with string : a .dektop filename representing a pinned app string : another pinned app, etc. etc. pa_on_all_ws : boolean - if true, pinned apps appear on all workspaces. If false, pinned apps are set according to the current workspace notification : the latest unpin notification matcher : a Bamf.Matcher for matching apps with their windows and .desktop files etc avail_panel_space : a tuple containing the amount of panel space (x & y) available to the dock scrolling : set to true when the dock doesn't have enough panel space to display itself and needs to scroll scroll_index : the index in self.app_list of the first visible item when scrolling is enabled scroll_timer : a timer to scroll the apps in the dock when the mouse hovers over an app icon dock_fixed_size : indicates that the dock is not to expand or contract and will instead claim enough space to display the specified number of apps. If set -1 the dock will in fact expand or contract panel_layout : the name of the current panel layout e.g. "mutiny" nice_sizing : whether or not we can use applet.set_size_hints to allow dock to request a size from the panel ns_base_apps : the minimum size of the dock (in app icons) to be used when nice_sizing is True ns_new_app : when nice_sizing is True, is used to indicate whether the last call to set_size_hints was in response to a new app being added to the dock ns_app_removed : when nice_sizing is True, specifies the visible index of the app that was removed from the dock, or None if no app was removed dds_done : when True, indicates that delayed setup has been completed and the applet is now fully setup drag_x : x coordinate of mouse where dragging began drag_y : y coordinate of mouse where dragging began dragging : are we dragging dock icons about max_num_actions : defines the maximum number of app actions that can appear in the panel right click menu or the action list popup hidden_views : a list of Bamf.View objects which have been opened but have not yet been made visible """ def __init__(self, applet): """Init the Dock. Load settings Setup the applet right click menu and actions Set default values """ super().__init__() Notify.init(_("Mate Dock Applet")) self.applet = applet # the panel applet, in case we need it later self.app_list = [] self.box = None if not build_gtk2: self.scrolled_win = Gtk.ScrolledWindow() self.scrolled_win.set_policy(Gtk.PolicyType.EXTERNAL, Gtk.PolicyType.EXTERNAL) self.scrolled_win.connect("scroll-event", self.window_scroll) self.icontheme = Gtk.IconTheme.get_default() self.icontheme.connect("changed", self.icon_theme_changed) self.window = None self.wnck_screen = Wnck.Screen.get_default() self.app_with_mouse = None # the dock app that mouse is currently over self.active_app = None # the currently active app self.right_clicked_app = None # the app that most recently had a right click self.settings_path = self.applet.get_preferences_path() self.settings = Gio.Settings.new_with_path("org.mate.panel.applet.dock", self.settings_path) # instantiate these - will be set up later self.object_settings = None self.panel_settings = None self.panel_event_handler_id = None self.panel_id = "" self.panel_layout = "" self.get_panel_layout() # specify the xml file to be used as an alternative storage location # for the applet settings self.xml_conf = os.path.expanduser("~/.config/mate_dock_applet.conf") self.prefs_win = None self.about_win = None self.ccl_win = None self.indicator = 0 self.fallback_bar_col = None self.multi_ind = False self.click_restore_last_active = True self.show_all_apps = True self.win_from_cur_ws_only = False self.use_win_list = True self.win_switch_unity_style = False self.click_action = dock_prefs.ClickActionType.MINMAX self.panel_act_list = False self.change_panel_color = False self.change_dock_color_only = False self.active_bg = 0 self.theme = 0 self.app_spacing = 0 self.attention_type = dock_prefs.AttentionType.BLINK self.popup_delay = 1000 self.pa_configs = [] self.pa_on_all_ws = True self.dock_fixed_size = -1 self.ns_new_app = False self.dds_done = False self.ns_app_removed = None self.read_settings() self.set_fallback_bar_colour() self.max_num_actions = 16 # read the list of apps which are difficult to match with their # .desktop files self.app_match = self.read_app_match() self.dock_action_group = None self.popup_action_group = None self.app_win_list = dock_win_list.DockWinList(self.wnck_screen, self.applet.get_orient(), 0) self.app_win_list.icontheme = self.icontheme self.app_act_list = dock_action_list.DockActionList(self.wnck_screen, self.applet.get_orient(), 0) self.app_act_list.icontheme = self.icontheme self.act_list_timer = None self.panel_x = 0 self.panel_y = 0 self.panel_size = 0 self.panel_expand = True self.panel_orient = "top" self.applet_pos = 0 self.dm_timer = None self.da_timer = None self.notification = None self.avail_panel_space = (1, 1) self.scrolling = False self.scroll_index = 0 self.scroll_timer = None self.sw_hadj = Gtk.Adjustment(0, 0, 1, 1, 1, 1) self.sw_vadj = Gtk.Adjustment(0, 0, 1, 1, 1, 1) # set a callback so that the window_control module can account for dock scrolling # when calculating window minimise positions window_control.adj_minimise_pos_cb = self.adjust_minimise_pos # create an event handler so that we can react to changes e.g # the panel the applet is on, or it's position on the panel applet_spath = self.settings_path[0: len(self.settings_path) - 6] # remove "prefs/" suffix self.object_settings = Gio.Settings.new_with_path("org.mate.panel.object", applet_spath) self.object_settings.connect("changed", self.applet_panel_settings_changed) self.setup_menu() self.panel_cc = dock_color_changer.PanelColorChanger() # we need to monitor the Unity dbus interface DBusGMainLoop(set_as_default=True) self.session_bus = dbus.SessionBus() # claim the Unity bus (Unity won't be using it...) so that clients know # to start using it self.session_bus.request_name("com.canonical.Unity", dbus.bus.NAME_FLAG_ALLOW_REPLACEMENT) # add a handler to listen in Unity dbus messages self.session_bus.add_signal_receiver(self.unity_cb_handler, dbus_interface="com.canonical.Unity.LauncherEntry", signal_name="Update") # we need a Bamf.Matcher for matching windows to running apps self.matcher = None # wait for max 10s to ensure bamf is available # (bamf is not always immediately available after login on Linux Mint # 19 - e.g. https://forums.linuxmint.com/viewtopic.php?t=272747 and # issue #158) i = 0 while i < 10: if (not self.session_bus.name_has_owner("org.ayatana.bamf")): i += 1 sleep(1) else: break self.matcher = Bamf.Matcher() # can we resize nicely on the panel? try: self.applet.set_size_hints([100, 0], 0) self.nice_sizing = True self.ns_base_apps = 4 except TypeError: self.nice_sizing = False self.ns_base_apps = 0 self.drag_x = self.drag_y = -1 self.dragging = False self.hidden_views = [] # instantiate a timer to perform further setup once the applet has been # fully created GObject.timeout_add(1000, self.do_delayed_setup) def __del__(self): """ Clean up ... """ # release the unity bus self.session_bus.release_name("com.canonical.Unity") def do_delayed_setup(self): """ Perform setup operations that we couldn't do until the dock was fully instantiated Get the id of the panel the applet is on Do the initial panel colour change (if necessary) Set the minimise locations of open windows Get the applet window """ self.get_panel_id() # now that we have our panel id, we can set use it to set to panel # colour changer if necessary if self.change_dock_color_only: self.panel_cc.set_single_panel(self.panel_id) else: self.panel_cc.set_single_panel("") # enable panel colour changing? if self.change_panel_color: self.panel_cc.enable_color_change() # we're starting up so need to do an initial panel colour change self.panel_cc.do_change_panel_color() # get info about the panel the applet is on self.get_applet_panel_info() # TODO: we wont need this when applet.set_size_hints and when MATE 1.20 # has been rolled out to all distros... # get the current panel layout self.get_panel_layout() # constrain the dock's size so that it does not overlap any other applets if not build_gtk2: if self.nice_sizing: # set the applet's size hints self.set_size_hints() elif self.panel_layout.upper() == "MUTINY": self.dock_fixed_size = self.get_mutiny_fixed_size() self.avail_panel_space = self.get_avail_panel_space() self.set_dock_panel_size() self.write_settings() else: self.avail_panel_space = self.get_avail_panel_space() self.set_dock_panel_size() self.set_all_apps_minimise_targets() # if panel layout is mutiny and there are no saved settings files # then create one. Workaround for launchpad bug #1755835 # https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1755835?comments=all if self.panel_layout.upper() == "MUTINY": if not os.path.isfile(self.xml_conf): self.write_settings() self.dds_done = True if build_gtk2: applet_win = self.applet.window else: applet_win = self.applet.get_window() for app in self.app_list: app.applet_win = applet_win return False # cancel the timer def get_panel_id(self): """ Get the toplevel id of the panel the applet is on """ # get the info from dconf settings self.panel_id = self.object_settings.get_string("toplevel-id") def get_applet_panel_custom_rgb(self): """ If the panel containing the applet has a custom background colour, return the colour Requires get_applet_panel_info to have been called at least once beforehand Returns: A tuple of 3 x int : the r,g,b components or None if a custom colour is not being used """ settings_path = "/org/mate/panel/toplevels/%s/background/" % self.panel_id # get this panel's background settings psettings = Gio.Settings.new_with_path("org.mate.panel.toplevel.background", settings_path) if psettings.get_string("type") == "none": # colours from the current theme are being used return None else: # get the panel's custom colour rgb components colstr = psettings.get_string("color") if (colstr.startswith("rgba")) or (colstr.startswith("rgb")): colstrip = colstr.strip("rgba()") cols = colstrip.split(",") pr = int(cols[0]) pg = int(cols[1]) pb = int(cols[2]) else: pr = int(colstr[1:3], 16) pg = int(colstr[3:5], 16) pb = int(colstr[5:7], 16) return [pr, pg, pb] def get_panel_layout(self): """ Get the current panel layout from dconf""" psettings = Gio.Settings.new("org.mate.panel") self.panel_layout = psettings.get_string("default-layout") def get_panel_height(self, mypanel, orients): """ Returns the combined heights of the panels with the specified orientations" Args: mypanel - the toplevel id of the panel the applet is on orients - a list of panel orientation we're interested in. Will typically contain "top", "bottom" or both) Returns: The height of the specified panel(s), or 0 is there were no panels found with the specified orientations """ # get this panel's settings plist = Gio.Settings.new("org.mate.panel") panel_heights = 0 toplevels = plist.get_value("toplevel-id-list").unpack() for toplevel in toplevels: if mypanel != toplevel: tl_path = "/org/mate/panel/toplevels/%s/" % toplevel # get this panel's settings tpsettings = Gio.Settings.new_with_path("org.mate.panel.toplevel", tl_path) # get the info panel_orient = tpsettings.get_string("orientation") if panel_orient in orients: panel_heights += tpsettings.get_int("size") return panel_heights def get_applet_panel_info(self): """ Read info from dconf regarding the panel the applet is on, and also the applet's position on the panel Note: the toplevel id of the panel must already have been read """ self.applet_pos = self.object_settings.get_int("position") # get the settings path for the current panel settings_path = "/org/mate/panel/toplevels/%s/" % self.panel_id # get this panel's settings if self.panel_event_handler_id is not None: self.panel_settings.disconnect(self.panel_event_handler_id) try: self.panel_settings = Gio.Settings.new_with_path("org.mate.panel.toplevel", settings_path) except TypeError: # this can happen when the applet quits and it is therefore # safe to ignore return # get the info self.panel_orient = self.panel_settings.get_string("orientation") self.panel_x = self.panel_settings.get_int("x") self.panel_y = self.panel_settings.get_int("y") self.panel_size = self.panel_settings.get_int("size") self.panel_expand = self.panel_settings.get_boolean("expand") # if this is left or right oriented panel we need to account for the # height of any top panel so that minimise locations are correctly # calculated... if (self.panel_orient == "left") or (self.panel_orient == "right"): self.applet_pos += self.get_panel_height(self.panel_id, ["top"]) # finally, connect an event handler so that if this panel's settings # are changed i.e. orientation, size, we can respond to this self.panel_event_handler_id = self.panel_settings.connect("changed", self.panel_settings_changed) def applet_panel_settings_changed(self, settings, key): """ Callback for when the the applet settings with regards to it's panel are changed If the panel the applet is on is changed, update our panel id and recalculate all docked apps minimize positions accordingly If the applet's position on its panel is changed, the minimize positions of all docked apps also need to be minimized """ if key == "toplevel-id": self.get_panel_id() if self.change_dock_color_only: self.panel_cc.set_single_panel(self.panel_id) # remove any scroll indicators and reset the scroll positions if self.panel_id != "": if self.scrolling: self.set_app_scroll_dirs(False) self.reset_scroll_position() if self.scrolling and not self.nice_sizing: self.set_app_scroll_dirs(True) self.get_applet_panel_info() self.set_all_apps_minimise_targets() if not build_gtk2: if not self.nice_sizing: self.avail_panel_space = self.get_avail_panel_space() self.set_dock_panel_size() if key == "position": # we can get here during applet creation when we still might not # know out panel id so... if self.panel_id != "": if not self.nice_sizing: if self.scrolling: self.set_app_scroll_dirs(False) self.reset_scroll_position() self.get_applet_panel_info() self.set_all_apps_minimise_targets() if not build_gtk2: if not self.nice_sizing: self.avail_panel_space = self.get_avail_panel_space() self.set_dock_panel_size() if self.scrolling: self.set_app_scroll_dirs(True) def panel_settings_changed(self, settings, key): """ Callback for the settings of the panel the applet is on changed If the size or orientation of the panel changes, we need to recalculate app minimise locations """ if key in ["orientation", "size", "expand", "x", "x-centered", "y", "y-centered", "x-right", "y-bottom"]: self.get_applet_panel_info() if not build_gtk2: self.set_dock_panel_size() self.set_all_apps_minimise_targets() for app in self.app_list: app.queue_draw() def read_settings(self): """ Read the current dock settings from dconf If this particular dock has not been run before and a settings xml file exists, import the settings and apply them to the new dock """ # is this dock being run for the first time? if self.settings.get_boolean("first-run") is True: # this dock is being run for the first time, so if we have any # saved settings from other docks, import them. Note: prior to # V0.70 the applet presented a dialog asking the user whether or # not to import the previous settings. This dialog has been removed # and the previous settings are now silently imported to prevent # problems when swtiching to the Mutiny layout in Ubuntu # Mate 16.04 xml_settings = dock_xml.read_xml(self.xml_conf) if xml_settings[0] is True: # the settings were read correctly, so set everything up pinned_apps = [] for pinned_app in xml_settings[1]: pinned_apps.append(pinned_app) self.indicator = xml_settings[2] self.show_all_apps = xml_settings[3] self.multi_ind = xml_settings[4] self.use_win_list = xml_settings[5] self.win_from_cur_ws_only = xml_settings[6] self.win_switch_unity_style = xml_settings[7] self.change_panel_color = xml_settings[8] self.change_dock_color_only = xml_settings[9] self.panel_act_list = xml_settings[10] self.active_bg = xml_settings[11] self.fallback_bar_col = [] for col in xml_settings[12]: self.fallback_bar_col.append(col) self.app_spacing = xml_settings[13] self.attention_type = xml_settings[14] self.popup_delay = xml_settings[15] self.pa_configs = xml_settings[16] self.pa_on_all_ws = xml_settings[17] self.dock_fixed_size = xml_settings[18] self.click_action = xml_settings[19] self.theme = xml_settings[20] # now, immediately write the settings to dconf and back to the # config file so the dock can access them configs = self.configs_to_settings() self.settings.set_value("pinned-apps", GLib.Variant('as', pinned_apps)) self.settings.set_int("indicator-type", self.indicator) self.settings.set_boolean("multi-ind", self.multi_ind) self.settings.set_boolean("apps-from-all-workspaces", self.show_all_apps) self.settings.set_boolean("first-run", False) self.settings.set_boolean("use-win-list", self.use_win_list) self.settings.set_boolean("win-from-cur-workspace-only", self.win_from_cur_ws_only) self.settings.set_boolean("win-switch-unity-style", self.win_switch_unity_style) self.settings.set_boolean("change-panel-color", self.change_panel_color) self.settings.set_boolean("change-panel-color-dock-only", self.change_dock_color_only) self.settings.set_boolean("panel-act-list", self.panel_act_list) self.settings.set_int("bg-type", self.active_bg) self.settings.set_value("fallback-bar-col", GLib.Variant('as', self.fallback_bar_col)) self.settings.set_int("app-spacing", self.app_spacing) self.settings.set_int("attention-type", self.attention_type) self.settings.set_int("popup-delay", self.popup_delay) self.settings.set_boolean("pinned-apps-on-all-workspaces", self.pa_on_all_ws) self.settings.set_value("saved-configs", GLib.Variant('as', configs)) self.settings.set_int("dock-fixed-size", self.dock_fixed_size) self.settings.set_int("click-action", self.click_action) self.settings.set_int("theme", self.theme) dock_xml.write_xml(self.xml_conf, pinned_apps, self.indicator, self.show_all_apps, self.multi_ind, self.use_win_list, self.win_from_cur_ws_only, self.win_switch_unity_style, self.change_panel_color, self.change_dock_color_only, self.panel_act_list, self.active_bg, self.fallback_bar_col, self.app_spacing, self.attention_type, self.popup_delay, self.pa_configs, self.pa_on_all_ws, self.dock_fixed_size, self.click_action, self.theme) return # we get here if there was no previous configuration, or the # configuration couldn't be read. # Where the configuration couldn't be read this could be due to an # error or because new versions of the applet have added configuration # options not yet in the user's xml file. To recover, simply assume a # default set of options self.indicator = self.settings.get_int("indicator-type") self.multi_ind = self.settings.get_boolean("multi-ind") self.show_all_apps = self.settings.get_boolean("apps-from-all-workspaces") self.use_win_list = self.settings.get_boolean("use-win-list") self.win_switch_unity_style = self.settings.get_boolean("win-switch-unity-style") self.click_restore_last_active = self.settings.get_boolean("click-restore-last-active") self.change_panel_color = self.settings.get_boolean("change-panel-color") self.change_dock_color_only = self.settings.get_boolean("change-panel-color-dock-only") self.panel_act_list = self.settings.get_boolean("panel-act-list") self.active_bg = self.settings.get_int("bg-type") self.fallback_bar_col = self.settings.get_value("fallback-bar-col").unpack() self.app_spacing = self.settings.get_int("app-spacing") self.attention_type = self.settings.get_int("attention-type") self.popup_delay = self.settings.get_int("popup-delay") self.pa_on_all_ws = self.settings.get_boolean("pinned-apps-on-all-workspaces") self.dock_fixed_size = self.settings.get_int("dock-fixed-size") self.click_action = self.settings.get_int("click-action") self.theme = self.settings.get_int("theme") def configs_to_settings(self): """ Convenience function to convert the current set of saved configs into a form that can to be written to dconf settings i.e. a list of csv strings """ pa_configs = [] for config in self.pa_configs: item = '"' + config[0] + "," + config[1] for loop in range(2, len(config)): item += "," + config[loop] item += '"' pa_configs.append(item) return pa_configs def write_settings(self): """Write the current dock settings. Write a list of all of the currently pinned apps .desktop files Write the indicator type, whether to use multiple indicators, and whether to show unpinned apps from all workspaces etc. Set the first-run indicator to False """ pinned_apps = [] if self.pa_on_all_ws: for dock_app in self.app_list: if dock_app.desktop_file is not None and dock_app.is_pinned: pinned_apps.append(os.path.basename(dock_app.desktop_file)) else: # get the original pinned app configuration from dconf, so # it can be written to the .xml config file. # Doing so allows the pinned app configuration to be restored on # if the user reverts the pa_on_all_ws setting, even if the applet # is deleted from the panel and then add it again pinned_apps = self.settings.get_value("pinned-apps").unpack() pa_configs = self.configs_to_settings() if self.settings: # only save the current set of self.pinned apps if we're not inking pinned apps # to workspaces if self.pa_on_all_ws: self.settings.set_value("pinned-apps", GLib.Variant('as', pinned_apps)) self.settings.set_int("indicator-type", self.indicator) self.settings.set_boolean("multi-ind", self.multi_ind) self.settings.set_boolean("apps-from-all-workspaces", self.show_all_apps) self.settings.set_boolean("win-from-cur-workspace-only", self.win_from_cur_ws_only) self.settings.set_boolean("win-switch-unity-style", self.win_switch_unity_style) self.settings.set_boolean("use-win-list", self.use_win_list) self.settings.set_boolean("change-panel-color", self.change_panel_color) self.settings.set_boolean("change-panel-color-dock-only", self.change_dock_color_only) self.settings.set_boolean("panel-act-list", self.panel_act_list) self.settings.set_int("bg-type", self.active_bg) self.settings.set_boolean("first-run", False) self.settings.set_value("fallback-bar-col", GLib.Variant('as', self.fallback_bar_col)) self.settings.set_int("app-spacing", self.app_spacing) self.settings.set_int("attention-type", self.attention_type) self.settings.set_int("popup-delay", self.popup_delay) self.settings.set_boolean("pinned-apps-on-all-workspaces", self.pa_on_all_ws) self.settings.set_value("saved-configs", GLib.Variant('as', pa_configs)) self.settings.set_int("dock-fixed-size", self.dock_fixed_size) self.settings.set_int("click-action", self.click_action) self.settings.set_int("theme", self.theme) dock_xml.write_xml(self.xml_conf, pinned_apps, self.indicator, self.show_all_apps, self.multi_ind, self.use_win_list, self.win_from_cur_ws_only, self.win_switch_unity_style, self.change_panel_color, self.change_dock_color_only, self.panel_act_list, self.active_bg, self.fallback_bar_col, self.app_spacing, self.attention_type, self.popup_delay, self.pa_configs, self.pa_on_all_ws, self.dock_fixed_size, self.click_action, self.theme) def read_app_match(self): """ Read an xml file which contains a list of apps which are difficult to match with their respective .desktop file. Returns: A list of tuples which containing the following: the app name (as reported by wnck) the app's wm_class (as reported by wnck) the .desktop file to be used by apps whose name or wm_class match the above """ d, f = os.path.split(os.path.abspath(__file__)) results = dock_xml.read_app_xml("%s/app_match.xml" % d) if results[0]: # the file was read successfully return results[1] else: return [] def set_fallback_bar_colour(self): """ Set the colour to be used for drawing bar and other types of indicators when the highlight colour from the theme can't be determined """ r = int(self.fallback_bar_col[0]) / 255 g = int(self.fallback_bar_col[1]) / 255 b = int(self.fallback_bar_col[2]) / 255 docked_app_helpers.fallback_ind_col = [r, g, b] def get_docked_app_by_desktop_file(self, dfname): """ Returns the docked app which has the same destop file name as dfname Params: dfname - the filename of the .desktop file (note: any path is ignored, only the actual file name is taken into account Returns a docked app if a match for dfname is found, None otherwise """ df = os.path.basename(dfname) for app in self.app_list: if app.desktop_file is not None: if os.path.basename(app.desktop_file) == df: return app return None def get_docked_app_by_bamf_app(self, bamf_app): """ Returns the docked_app relating to the running bamf_app Params: bamf_app: the bamf_app of the application Returns a docked_app, or None if it could not be found """ if bamf_app is not None: for app in self.app_list: if app.has_bamf_app(bamf_app): return app return None def get_docked_app_by_bamf_window(self, bamf_win): """ Returns the docked app which owns the specified window Params: bamf_window: the Bamf.Window in Returns a docked app, or None if it could not be found """ if bamf_win is not None: for app in self.app_list: if app.has_bamf_window(bamf_win): return app return None def set_actions_for_app(self, app): """Show or hide actions in the context menu and action list so that only relevant ones are shown for the specified app If the app is pinned, do not show the pin actions. If the app in not pinned, don't show the unpin actions. Depending on applet orientation actions to move the app left/right or up/down along the dock need to shown or hidden. If the app is the first or last on the dock, then the options to move it up/left or down/right will also need to be hidden Include the app name in the menu text e.g. Pin Caja to the dock If the app has more than one window on screen, show actions allowing the user to select one If the app is running and has one or more windows on screen, show an option allowing them to be closed If the app does not have a desktop file, show an option allowing a custom launcher to be created Show any right click options specified in the app's desktop file Args: app : The DockedApp """ def set_vis(action, vis): """ convenience function to set an action's visibilty Args: action : the action vis : boolean - True if the action is to be visible, False otherwise """ action.set_visible(vis) # hide all actions which can appear either in the panel right click menu or popop action list # # they'll be shown again later depending on which is being used... # if we have have no app, set all actions invisible and exit... panel_act_visible = self.panel_act_list and (app is not None) popup_act_visible = (not self.panel_act_list) and (app is not None) # first, hide actions defined in the desktop file... act_no = 1 while act_no <= self.max_num_actions: act = self.dock_action_group.get_action("df_shortcut_%d_action" % act_no) set_vis(act, panel_act_visible) act_no += 1 # now do pin and unpin actions act = self.dock_action_group.get_action("pin_action") set_vis(act, panel_act_visible) act = self.dock_action_group.get_action("unpin_action") set_vis(act, panel_act_visible) act = self.popup_action_group.get_action("pin_action") set_vis(act, popup_act_visible) act = self.popup_action_group.get_action("unpin_action") set_vis(act, popup_act_visible) close_win_action = self.dock_action_group.get_action("close_win_action") close_win_action.set_visible(False) # TODO: decide - do we need ccl anymore??? ccl_action = self.dock_action_group.get_action("ccl_action") ccl_action.set_visible(False) if app is None: return # now setup the relevant actions panel/action list items act_no = 1 while act_no <= self.max_num_actions: if self.panel_act_list: df_shortcut_action = self.dock_action_group.get_action("df_shortcut_%d_action" % act_no) # df_shortcut_1_action = self.dock_action_group.get_action("df_shortcut_1_action") else: df_shortcut_action = self.popup_action_group.get_action("df_shortcut_%d_action" % act_no) # df_shortcut_1_action = self.popup_action_group.get_action("df_shortcut_1_action") act_exists, act_name = app.get_rc_action(act_no) df_shortcut_action.set_visible(act_exists) if act_exists is True: df_shortcut_action.set_label(act_name) df_shortcut_action.set_icon_name(app.icon_name) act_no += 1 if self.panel_act_list: pin_action = self.dock_action_group.get_action("pin_action") unpin_action = self.dock_action_group.get_action("unpin_action") else: pin_action = self.popup_action_group.get_action("pin_action") unpin_action = self.popup_action_group.get_action("unpin_action") # pin/unpin actions don't appear when we don't have a .desktop file... if not app.has_desktop_file(): pin_action.set_visible(False) unpin_action.set_visible(False) else: pin_action.set_visible(not app.is_pinned) unpin_action.set_visible(app.is_pinned) if pin_action.is_visible(): pin_action.set_label(_("Pin %s") % app.app_name) else: unpin_action.set_label(_("Unpin %s") % app.app_name) move_up_action = self.dock_action_group.get_action("move_up_action") move_down_action = self.dock_action_group.get_action("move_down_action") move_left_action = self.dock_action_group.get_action("move_left_action") move_right_action = self.dock_action_group.get_action("move_right_action") index = self.get_app_position_in_dock(app) orientation = self.applet.get_orient() if orientation == MatePanelApplet.AppletOrient.LEFT or \ orientation == MatePanelApplet.AppletOrient.RIGHT: move_up_action.set_visible(index > 0) move_down_action.set_visible(index < (len(self.box.get_children())) - 1) move_left_action.set_visible(False) move_right_action.set_visible(False) move_up_action.set_label(_("Move %s up the dock") % app.app_name) move_down_action.set_label(_("Move %s down the dock") % app.app_name) else: move_up_action.set_visible(False) move_down_action.set_visible(False) move_left_action.set_visible(index > 0) move_right_action.set_visible(index < (len(self.box.get_children())) - 1) move_left_action.set_label(_("Move %s to the left on the dock") % app.app_name) move_right_action.set_label(_("Move %s to the right on the dock") % app.app_name) # set the actions for selecting specific windows num_win = app.get_num_windows() if num_win == 1: close_win_action.set_label(_("Close %s") % app.app_name) else: close_win_action.set_label(_("Close all windows")) if num_win > 0: close_win_action.set_visible(True) # ccl_action.set_visible(not app.has_desktop_file()) # now setup the actions which can appear in either the right click menu or # action list, depending on which is being used def setup_menu(self): """Set up the actions and right click menu for the applet. Also setup the actions for the popup action list """ # actions named df_shortcut__action are used for implementing # shortcuts/actions specified in an app's .desktop file self.dock_action_group = Gtk.ActionGroup("DockActions") # first define actions for all of the possible app actions shortcut_action_no = 1 shortcut_action_list = [] while shortcut_action_no <= self.max_num_actions: shortcut_name = "df_shortcut_%d_action" % shortcut_action_no shortcut_action_list.append([shortcut_name, None, shortcut_name, None, shortcut_name, None]) shortcut_action_no += 1 self.dock_action_group.add_actions(shortcut_action_list) # set the activate handler for each of the actions shortcut_action_no = 1 while shortcut_action_no <= self.max_num_actions: the_action = self.dock_action_group.get_action("df_shortcut_%d_action" % shortcut_action_no) shortcut_action_no += 1 the_action.connect("activate", self.df_shortcut_handler, shortcut_action_no) # add the rest of the actions to the dock self.dock_action_group.add_actions([ ("pin_action", Gtk.STOCK_ADD, _("_Pin app to the dock"), None, _("Pin app to the dock"), self.pin_app), ("unpin_action", Gtk.STOCK_REMOVE, _("_Unpin app from the dock"), None, _("Unpin app from the dock"), self.unpin_app), ("move_up_action", Gtk.STOCK_GO_UP, _("Move app _up the dock"), None, _("Move app up the dock"), self.move_app_up), ("move_down_action", Gtk.STOCK_GO_DOWN, _("Move app _down the dock"), None, _("Move app down the dock"), self.move_app_down), ("move_left_action", Gtk.STOCK_GO_BACK, _("Move app _left in the dock"), None, _("Move app left in the dock"), self.move_app_up), ("move_right_action", Gtk.STOCK_GO_FORWARD, _("Move app _right in the dock"), None, _("Move app right in the dock"), self.move_app_down), ("prefs_action", Gtk.STOCK_PREFERENCES, _("Dock P_references"), None, _("Dock Preferences"), self.show_prefs_win), ("ccl_action", Gtk.STOCK_EXECUTE, _("Create custo_m launcher for this app"), None, _("Create custom launcher for this app"), self.show_ccl_win), ("about_action", Gtk.STOCK_ABOUT, _("About..."), None, _("About..."), self.show_about_win), ("close_win_action", Gtk.STOCK_CLOSE, _("_Close"), None, _("Close"), self.close_win) ]) shortcut_action_no = 1 menu_xml = "" while shortcut_action_no <= self.max_num_actions: menu_xml += '' % (shortcut_action_no, shortcut_action_no) shortcut_action_no += 1 menu_xml += '' # we only need menu items for moving app icons for Gtk2 # (Gtk3 does it with drag and drop) if build_gtk2: menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' menu_xml += '' self.applet.setup_menu(menu_xml, self.dock_action_group) # setup the action list items - pin/unpin options plus actions defined in the # desktop file. self.popup_action_group = Gtk.ActionGroup("PopupActions") self.popup_action_group.add_actions(shortcut_action_list) # set the activate handler for each of the actions shortcut_action_no = 1 while shortcut_action_no <= self.max_num_actions: the_action = self.popup_action_group.get_action("df_shortcut_%d_action" % shortcut_action_no) shortcut_action_no += 1 the_action.connect("activate", self.df_shortcut_handler, shortcut_action_no) self.popup_action_group.add_actions([ ("pin_action", Gtk.STOCK_ADD, _("Pin app to the dock"), None, _("Pin app to the dock"), self.pin_app), ("unpin_action", Gtk.STOCK_REMOVE, _("_Unpin app from the dock"), None, _("Unpin app from the dock"), self.unpin_app) ]) def df_shortcut_handler(self, action, action_no): """ Perform one of the currently selected app's shortcut actions Params: action : the Gtk.Action that was activated action_no : the number of the action to perform """ if self.panel_act_list: the_app = self.right_clicked_app else: if self.app_act_list.get_visible(): the_app = self.app_act_list.the_app else: the_app = self.right_clicked_app if the_app is not None: the_app.run_rc_action(action_no - 1) def update_pinned_app_config(self): """ Updates the pinned app configuration data for the current workspace from self.app_list Should be called when apps are pinned/unpinned, change positions on the dock etc. """ ws = self.wnck_screen.get_active_workspace() if ws is not None: ws_name = ws.get_name() # get the list of currently pinned apps from self.app_list app_list = [] for app in self.app_list: if app.is_pinned: app_list.append(os.path.basename(app.desktop_file)) # update the config with the new list newconf = [] index = 0 for config in self.pa_configs: if config[1] == ws_name: # copy the config name and workspace newconf = [config[0], config[1]] for app in app_list: newconf.append(app) self.pa_configs.remove(config) self.pa_configs.append(newconf) break index += 1 if newconf == []: # this is a new configuration, so append it to the list # (config name can be the same as the workspace name for now) newconf = [ws_name, ws_name] for app in app_list: newconf.append(app) self.pa_configs.append(newconf) def unpin_app(self, data=None): """Unpin an app from the dock This action is performed from the action list or the pane; right click menu Unpin the app and update the dock settings. If the app is not running, remove it from the dock also """ # get the app in question if self.panel_act_list: the_app = self.right_clicked_app else: if self.app_act_list.get_visible(): the_app = self.app_act_list.the_app else: the_app = self.right_clicked_app if the_app is not None: # get the index of the app in self.app list app_index = self.app_list.index(the_app) the_app.is_pinned = False if not the_app.is_running(): self.remove_app_from_dock(the_app) self.set_all_apps_minimise_targets() self.right_clicked_app = None # if we're pinning apps to specific workspaces, remove the app from # the workspace its pinned to if not self.pa_on_all_ws: self.update_pinned_app_config() # if the app is running, but has no windows on the current workspace # it needs to be removed from the dock if the_app.is_running(): cur_ws = self.wnck_screen.get_active_workspace() if not the_app.has_windows_on_workspace(cur_ws): self.remove_app_from_dock(the_app) self.set_all_apps_minimise_targets() self.right_clicked_app = None # maintain a reference to the unpin notification... self.notification = Notify.Notification.new(_("%s unpinned") % the_app.app_name) self.notification.set_icon_from_pixbuf(the_app.app_pb) self.notification.add_action("action_click", _("Undo"), self.notify_cb, [the_app, app_index]) self.notification.show() self.write_settings() def notify_cb(self, notification, action, app_data): """ Callback for 'Unpin' notification Pin the app back onto the dock in the same position and workspace as it was unpinned from Params: notification: the notification action: the action performed i.e. mouse click app_data : a tuple containing the docked_app that was unpinned, and the index in self.app_list from which the app was unpinned """ the_app = app_data[0] # is the app still in the dock? app_in_dock = False for app in self.app_list: if app == the_app: app_in_dock = True break the_app.is_pinned = True if not app_in_dock: # the app is not in the dock, so add it again and then move to the required # position self.app_list.append(the_app) self.add_app(the_app) self.move_app(the_app, app_data[1]) notification = Notify.Notification.new(_("%s re-pinned") % the_app.app_name) notification.set_icon_from_pixbuf(the_app.app_pb) notification.show() # write settings... if not self.pa_on_all_ws: self.update_pinned_app_config() self.write_settings() def pin_app(self, data=None): """Pin an app to the dock. Pin the app and update the dock settings""" # get the app in question if self.panel_act_list: the_app = self.right_clicked_app else: if self.app_act_list.get_visible(): the_app = self.app_act_list.the_app else: the_app = self.right_clicked_app if the_app is not None: the_app.is_pinned = True # if we're pinning apps to specific workspaces, add the app to # the current workspaces saved configuration if not self.pa_on_all_ws: self.update_pinned_app_config() self.write_settings() def get_app_position_in_dock(self, app): """ Get the position of a specified app in the dock. Args : app - A DockedApp Returns : the index of the app, or -1 if it wasn't found """ index = 0 hidden = 0 for app_da in self.box.get_children(): if app_da == app.drawing_area: if build_gtk2: return index else: if self.box.orientation == Gtk.Orientation.HORIZONTAL: boxi = self.box.child_get_property(app_da, "left-attach") else: boxi = self.box.child_get_property(app_da, "top_attach") return boxi index += 1 return -1 def move_app_up(self, data=None): """ Move the right clicked app up one position on the dock (or left if the panel is on the top or bottom of the screen). Moves the app and then recaculates the minimize location for it's windows. Writes the dock settings once all is done. """ if self.right_clicked_app is not None: index = self.get_app_position_in_dock(self.right_clicked_app) if index > 0: app = self.app_list[index - 1] # we need to move the app both in self.applist and self.box if build_gtk2: self.box.reorder_child(self.right_clicked_app.drawing_area, index - 1) else: if self.box.orientation == Gtk.Orientation.HORIZONTAL: prop = "left-attach" else: prop = "top-attach" self.box.child_set_property(self.right_clicked_app.drawing_area, prop, index - 1) self.box.child_set_property(app.drawing_area, prop, index) self.app_list[index - 1] = self.app_list[index] self.app_list[index] = app # allow Gtk to perform the move while Gtk.events_pending(): Gtk.main_iteration() # recalculate the minimize targets for each app self.set_minimise_target(self.app_list[index - 1]) self.set_minimise_target(self.app_list[index]) if not self.pa_on_all_ws: self.update_pinned_app_config() self.write_settings() def move_app(self, the_app, new_pos): """ Move a docked app to a new position in the dock, adjusting the the positions of other apps as necessary This is used during drag and drop operatations and when repinning unpinned apps in response to notifications Args: the_app : the docked_app we're moving new_pos : int, the new position in the docked """ old_pos = self.app_list.index(the_app) if self.scrolling: self.set_app_scroll_dirs(False) # first move the app's drawing area if build_gtk2: self.box.reorder_child(the_app.drawing_area, new_pos) else: if self.box.orientation == Gtk.Orientation.HORIZONTAL: prop = "left-attach" else: prop = "top-attach" if new_pos > old_pos: step = 1 else: step = -1 i = old_pos + step # first, adjust the contents of the box containing the app drawing areas while i != new_pos + step: app_to_move = self.app_list[i] self.box.child_set_property(app_to_move.drawing_area, prop, i - step) i += step # move the desired app's drawing area self.box.child_set_property(the_app.drawing_area, prop, new_pos) # now move things around in the app list to match self.app_list.remove(the_app) self.app_list.insert(new_pos, the_app) # allow Gtk toperform the move while Gtk.events_pending(): Gtk.main_iteration() if self.scrolling: self.set_app_scroll_dirs(True) # we need to redraw the icons and recalculate the minimise positions # of all apps for app in self.app_list: app.queue_draw() self.set_all_apps_minimise_targets() # save the new settings if not self.pa_on_all_ws: self.update_pinned_app_config() self.write_settings() def get_app_root_coords(self, app): """ Calculate and return the root x and y co-ordinates of the top left pixel of a docked app Args: app: the docked app Returns: two integers, the x and y coordinates """ dock_x, dock_y = self.get_dock_root_coords() x, y, w, h = app.get_allocation() dock_x += x dock_y += y return dock_x, dock_y def get_dock_root_coords(self): """ Get the root coords of the top left pixel of the dock Returns: two integers, the x and y coordinates """ # get root coord from the applet window rather from panel settings... win = self.applet.props.window # check validity of win - can be None during applet creation... if win is None: return 0, 0 if build_gtk2: # win.get_origin doesn't work on gtk2, so... dock_x, dock_y = win.get_root_coords(0, 0) else: thing, dock_x, dock_y = win.get_origin() return dock_x, dock_y def set_minimise_target(self, app, win=None): """ Calculate and set the minimise locations for an app's windows, or just for a single window Args: app: the docked_app win : a single window which needs its minimise location set """ min_x, min_y = self.get_app_root_coords(app) # its more visually appealing if we minimize to the centre of the app's # icon, so reduce the size of the minimize areas and adjust the # coordinates ... adj = app.drawing_area_size / 4 min_x += adj min_y += adj app_w = app_h = app.drawing_area_size - (adj * 2) if win is None: app.set_all_windows_icon_geometry(min_x, min_y, app_w, app_h) else: window_control.set_minimise_target(win, min_x, min_y, app_w, app_h) def set_all_apps_minimise_targets(self): """ Calculate and set the window minimise locations for all app's """ for app in self.app_list: self.set_minimise_target(app) def move_app_down(self, data=None): """ Move the right clicked app down one position on the dock (or right if the panel is on the top or bottom of the screen). Moves the app and then recaculates the minimize location for it's windows. Writes the dock settings once all is done. """ if self.right_clicked_app is not None: index = self.get_app_position_in_dock(self.right_clicked_app) if index < len(self.box.get_children()) - 1: app = self.app_list[index + 1] # we need to move the app both in self.applist and self.box if build_gtk2: self.box.reorder_child(self.right_clicked_app.drawing_area, index + 1) else: if self.box.orientation == Gtk.Orientation.HORIZONTAL: prop = "left-attach" else: prop = "top-attach" self.box.child_set_property(self.right_clicked_app.drawing_area, prop, index + 1) self.box.child_set_property(app.drawing_area, prop, index) self.app_list[index + 1] = self.app_list[index] self.app_list[index] = app # allow Gtk to move perform the move while Gtk.events_pending(): Gtk.main_iteration() # recalculate the minimize targets for each app self.set_minimise_target(self.app_list[index + 1]) self.set_minimise_target(self.app_list[index]) if not self.pa_on_all_ws: self.update_pinned_app_config() self.write_settings() def show_prefs_win(self, data=None): """ Show the preferences window. If, necessary create the window and register a callback for the 'ok' button press If the window has already been shown, just show it again. """ if self.prefs_win is None: self.prefs_win = dock_prefs.DockPrefsWindow(self.prefs_win_ok_cb, self.app_list[0]) self.prefs_win.set_indicator(self.indicator) self.prefs_win.set_multi_ind(self.multi_ind) self.prefs_win.set_show_unpinned_apps_on_all_ws(self.show_all_apps) self.prefs_win.set_click_action(self.click_action) self.prefs_win.set_change_panel_color(self.change_panel_color) self.prefs_win.set_change_dock_color_only(self.change_dock_color_only) self.prefs_win.set_pan_act(self.panel_act_list) self.prefs_win.set_win_cur_ws_only(self.win_from_cur_ws_only) self.prefs_win.set_win_switch_unity_style(self.win_switch_unity_style) self.prefs_win.set_bg(self.active_bg) self.prefs_win.set_fallback_bar_col(self.fallback_bar_col) self.prefs_win.set_app_spacing(self.app_spacing) self.prefs_win.set_attention_type(self.attention_type) self.prefs_win.set_popup_delay(self.popup_delay) self.prefs_win.set_show_pinned_apps_on_all_ws(self.pa_on_all_ws) self.prefs_win.set_theme(self.theme) if not build_gtk2 and not self.nice_sizing: self.prefs_win.set_fixed_size(self.dock_fixed_size != -1, self.dock_fixed_size, self.panel_layout.upper() == "MUTINY") else: self.prefs_win.show_all() if self.nice_sizing: # we don't need the dock size options self.prefs_win.set_dock_size_visible(False) def show_about_win(self, data=None): """ Show the About window. If, necessary create the window and show it. If the window has already been shown, just show it again. """ if self.about_win is None: self.about_win = dock_about.AboutWindow() self.about_win.show_all() def prefs_win_ok_cb(self, widget, event): """ Callback for the 'ok' button on the preferences window. If the preferences have been changed then: write the new settings redraw each running app in app_list with the new indicator type Args: widget - the button the caused the event event - the event args """ if not build_gtk2: if self.panel_layout.upper() == "MUTINY": fixed_size_changes = False else: prefs_fixed_size, prefs_num_icons = self.prefs_win.get_fixed_size() if not prefs_fixed_size: prefs_num_icons = -1 # indicate a varaible size fixed_size_changes = prefs_num_icons != self.dock_fixed_size else: fixed_size_changes = False if (self.indicator != self.prefs_win.get_indicator_type()) or \ (self.multi_ind != self.prefs_win.get_multi_ind()) or \ (self.show_all_apps != self.prefs_win.get_show_unpinned_apps_on_all_ws()) or \ (self.win_from_cur_ws_only != self.prefs_win.get_win_cur_ws_only()) or \ (self.win_switch_unity_style != self.prefs_win.get_win_switch_unity_style()) or \ (self.click_action != self.prefs_win.get_click_action()) or \ (self.change_panel_color != self.prefs_win.get_change_panel_color()) or \ (self.change_dock_color_only != self.prefs_win.get_change_dock_color_only()) or \ (self.panel_act_list != self.prefs_win.get_pan_act()) or \ (self.active_bg != self.prefs_win.get_bg()) or \ (self.fallback_bar_col != self.prefs_win.get_fallback_bar_col()) or \ (self.app_spacing != self.prefs_win.get_app_spacing()) or \ (self.attention_type != self.prefs_win.get_attention_type()) or \ (self.popup_delay != self.prefs_win.get_popup_delay()) or \ (self.pa_on_all_ws != self.prefs_win.get_show_pinned_apps_on_all_ws()) or \ (self.theme != self.prefs_win.get_theme()) or \ fixed_size_changes: old_ind = self.indicator old_bg = self.active_bg self.indicator = self.prefs_win.get_indicator_type() self.multi_ind = self.prefs_win.get_multi_ind() self.show_all_apps = self.prefs_win.get_show_unpinned_apps_on_all_ws() self.win_from_cur_ws_only = self.prefs_win.get_win_cur_ws_only() self.win_switch_unity_style = self.prefs_win.get_win_switch_unity_style() self.click_action = self.prefs_win.get_click_action() self.app_spacing = self.prefs_win.get_app_spacing() self.attention_type = self.prefs_win.get_attention_type() self.popup_delay = self.prefs_win.get_popup_delay() new_panel_color_setting = self.change_panel_color != self.prefs_win.get_change_panel_color() self.change_panel_color = self.prefs_win.get_change_panel_color() self.change_dock_color_only = self.prefs_win.get_change_dock_color_only() if self.change_dock_color_only: self.panel_cc.set_single_panel(self.panel_id) else: self.panel_cc.set_single_panel("") if self.panel_act_list != self.prefs_win.get_pan_act(): self.panel_act_list = self.prefs_win.get_pan_act() old_bg = self.active_bg self.active_bg = self.prefs_win.get_bg() self.theme = self.prefs_win.get_theme() # if we're changing to or from a Unity background we need # to reload docked app icons unity_bg_types = [docked_app_helpers.IconBgType.UNITY, docked_app_helpers.IconBgType.UNITY_FLAT] if (old_bg in unity_bg_types and self.active_bg not in unity_bg_types) or \ (old_bg not in unity_bg_types and self.active_bg in unity_bg_types): size = self.applet.get_size() for app in self.app_list: self.set_app_icon(app, size) self.fallback_bar_col = self.prefs_win.get_fallback_bar_col() self.set_fallback_bar_colour() if self.pa_on_all_ws != self.prefs_win.get_show_pinned_apps_on_all_ws(): self.pa_on_all_ws = self.prefs_win.get_show_pinned_apps_on_all_ws() self.clear_dock_apps() self.setup_app_list() self.setup_dock_apps() self.set_all_apps_minimise_targets() if fixed_size_changes: if self.scrolling: self.set_app_scroll_dirs(False) self.reset_scroll_position() self.dock_fixed_size = prefs_num_icons self.set_dock_panel_size() self.write_settings() # redraw everything here if build_gtk2: self.box.set_spacing(self.app_spacing + 2) else: self.box.set_row_spacing(self.app_spacing + 2) self.box.set_column_spacing(self.app_spacing + 2) if old_ind != self.indicator: # if the new indicator requires a different amount of space than the # old one did then we need to remove all of the drawing areas from # the box/grid, set new size requests for each app and then re-add them if docked_app_helpers.ind_extra_s(self.indicator) != docked_app_helpers.ind_extra_s(old_ind): size = self.applet.get_size() for app in self.app_list: self.box.remove(app.drawing_area) app.set_indicator(self.indicator) app.set_drawing_area_size(size) # request a new size for app in self.app_list: self.add_app(app) # add the drawing area back to the box else: # if there is no size difference, just set the new indicator for app in self.app_list: app.set_indicator(self.indicator) for app in self.app_list: app.set_multi_ind(self.multi_ind) app.set_active_bg(self.active_bg) app.set_attention_type(self.attention_type) app.queue_draw() self.show_or_hide_app_icons() self.show_or_hide_indicators() if new_panel_color_setting: # panel colour changing setting has been changed so we need to # enable or disable colour changing if self.change_panel_color: self.panel_cc.enable_color_change() self.panel_cc.do_change_panel_color() else: self.panel_cc.disable_color_change() self.prefs_win.hide() def show_ccl_win(self, data=None): """ Show the create custom launcher window. If, necessary create the window and register a callback for the 'ok' button press If the window has already been shown, clear all of the fields before showing it """ if self.ccl_win is None: self.ccl_win = dock_custom_launcher.DockCLWindow(self.ccl_win_ok_cb) else: self.ccl_win.set_default_values() self.ccl_win.name = self.right_clicked_app.app_name.strip() self.ccl_win.wm_class = self.right_clicked_app.wm_class_name self.ccl_win.show_all() def ccl_win_ok_cb(self, widget, event): """ Callback for the 'ok' button on the create custom launcher window. Check to ensure that all required fields (icon, launcher name and command) have been entered and display an error dialog if not. If all required fields have been entered, use the info from the window to create a .desktop file in ~/.local/share/applications The .desktop file will be named mda_.desktop - the initial 'mda_' will allow the applet to search for and priorities self created .desktop files over system created ones... Args: widget - the button the caused the event event - the event args """ valid_launcher = False if self.ccl_win.name == "": error_text = _("The name of the launcher has not been set") elif self.ccl_win.command == "": error_text = _("The command of the launcher has not been set") elif self.ccl_win.icon_filename == "": error_text = _("The icon of the launcher has not been set") else: valid_launcher = True if valid_launcher is False: md = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, None) md.set_markup('' + _("Cannot create launcher") + '') md.format_secondary_text(error_text) md.run() md.destroy() return else: self.ccl_win.hide() # the gnome developer docs at # https://developer.gnome.org/integration-guide/stable/desktop-files.html.en # state that .desktop filenames should not contain spaces, so.... dfname = self.ccl_win.name.replace(" ", "-") local_apps = os.path.expanduser("~/.local/share/applications") if not os.path.exists(local_apps): # ~/.local/share/applications doesn't exist, so create it os.mkdir(local_apps) dfname = os.path.expanduser("%s/mda-%s.desktop" % (local_apps, dfname)) dfile = open(dfname, "w") dfile.write("[Desktop Entry]\n") dfile.write("Name=%s\n" % self.ccl_win.name) dfile.write("Type=Application\n") dfile.write("Comment=%s\n" % self.ccl_win.comment) dfile.write("Exec=%s\n" % self.ccl_win.command) dfile.write("Icon=%s\n" % self.ccl_win.icon_filename) dfile.write("StartupWMClass=%s\n" % self.ccl_win.wm_class) # Code below can be uncommented if adding terminal apps to the dock # everbecomes a needed thing # term_app = "%s" %self.ccl_win.is_terminal_app # dfile.write("Terminal=%s\n" %term_app.lower()) # we don't want this launcher displayed in the MATe menu dfile.write("NoDisplay=true\n") dfile.close() # create a docked app from the .desktop we just created and add it # to the dock dock_app = docked_app.DockedApp() dock_app.desktop_file = dfname dock_app.read_info_from_desktop_file() dock_app.is_pinned = True if build_gtk2: dock_app.applet_win = self.applet.window else: dock_app.applet_win = self.applet.window.get_window() dock_app.applet = self.applet dock_app.applet_orient = self.applet.get_orient() dock_app.set_indicator(self.indicator) dock_app.set_multi_ind(self.multi_ind) dock_app.set_active_bg(self.active_bg) dock_app.set_attention_type(self.attention_type) size = self.applet.get_size() self.set_app_icon(dock_app, size) self.app_list.append(dock_app) self.add_app(dock_app) self.show_or_hide_app_icons() self.show_or_hide_indicators() self.write_settings() def add_app_to_dock(self, desktop_file): """ Adds the app specified by a desktop file to the dock and pins it If the app is already present in the dock, no action is taken :param desktop_file: the .desktop_file of the app """ for app in self.app_list: if app.desktop_file == desktop_file: return dock_app = docked_app.DockedApp() dock_app.desktop_file = desktop_file dock_app.read_info_from_desktop_file() if dock_app.read_info_from_desktop_file(): if build_gtk2: dock_app.applet_win = self.applet.window else: dock_app.applet_win = self.applet.get_window() dock_app.applet = self.applet dock_app.applet_orient = self.applet.get_orient() size = self.applet.get_size() self.set_app_icon(dock_app, size) self.app_list.append(dock_app) self.add_app(dock_app) if self.show_all_apps: dock_app.show_icon() else: self.show_or_hide_app_icons() self.show_or_hide_indicators() self.set_all_apps_minimise_targets() dock_app.applet_orient = self.applet.get_orient() dock_app.set_indicator(self.indicator) dock_app.set_multi_ind(self.multi_ind) dock_app.set_active_bg(self.active_bg) dock_app.set_attention_type(self.attention_type) dock_app.is_pinned = True self.write_settings() return if build_gtk2: dock_app.applet_win = self.applet.window else: dock_app.applet_win = self.applet.get_window() dock_app.applet = self.applet dock_app.applet_orient = self.applet.get_orient() dock_app.set_indicator(self.indicator) dock_app.set_multi_ind(self.multi_ind) dock_app.set_active_bg(self.active_bg) dock_app.set_attention_type(self.attention_type) size = self.applet.get_size() self.set_app_icon(dock_app, size) self.app_list.append(dock_app) self.add_app(dock_app) self.show_or_hide_app_icons() self.show_or_hide_indicators() self.set_all_apps_minimise_targets() self.write_settings() def show_win(self, win_no): """ Bring the specified window number of the right clicked app to the front Args: win_no - the window number, starting at 1 """ win_list = self.right_clicked_app.get_windows() win = win_list[win_no - 1] window_control.activate(win) def close_win(self, data=None): """Close all windows for the right clicked app""" win_list = self.right_clicked_app.get_windows() for win in win_list: window_control.close_win(win) def icon_theme_changed(self, icontheme): """ Callback for when the Gtk icon theme changes Load the new icon set Iterate through each app in self.app_list and get it to reload it's icon """ self.icontheme.rescan_if_needed() size = self.applet.get_size() for app in self.app_list: self.set_app_icon(app, size) def find_desktop_file(self, df_name): """ Find the full filename of a specified .desktop file Search the following directories (and their subdirectories) for the specified filename /usr/share/applications /usr/local/share/applications /var/lib/snapd/desktop/applications ~/.local/share/applications Args : df_name : the name of the .desktop file e.g. pluma.desktop. The .desktop extension must be included Returns: The full filename (path + filename) of the desktop file if it exists or "" otherwise """ srch_dirs = ["/usr/share/applications/", "/usr/local/share/applications/", "/var/lib/snapd/desktop/applications/", os.path.expanduser("~/.local/share/applications/")] for srch_dir in srch_dirs: for the_dir, dir_list, file_list in os.walk(srch_dir): try: unused_var = file_list.index(df_name) # if we get here the file is found the_name = os.path.join(the_dir, df_name) return the_name except ValueError: pass return "" def setup_app_list(self): """Setup the list of docked apps. If pinned apps are pinned to all workspaces read the list of pinned apps from the settings and add them to the app list, otherwise get the current workspace and load the config assigned for it, if any Then iterate through the running apps, and then either: if this is a non-pinned app add it to app list if this is a pinned app, integrate the running app info with the pinned app details already set up Also, set up event handlers allowing us keep track of window added and removed events, pluse change of active workspace """ self.wnck_screen.force_update() # recommended per Wnck documentation self.app_list = [] if self.pa_on_all_ws: pinned_apps = self.settings.get_value("pinned-apps").unpack() # the settings contain a list of .desktop files, so we need to find and # read each file else: pinned_apps = [] cur_ws = self.wnck_screen.get_active_workspace() if cur_ws is not None: ws_name = cur_ws.get_name() for config in self.pa_configs: if ws_name == config[1]: for loop in range(2, len(config)): pinned_apps.append(config[loop]) for pinned_app in pinned_apps: dock_app = docked_app.DockedApp() full_name = self.find_desktop_file(pinned_app) if full_name != "": dock_app.desktop_file = full_name if dock_app.read_info_from_desktop_file(): b_app = self.matcher.get_application_for_desktop_file(full_name, True) dock_app.set_bamf_app(b_app) self.app_list.append(dock_app) dock_app.is_pinned = True # unpinned apps - get a list of all running apps and if an app is not already in the dock # and if it is an app (and not e.g. a panel...) then add it to the dock for b_app in self.matcher.get_running_applications(): if (self.get_docked_app_by_bamf_app(b_app) is None) and b_app.is_user_visible(): # we need to examine all the app's windows - if any of them are Normal/Dialogs the app needs to be # added to the dock add_to_dock = False for b_win in b_app.get_windows(): if (b_win.get_window_type() == Bamf.WindowType.NORMAL) or \ (b_win.get_window_type() == Bamf.WindowType.DIALOG) and b_win.is_user_visible(): add_to_dock = True break if add_to_dock: dock_app = docked_app.DockedApp() dock_app.set_bamf_app(b_app) dock_app.desktop_file = b_app.get_desktop_file() if dock_app.desktop_file is not None: if dock_app.read_info_from_desktop_file(): self.app_list.append(dock_app) else: # bamf cannot match the app, so get as much info about it as we can # e.g. the icon, and use that ... dock_app.setup_from_bamf(self.app_match) self.app_list.append(dock_app) # for all the apps we have, setup signal handlers for app in self.app_list: # connect signal handlers so that we detect windows being added and removed from the Bamf.App b_app = app.bamf_app self.set_bamf_app_handlers(b_app) # for each window the app has open, connect workspace changed events for win in app.get_windows(): win_type = win.get_window_type() if (win_type == Bamf.WindowType.NORMAL) or (win_type == Bamf.WindowType.DIALOG): wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is not None: wnck_win.connect("workspace-changed", self.window_ws_changed) def clear_dock_apps(self): """ Clear out the current list of apps, pinned and unpinned, restoring the dock to an empty state :return: """ a1 = self.app_list.copy() for dock_app in a1: self.remove_app_from_dock(dock_app) def active_workspace_changed(self, wnck_screen, previously_active_space): """ Event handler for the active workspace change even Load a saved pinned app config for the new workspace if appropriate Show or hide pinned and unpinned dock apps as appropriate Arguments : wnck_screen : the screen that emitted the event. Will be the same as self.wnck_screen previously_active_space : the workspace that was previously active """ # get the name of the new workspace ws = wnck_screen.get_active_workspace() if ws is None: return ws_name = ws.get_name() update_dock = False # if we're using a different dock if not self.pa_on_all_ws: # we're using different configurations on pinned apps on each workspace # so first, clear out the current set of apps self.clear_dock_apps() # setup the new dock for this workspace self.setup_app_list() self.setup_dock_apps() update_dock = True if not self.show_all_apps: update_dock = True if update_dock: self.show_or_hide_app_icons() self.show_or_hide_indicators() self.set_all_apps_minimise_targets() def active_app_changed(self, matcher, object, p0): """ Handler of the active app changed signal Set the old docked app as inactive, the new one as active and redraw both Params: matcher: the Bamf.Matcher which received the event - will be the same as self.matcher object : the previously active Bamf.Application p0 : the new active Bamf.Application """ if object is not None: old_app = self. get_docked_app_by_bamf_app(object) if old_app is not None: old_app.is_active = False old_app.queue_draw() if p0 is not None: new_app = self.get_docked_app_by_bamf_app(p0) if new_app is not None: new_app.is_active = True new_app.queue_draw() def set_bamf_app_handlers(self, b_app): """ Set up signal handlers for a Bamf.Application Params: b_app - the Bamf.Application """ if b_app is not None: b_app.connect("running-changed", self.do_running_changed) b_app.connect("starting-changed", self.do_starting_changed) b_app.connect("urgent-changed", self.do_urgent_changed) def remove_bamf_app_handlers(self, b_app): """ Remove signal handlers we set up Params: b_app - the Bamf.Application """ if b_app is not None: try: b_app.disconnect_by_func(self.do_running_changed) b_app.disconnect_by_func(self.do_starting_changed) b_app.disconnect_by_func(self.do_urgent_changed) except TypeError: pass def active_win_changed(self, matcher, object, p0): """Event handler for the active window change event Remove the highlighted background from any prevously active app and redraw its icon Set the new app as active and redraw it with a highlighted background Args: matcher : the Bamf.Matcher which received the event - will be the same as self.matcher object : a Bamf.Window - the previously active window p0 : a Bamf Window - the newly active window """ self.wnck_screen.force_update() for app in self.app_list: if app.is_active is True: app.is_active = False app.queue_draw() if p0 is not None: for app in self.app_list: if app.has_bamf_window(p0): win_type = p0.get_window_type() # we only want to allow normal and dialog windows to be the last active window if win_type in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG]: app.last_active_win = p0 app.is_active = True app.queue_draw() break def match_bamf_app_to_dock_app(self, b_app): """ Attempts to match a Bamf.Application to a docked_app If a matching docked_app is found, the docked_app will be setup with the details from the Bamf app. If a match cannot be found, a new docked app is created and added to the dock as an unpinned app Params: b_app : the Bamf.Application Returns: the docked_app that was matched or added to the dock """ # first of all, try to match the application with those in the dock # by their .desktop file if b_app.get_desktop_file() is not None: dock_app = self.get_docked_app_by_desktop_file(b_app.get_desktop_file()) if (dock_app is not None) and (dock_app.bamf_app is None): dock_app.set_bamf_app(b_app) self.set_bamf_app_handlers(b_app) else: # see if there's a match by Bamf.Application dock_app = self.get_docked_app_by_bamf_app(b_app) if dock_app is None: # No match, so add the app to the dock dock_app = docked_app.DockedApp() dock_app.set_bamf_app(b_app) dock_app.desktop_file = b_app.get_desktop_file() add_to_dock = True if dock_app.desktop_file is not None: add_to_dock = dock_app.read_info_from_desktop_file() else: dock_app.setup_from_bamf(self.app_match) if add_to_dock: if build_gtk2: dock_app.applet_win = self.applet.window else: dock_app.applet_win = self.applet.get_window() self.set_bamf_app_handlers(b_app) dock_app.applet = self.applet dock_app.applet_orient = self.applet.get_orient() size = self.applet.get_size() self.set_app_icon(dock_app, size) self.app_list.append(dock_app) self.add_app(dock_app, True) if self.show_all_apps: dock_app.show_icon() else: self.show_or_hide_app_icons() self.show_or_hide_indicators() # make sure the dock_app has been fully realised while Gtk.events_pending(): Gtk.main_iteration() self.set_all_apps_minimise_targets() dock_app.applet_orient = self.applet.get_orient() dock_app.set_indicator(self.indicator) dock_app.set_multi_ind(self.multi_ind) dock_app.set_active_bg(self.active_bg) dock_app.set_attention_type(self.attention_type) self.set_all_apps_minimise_targets() else: if dock_app.startup_id is not None: # if we have a startup id set this means the dock started the app. Since the app has # now opened a new window we can now assume the app has started and end the notification # process dock_app.cancel_startup_notification() # redraw the app's dock icon dock_app.queue_draw() return dock_app def view_opened(self, matcher, object): """ Handler for the view_opened signal If an app has been opened that isn't already in the dock, add it If it is already in the dock, add it to the relevant docked_app Params: matcher - a Bamf.Matcher object - the Bamf.Application or Bamf.Window that was opened """ self.wnck_screen.force_update() if (type(object) is Bamf.Application): if object.is_user_visible(): dock_app = self.match_bamf_app_to_dock_app(object) if dock_app is not None: if dock_app.startup_id is None: dock_app.pulse_once() else: self.hidden_views.append(object) object.connect_after("user-visible-changed", self.view_vis_changed) elif (type(object) is Bamf.Window) and (object.is_user_visible()): the_app = matcher.get_application_for_window(object) # fix for #174 if the_app is not None: self.window_added(the_app, object) def view_vis_changed(self, view, object): """ Handler for the Bamf.View user visibilty changed signal Add the application specifed by the view to the the dock, remove the view from the list of hidden views and disconnect this handler """ if view.is_user_visible(): self.view_opened(self.matcher, view) self.hidden_views.remove(view) view.disconnect_by_func(self.view_vis_changed) def window_added(self, application, object): """ Handler for with Bamf.Application window-added signal Get the docked_app relating to the Bamf.Application If the docked_app is starting and we started it, cancel the startup notification Redraw the app icon If there isn't a docked app for the Bamf.App, add one to the dock Params: Application : the Bamf.Application the received the signal Object : the Bamf.Window that has been added """ if (object.get_window_type() not in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG]) or not object.is_user_visible(): return # get the application for the new window dock_app = self.get_docked_app_by_bamf_app(application) if dock_app is None: # try and match it ourselves dock_app = self.get_docked_app_by_bamf_window(object) if dock_app is None: if application.get_desktop_file() is not None: dock_app = self.get_docked_app_by_desktop_file(application.get_desktop_file()) if dock_app is None: # try again to to match the docked_app, but this time create a new one if it # can't be found dock_app = self.match_bamf_app_to_dock_app(application) # connect a signal handler so that we can detect when a window changes workspaces win_type = object.get_window_type() if (win_type == Bamf.WindowType.NORMAL) or (win_type == Bamf.WindowType.DIALOG): wnck_win = Wnck.Window.get(object.get_xid()) if wnck_win is not None: wnck_win.connect("workspace-changed", self.window_ws_changed) # redraw the app's icon to update the number of indicators etc. if dock_app is not None: if self.show_all_apps: dock_app.queue_draw() else: self.show_or_hide_app_icons() self.show_or_hide_indicators() # update minimize locations ... # at this point, the window will not be returned by application.get_windows() so # self.set_minimise_target(dock_app) will not work. Therefore we need to # set the minimise target of the new window directly... self.set_minimise_target(dock_app, object) def window_removed(self, application, object): """ Handler for the Bamf.Application.window-removed signal Get the docked app relating to the bamf App If the docked app is pinned, redraw it's icon If it unpinned, remove the app from the dock if it has no more windows open, otherwise redraw the app icon Params: application - The Bamf.Application that received the signal object - the Bamf.Window that is being removed """ dock_app = self.get_docked_app_by_bamf_app(application) if dock_app is None: dock_app = self.get_docked_app_by_bamf_window(object) if dock_app is None: the_app = self.matcher.get_application_for_window(object) if the_app is not None: dock_app = self.get_docked_app_by_bamf_app(the_app) if dock_app is not None: # disconnect the signal we connected to the related wnck_window earlier wnck_win = Wnck.Window.get(object.get_xid()) if wnck_win is not None: try: wnck_win.disconnect_by_func(self.window_ws_changed) except TypeError: pass if dock_app.is_pinned: dock_app.queue_draw() else: # note: if the app is no longer running it will be removed in the view_closed handler # unpinned apps that are no longer running can be removed from the dock if dock_app.is_running(): # if the app is still running but does not have visible normal or dialog windows remaining # it needs to be removed from the dock keep_in_dock = dock_app.get_num_windows() > 0 if keep_in_dock: if self.show_all_apps: dock_app.queue_draw() else: self.show_or_hide_app_icons() self.show_or_hide_indicators() else: self.remove_app_from_dock(dock_app) def view_closed(self, matcher, object): """ If the object being closed is an app then: if is not pinned to the dock it needs to be removed from the dock if the app in pinned redraw the dock icon if the app is not pinned and is no longer running, remove the app from the dock if the is not pinned and is still running but has no normal or dialog windows open, remove it from the dock, otherwise redraw the dock icon Params: matcher - a Bamf.Matcher object - the Bamf.Application or Bamf.Window that was closed """ if type(object) is Bamf.Application: dock_app = self.get_docked_app_by_bamf_app(object) if dock_app is not None: if dock_app.startup_id is not None: dock_app.cancel_startup_notification() if dock_app.is_pinned: dock_app.is_active = False dock_app.queue_draw() elif (dock_app.is_running() and (dock_app.get_num_windows() > 0)): if self.show_all_apps: dock_app.queue_redraw() else: self.show_or_hide_app_icons() self.show_or_hide_indicators() else: self.remove_app_from_dock(dock_app) # to prevent Bamf dbus errors remove signal handlers we added self.remove_bamf_app_handlers(object) self.set_all_apps_minimise_targets() elif type(object is Bamf.Window): the_app = matcher.get_application_for_window(object) # fix for #174 if the_app is not None: self.window_removed(the_app, object) def do_urgent_changed(self, view, object): """ Handler for the Bamf.Application urgent changed signal Update the app's icon to reflect the new urgency state... Params: view: the object which received the signal (i.e. a Bamf.Application) object - bool, whether or not this app is signalling urgency """ dock_app = self.get_docked_app_by_bamf_app(view) if dock_app is not None: dock_app.set_urgency(object) def do_running_changed(self, view, object): """ Handler for the Bamf.Application running changed signal If the app is pinned, redraw the icon to reflect the change Params: view: the object which received the signal (i.e. a Bamf.Application) object : bool, whether the app is running or not """ dock_app = self.get_docked_app_by_bamf_app(view) if dock_app is not None: if dock_app.is_pinned: dock_app.queue_draw() def do_starting_changed(self, view, object): """ Handler for the Bamf.Application staring changed signal If the related app is starting and the startup_id is not None, then we've started the app ourselves so nothing further need be done. If we haven't started the app ourselves, make the app icon pulse If the app has finished started and we started it ourselves, we need to cancel the startup notification process. Params: view: the object which received the signal (i.e. a Bamf.Application) object - bool, whether or not this app is starting """ dock_app = self.get_docked_app_by_bamf_app(view) if dock_app is not None: if object is True: if dock_app.startup_id is None: dock_app.pulse_once() else: if dock_app.startup_id is not None: dock_app.cancel_startup_notification() def window_ws_changed(self, wnck_window): """ Handler for the wnck_window workspace changed signal If we're showing unpinned apps from the current workspace only, then we need to make sure that dock icons are hidden if an unpinned app has no more windows on the current workspace, but still has windows open on another... """ if not self.show_all_apps: self.show_or_hide_app_icons() self.show_or_hide_indicators() self.set_all_apps_minimise_targets() def app_is_pinned_to_workspace(self, app): """ Checks to see if the app is pinned to a specific workspace Args: app : a docked app Returns: String: the name of the workspace the app is pinned to, or "" if it isn't pinned to any """ if app.desktop_file is None: return"" df = os.path.basename(app.desktop_file) if df is None: return "" for config in self.pa_configs: if df in config or app.desktop_file in self.pa_configs: return config[1] return "" def show_or_hide_app_icons(self): """ If we're only showing unpinned apps from the current workspace then then show/hide unpinned apps as appropriate. If we're showing unpinned apps from all workspaces then we also need to check if we're also showing pinned apps on the workspaces they were pinned to. If so we can only show icons for apps which are not pinned to the current workspace if they're not already pinned to another... Finally, recalculate all app minimization targets """ cur_ws = self.wnck_screen.get_active_workspace() if self.show_all_apps: if self.pa_on_all_ws: for app in self.app_list: if not app.is_pinned: if not app.is_visible(): app.show_icon() else: ws_name = cur_ws.get_name() for app in self.app_list: if not app.is_pinned: pinned_ws = self.app_is_pinned_to_workspace(app) if pinned_ws == "": app.show_icon() else: app.hide_icon() else: for app in self.app_list: if not app.is_pinned: if app.has_windows_on_workspace(cur_ws): app.show_icon() else: app.hide_icon() self.set_all_apps_minimise_targets() def show_or_hide_indicators(self): """ Show or hide app indicators as appropriate If we're only showing indicators for apps which have windows on the current workspace then set the apps hide_indicators setting as appropriate. Otherwise, set the hide_indicators setting to False. """ cur_ws = self.wnck_screen.get_active_workspace() for app in self.app_list: if self.win_from_cur_ws_only: app.ind_ws = cur_ws else: app.ind_ws = None def remove_app_from_dock(self, app): """Remove an app from the dock. Remove the app from the app_list Remove the app's drawing area from self.box Args: app : the app to be removed """ app_pos = None if not build_gtk2: if self.scrolling: # clear the scroll indicators self.set_app_scroll_dirs(False) app_pos = self.get_visible_app_index(app) self.app_list.remove(app) if not build_gtk2: if self.dock_fixed_size == -1: self.set_dock_panel_size() num_apps = len(self.box.get_children()) if build_gtk2: self.box.remove(app.drawing_area) else: # the row/column which contains the app needs to be # removed, so get the app's position in the grid pos = 0 while pos < num_apps: if self.box.orientation == Gtk.Orientation.VERTICAL: left = 0 top = pos else: left = pos top = 0 if self.box.get_child_at(left, top) == app.drawing_area: # we've found the app if self.box.orientation == Gtk.Orientation.VERTICAL: self.box.remove_row(pos) else: self.box.remove_column(pos) pos += 1 app = None # if we're scrolling things get a bit complicated now... # (if nice_sizing then we handle things in fit_to_alloc) if self.nice_sizing: self.ns_app_removed = app_pos self.set_size_hints() elif self.scrolling: # do we now have enough space to show all visible apps? if self.get_total_num_visible_apps() < self.dock_fixed_size: self.scrolling = False if self.panel_orient in ["top", "bottom"]: self.scrolled_win.get_hadjustment().set_value(0) else: self.scrolled_win.get_vadjustment().set_value(0) else: do_scroll = False if (app_pos is not None) and app_pos < self.scroll_index: do_scroll = True do_scroll = do_scroll or \ (self.scroll_index + self.dock_fixed_size >= self.get_total_num_visible_apps()) if do_scroll: # if the app we deleted is before the current scroll index, or if we are left with an # empty space at the end of the dock, we need to scroll self.scroll_index -= 1 new_pos = self.scroll_index * self.get_app_icon_size() if self.panel_orient in ["top", "bottom"]: self.scrolled_win.get_hadjustment().set_value(new_pos) else: self.scrolled_win.get_vadjustment().set_value(new_pos) self.set_app_scroll_dirs(True) # sort out the app under the mouse app = self.get_app_under_mouse() self.app_with_mouse = app if app is not None: app.has_mouse = True app.queue_draw() app = None def set_app_icon(self, dock_app, size): """ Sets up an app's icon, scaling it to a specified size Select an appropriate icon size based on the specified size Load the app's icon, using a fallback STOCK_EXEC as a fallback Scale the icon to the specified size Args: dock_app : the DockedApp size : the required icon size in pixels """ # if we're emulating the Unity look, icons need to be 66% smaller orig_size = size if self.active_bg in [docked_app_helpers.IconBgType.UNITY, docked_app_helpers.IconBgType.UNITY_FLAT]: icon_size = (size * 3) / 4 else: icon_size = size - 6 if size >= 56: stock_size = Gtk.IconSize.DIALOG elif size >= 40: stock_size = Gtk.IconSize.DIALOG elif size >= 28: stock_size = Gtk.IconSize.DND elif size >= 20: stock_size = Gtk.IconSize.LARGE_TOOLBAR else: stock_size = Gtk.IconSize.BUTTON dock_app.icon_filename = None scale_factor = self.box.get_scale_factor() pixbuf = None pixbuf_s = None # try to get the icon from wnck if dock_app.icon_name == "wnck": win = dock_app.get_first_normal_win() if win is not None: pixbuf = window_control.get_icon_pb(win) if pixbuf is not None: # scale at best quality pixbuf = pixbuf.scale_simple(icon_size * scale_factor, icon_size * scale_factor, GdkPixbuf.InterpType.HYPER) # scale to the correct size dock_app.icon_filename = "wnck" # we got it from wnck # look up the icon filename using Gtk if pixbuf is None and dock_app.has_desktop_file(): dai = Gio.DesktopAppInfo.new_from_filename(dock_app.desktop_file) the_icon = dai.get_icon() if the_icon is Gio.ThemedIcon: icon_info = self.icontheme.choose_icon_for_scale(the_icon.get_names(), icon_size, scale_factor, Gtk.IconLookUpFlags.FORCE_SIZE) else: icon_info = self.icontheme.choose_icon_for_scale([dock_app.icon_name, None], icon_size, scale_factor, Gtk.IconLookupFlags.FORCE_SIZE) if icon_info is not None: dock_app.icon_filename = icon_info.get_filename() try: pixbuf = icon_info.load_icon() except GLib.GError: pixbuf = None if pixbuf is None: # we couldn't get the icon from the .desktop or wnck but there a still a few # things we can do... # # 1 .. quick and dirty - check to see if the icon points to an actual file # or ... # look in /usr/share/icons/hicolor/x/ # apps/ and # ~/.local/share/icons/icons/hicolorx/apps/ # for the icon name or ... # look in /usr/share/pixmaps for an icon of any type with # the same name as the app # then ... # look in ~/.local/share/icons for an icon with the same name # and extension as the icon # # 2 .. sloooow - iterate through each installed icon theme and try to # find the app - not implement for now # the png method. look for lower and uppercased variations of the filename # and note that all files in /usr/share/pixmaps are .png icon_file = "" if os.path.isfile(dock_app.icon_name): icon_file = dock_app.icon_name pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(dock_app.icon_name, icon_size * scale_factor, icon_size * scale_factor) else: icon_name = dock_app.icon_name # look in the 'hicolor' icon directories for the icon file icon_path = "/usr/share/icons/hicolor/%dx%d/apps/" % (icon_size * scale_factor, icon_size * scale_factor) if os.path.isfile("%s%s" % (icon_path, icon_name)): icon_file = "%s%s" % (icon_path, icon_name) else: icon_path = os.path.expanduser("~/.local/share/icons/hicolor/%dx%d/apps/" % (icon_size * scale_factor, icon_size * scale_factor)) if os.path.isfile("%s%s" % (icon_path, icon_name)): icon_file = "%s%s" % (icon_path, icon_name) # if we still haven't found the icon, look in # /usr/share/pixmaps for a .png file if icon_file == "": icon_name = os.path.splitext(dock_app.icon_name)[0] # remove any extension if os.path.isfile("/usr/share/pixmaps/%s.png" % icon_name): icon_file = "/usr/share/pixmaps/%s.png" % icon_name elif os.path.isfile("/usr/share/pixmaps/%s.png" % icon_name.upper()): icon_file = "/usr/share/pixmaps/%s.png" % icon_name.upper() elif os.path.isfile("/usr/share/pixmaps/%s.png" % icon_name.lower()): icon_file = "/usr/share/pixmaps/%s.png" % icon_name.lower() # final attempt - look in ~/.local/share/icons for the icon if icon_file == "": if os.path.isfile(os.path.expanduser("~/.local/share/icons/%s" % dock_app.icon_name)): icon_file = os.path.expanduser("~/.local/share/icons/%s" % dock_app.icon_name) # if we've found an icon, load it if icon_file != "": pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, icon_size * scale_factor, icon_size * scale_factor) else: # if not, use a stock icon to represent the app pixbuf = self.applet.render_icon(Gtk.STOCK_EXECUTE, stock_size, None) dock_app.icon_filename = "STOCK_EXECUTE" # we should now have an icon - either the app's own icon or the # stock_execute .. dock_app.set_drawing_area_size(size) dock_app.set_pixbuf(pixbuf) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale_factor, None) dock_app.set_surface(surface) def set_size_hints(self): """ Set the size hints for the applet """ app_size = self.get_app_icon_size() # Temporary fix https://github.com/mate-desktop/mate-panel/issues/745 to # ensure MUTINY layout works as intended # TODO: delete this if self.panel_layout.upper() == "MUTINY": min_icons = self.get_mutiny_fixed_size() else: min_icons = self.ns_base_apps num_vis = self.get_total_num_visible_apps() if num_vis > min_icons: i = num_vis - min_icons size_hints = [] while i > 0: size_hints.append(i * app_size) size_hints.append((i * app_size)) # - 1) i -= 1 size_hints.append(1) size_hints.append(0) self.applet.set_size_hints(size_hints, min_icons * app_size) else: self.applet.set_size_hints([num_vis * app_size, 0], min_icons * app_size) def fit_to_alloc(self): """ Ensure the dock fits within the space allocated to it on the panel Enable/disable scrolling as appropriate Scroll newly launched apps into view if necessary Remove icons from the dock when apps are closed if necessary """ max_vis = self.get_max_visible_apps() total_vis = self.get_total_num_visible_apps() if not self.dds_done: # we haven't been fully setup yet... return # TODO: may have do something here to remove scroll arrows when dock is made # larger and is no longer scrolling if not self.scrolling and (total_vis > max_vis): self.enable_app_scrolling() if self.scrolling and self.ns_new_app: # a new app has been added to the dock, so we need to bring it into view self.scroll_index = self.get_total_num_visible_apps() - self.get_max_visible_apps() if self.panel_orient in ["top", "bottom"]: startpos = self.scrolled_win.get_hadjustment().get_value() else: startpos = self.scrolled_win.get_vadjustment().get_value() endpos = startpos + (self.scroll_index * self.get_app_icon_size()) ScrollAnimator(self.scrolled_win, startpos, endpos, self.panel_orient, 16, 5, self.scroll_anim_finished_cb) elif self.ns_app_removed is not None: if self.get_total_num_visible_apps() < max_vis: self.scrolling = False self.set_app_scroll_dirs(False) if self.panel_orient in ["top", "bottom"]: self.scrolled_win.get_hadjustment().set_value(0) else: self.scrolled_win.get_vadjustment().set_value(0) else: do_scroll = False if self.ns_app_removed < self.scroll_index: do_scroll = True do_scroll = do_scroll or (self.scroll_index + max_vis >= total_vis) if do_scroll: # if the app we deleted is before the current scroll index, or if we are left with an # empty space at the end of the dock, we need to scroll if self.scroll_index != 0: self.scroll_index -= 1 new_pos = self.scroll_index * self.get_app_icon_size() if self.panel_orient in ["top", "bottom"]: self.scrolled_win.get_hadjustment().set_value(new_pos) else: self.scrolled_win.get_vadjustment().set_value(new_pos) self.set_app_scroll_dirs(True) elif self.scrolling: # because we have a new allocation we need to redo the scroll indicators self.set_app_scroll_dirs(False) self.set_app_scroll_dirs(True) self.ns_app_removed = None self.ns_new_app = False def add_app(self, dock_app, do_scroll=False): """ Add an app's drawing area to the VBox/Hbox/Grid container Args: dock_app : the DockedApp do_scroll : boolean - if True indicates that the dock should scroll to bring the app into view """ if build_gtk2: self.box.add(dock_app.drawing_area) else: if not self.nice_sizing: # enabling/disabling scolling etc will be handled in fit_to_alloc # when nice_sizing overflow = False if self.dock_fixed_size == -1: self.set_dock_panel_size() elif not self.scrolling: overflow = self.will_overflow(self.get_app_icon_size()) if overflow: self.enable_app_scrolling() else: self.set_app_scroll_dirs(False) else: # we only indicate new apps after delayed setup is done # so that we don't scroll the dock during startup self.ns_new_app = self.dds_done self.set_size_hints() pos = len(self.box.get_children()) # use .attach instead of .add with Gtk.Grid - .add doesn't seem # to work properly with vertical orientation... if self.box.orientation == Gtk.Orientation.VERTICAL: self.box.attach(dock_app.drawing_area, 0, pos, 1, 1) else: self.box.attach(dock_app.drawing_area, pos, 0, 1, 1) if not self.nice_sizing and (self.scrolling and do_scroll): # scroll to bring the new app into view self.scroll_index = self.get_total_num_visible_apps() - self.dock_fixed_size if self.panel_orient in ["top", "bottom"]: startpos = self.scrolled_win.get_hadjustment().get_value() else: startpos = self.scrolled_win.get_vadjustment().get_value() endpos = startpos + (self.scroll_index * self.get_app_icon_size()) ScrollAnimator(self.scrolled_win, startpos, endpos, self.panel_orient, 16, 5, self.scroll_anim_finished_cb) def get_app_by_pos(self, position): """ Get the app at a specified position in the dock :param self: :param position: int - the position of the app in self.app_list :return: a docked_app, or none if position exceeds the number of apps in the dock """ if position < len(self.app_list): return self.app_list[position] else: return None def update_colours(self, box): context = box.get_style_context() # If the theme defines a dark variant, use it to ensure same color as the panel bg_color = context.lookup_color("bg_dark_color") if bg_color[0]: self.box.override_background_color(Gtk.StateFlags.NORMAL, bg_color[1]) else: self.box.override_background_color(Gtk.StateFlags.NORMAL, None) def create_box(self, orientation): """Create a vertical or horizontal (depending on the applet orientation) box to contain the docked apps areas. Args: orientation : the applet orientation """ if orientation == MatePanelApplet.AppletOrient.LEFT or \ orientation == MatePanelApplet.AppletOrient.RIGHT: if build_gtk2: self.box = Gtk.VBox() else: self.box = Gtk.Grid() self.box.orientation = Gtk.Orientation.VERTICAL else: if build_gtk2: self.box = Gtk.HBox() else: self.box = Gtk.Grid() self.box.orientation = Gtk.Orientation.HORIZONTAL self.box.set_hexpand(False) if build_gtk2: self.box.set_spacing(self.app_spacing + 2) else: self.box.set_row_spacing(self.app_spacing + 2) self.box.set_column_spacing(self.app_spacing + 2) def setup_dock(self): """Setup the dock." Add all applicable pinned apps to the dock Add all non-pinned running apps to the dock Setup all apps according to the applet size and orientation Setup signal handlers """ # make sure the applet is correctly oriented orientation = self.applet.get_orient() self.create_box(orientation) if not build_gtk2: self.scrolled_win.add(self.box) # setup up pinned and non-pinned running apps self.setup_app_list() self.setup_dock_apps() self.show_or_hide_app_icons() self.show_or_hide_indicators() # set up signal handlers self.matcher.connect("active-window-changed", self.active_win_changed) self.matcher.connect("active_application_changed", self.active_app_changed) self.matcher.connect_after("view-opened", self.view_opened) self.matcher.connect_after("view-closed", self.view_closed) self.box.connect("style-updated", self.update_colours) self.wnck_screen.connect("active-workspace-changed", self.active_workspace_changed) def set_size_request(self): """ Set the dock's size request Request the 'natural' size of the dock, according to the applet orientation""" if build_gtk2: return if self.panel_orient in ["top", "bottom"]: self.scrolled_win.set_size_request(-1, self.panel_size) else: self.scrolled_win.set_size_request(self.panel_size, -1) def setup_dock_apps(self): """ setup the apps in the app list according to the dock settings """ orientation = self.applet.get_orient() applet_size = self.applet.get_size() # add the apps to the dock for dock_app in self.app_list: dock_app.applet_orient = orientation dock_app.applet = self.applet if build_gtk2: dock_app.applet_win = self.applet.window else: dock_app.applet_win = self.applet.get_window() dock_app.set_indicator(self.indicator) self.set_app_icon(dock_app, applet_size) dock_app.set_multi_ind(self.multi_ind) dock_app.set_active_bg(self.active_bg) dock_app.set_attention_type(self.attention_type) self.add_app(dock_app) self.set_size_request() # make everything visible... self.box.show_all() def set_new_orientation(self, new_orient): """Change the dock applet to a new applet orientation For Gtk2: Remove all app's drawing areas from the V/HBox Remove the V/HBox and create a new one according to the new orientation Add all of the app's drawing areas to the new V/HBox For Gtk3: Change the orientation of the Gtk.Grid Swap the 'left-attach' and 'top-attach' properties of the grid's children Args: new_orient : the new applet orientation """ # we need to re-read/refresh the panel information ... self.get_panel_id() self.get_applet_panel_info() if build_gtk2: for dock_app in self.app_list: self.box.remove(dock_app.drawing_area) self.applet.remove(self.box) self.box = None self.create_box(new_orient) for dock_app in self.app_list: self.box.add(dock_app.drawing_area) dock_app.applet_orient = new_orient self.applet.add(self.box) else: old_orient = self.box.orientation if (new_orient == MatePanelApplet.AppletOrient.RIGHT) or \ (new_orient == MatePanelApplet.AppletOrient.LEFT): self.box.orientation = Gtk.Orientation.VERTICAL else: self.box.orientation = Gtk.Orientation.HORIZONTAL for child in self.box.get_children(): ol = self.box.child_get_property(child, "left-attach") # if we've switched orientations then realign the grid contents if old_orient != self.box.orientation: ot = self.box.child_get_property(child, "top-attach") self.box.child_set_property(child, "left-attach", ot) self.box.child_set_property(child, "top-attach", ol) for dock_app in self.app_list: dock_app.applet_orient = new_orient def get_app_at_mouse(self, mouse_x, mouse_y): """ Find the app underneath the mouse cursor. Args: mouse_x : the x coord of the mouse mouse_y : the y coord of the mouse Returns: The app under the mouse, or None if one could not be found """ for app in self.app_list: if app.is_visible(): alloc = app.drawing_area.get_allocation() mx = mouse_x my = mouse_y if self.scrolling: # if we're scrolling we need to adjust mouse_x (or mouse_y, according to the # panel orientation) to account for the current scroll position if self.panel_orient in ["top", "bottom"]: mx += self.scrolled_win.get_hadjustment().get_value() else: my += self.scrolled_win.get_vadjustment().get_value() # was this ...my += self.scroll_index * self.get_app_icon_size() if (mx >= alloc.x) and (mx <= alloc.x + alloc.width): if (my >= alloc.y) and \ (my <= alloc.y + alloc.height): return app return None def reset_scroll_timer(self): """ Reset the scroll timer If the timer is already instantiated, delete it. if the app rhat currently has the mouse has a scroll direction associated with it, start another timer """ if self.scroll_timer is not None: GObject.source_remove(self.scroll_timer) self.scroll_timer = None if self.scrolling and (self.app_with_mouse is not None) and \ (self.app_with_mouse.scroll_dir != docked_app.ScrollType.SCROLL_NONE): self.scroll_timer = GObject.timeout_add(500, self.do_app_scroll) def stop_scroll_timer(self): """ Stop the win list timer """ if self.scroll_timer is not None: GObject.source_remove(self.scroll_timer) self.scroll_timer = None def do_app_scroll(self): """ Scrolls the dock's scroll window in the direction indicated by the highlighted apps """ def hide_popups(): self.hide_win_list() self.stop_act_list_timer() self.hide_act_list() def set_new_app_with_mouse(): app = self.get_app_under_mouse() app.has_mouse = True self.app_with_mouse = app app.queue_draw() if not self.scrolling: # should never happen really... return False app = self.app_with_mouse if app is not None: if app.scroll_dir == docked_app.ScrollType.SCROLL_UP: if self.scroll_index != 0: self.set_app_scroll_dirs(False) self.scroll_index -= 1 # hide popups # the app will no longer have the mouse app.has_mouse = False app.queue_draw() hide_popups() if self.panel_orient in ["top", "bottom"]: sp = self.scrolled_win.get_hadjustment().get_value() else: sp = self.scrolled_win.get_vadjustment().get_value() ScrollAnimator(self.scrolled_win, sp, sp - self.get_app_icon_size(), self.panel_orient, 16, 5, self.scroll_anim_finished_cb) return False elif app.scroll_dir == docked_app.ScrollType.SCROLL_DOWN: if self.scroll_index < self.get_total_num_visible_apps(): self.set_app_scroll_dirs(False) self.scroll_index += 1 app.has_mouse = False app.queue_draw() hide_popups() if self.panel_orient in ["top", "bottom"]: sp = self.scrolled_win.get_hadjustment().get_value() else: sp = self.scrolled_win.get_vadjustment().get_value() ScrollAnimator(self.scrolled_win, sp, sp + self.get_app_icon_size(), self.panel_orient, 16, 5, self.scroll_anim_finished_cb) return False return True def scroll_anim_finished_cb(self): """ Callback for the scroll animation timer ends Add scroll indicators to the dock icons which can now initiate a scroll Highlight the app under the mouse Reset the scroll timer """ hp = self.scrolled_win.get_vadjustment().get_value() self.set_app_scroll_dirs(True) # if we're scrolling because the mouse hovered over a docked app, there will be another # app under the mouse app = self.get_app_under_mouse() self.app_with_mouse = app if app is not None: app.has_mouse = True app.queue_draw() self.reset_act_list_timer() self.scroll_timer = None # timer will have been cancelled by itself self.reset_scroll_timer() self.set_all_apps_minimise_targets() def reset_act_list_timer(self): """ Reset win_list timer If the timer is already instantiated, delete it. Start a new timer with the appropriate delay """ if self.act_list_timer is not None: GObject.source_remove(self.act_list_timer) if not self.panel_act_list: self.act_list_timer = GObject.timeout_add(self.popup_delay, self.show_act_list) def stop_act_list_timer(self): """ Stop the win list timer """ if self.act_list_timer is not None: GObject.source_remove(self.act_list_timer) self.act_list_timer = None def show_act_list(self): """ Show the the list of open windows and actions for the currently highlighted app If the window list is currently being shown then don't do anything, otherwise... Get the currently highlighted app. If the highlighted app is being launched or a window list is already being displayed for it, or a user interaction has already dismissed the window list, then do nothing Otherwise, fill the window list, set the window position and set the screen areas where the mouse must remain or the window list will hide """ if self.app_win_list.get_visible(): return highlighted_app = self.app_with_mouse self.right_clicked_app = highlighted_app # above is needed so that actions invoked from the window list work correctly if highlighted_app is None: return # is the app being launched? if highlighted_app.is_pulsing: self.act_list_timer = None return False # always recreate the window list e.g. to account for windows being # opened/closed, the app being pinned/unpinned etc. if self.app_act_list is not None: self.app_act_list.destroy() self.set_actions_for_app(self.app_with_mouse) df_shortcut_1_action = self.popup_action_group.get_action("df_shortcut_1_action") df_shortcut_2_action = self.popup_action_group.get_action("df_shortcut_2_action") df_shortcut_3_action = self.popup_action_group.get_action("df_shortcut_3_action") df_shortcut_4_action = self.popup_action_group.get_action("df_shortcut_4_action") pin_action = self.popup_action_group.get_action("pin_action") unpin_action = self.popup_action_group.get_action("unpin_action") # if we're scrolling we need to pass the current scroll position to the # action list if not self.scrolling or build_gtk2: scroll_adj = 0 else: if self.panel_orient in ["top", "bottom"]: scroll_adj = self.scrolled_win.get_hadjustment().get_value() else: scroll_adj = self.scrolled_win.get_vadjustment().get_value() self.app_act_list = dock_action_list.DockActionList(self.wnck_screen, self.applet.get_orient(), scroll_adj) self.app_act_list.icontheme = self.icontheme # get the panel custom background colour (if any) and then set the # window list colours self.app_act_list.set_colours(self.get_applet_panel_custom_rgb()) self.app_act_list.the_app = highlighted_app add_sep = False shortcut_action_no = 1 while shortcut_action_no <= self.max_num_actions: df_shortcut_action = self.popup_action_group.get_action("df_shortcut_%d_action" % shortcut_action_no) if df_shortcut_action.is_visible(): add_sep = True self.app_act_list.add_to_list(df_shortcut_action.get_label(), df_shortcut_action, True) shortcut_action_no += 1 if add_sep: self.app_act_list.add_separator() if pin_action.is_visible(): self.app_act_list.add_to_list(pin_action.get_label(), pin_action, False) if unpin_action.is_visible(): self.app_act_list.add_to_list(unpin_action.get_label(), unpin_action, False) if self.app_act_list.get_num_rows() == 0: self.act_list_timer = None return False self.app_act_list.clear_mouse_areas() applet_x, applet_y = self.get_dock_root_coords() applet_w = applet_h = highlighted_app.drawing_area_size self.app_act_list.set_applet_details(applet_x, applet_y, applet_w, applet_h) app_x, app_y = self.get_app_root_coords(highlighted_app) self.app_act_list.set_app_root_coords(app_x, app_y) self.app_act_list.show_all() self.app_act_list.set_opacity(0.9) self.act_list_timer = None return False def do_window_selection(self, app): """ Allow the user to change an app's currently active window If the dock's window list is being used, show or hide it as appropriate If the thumbnail preview option has been chosen, invoke Compiz via dbus """ if self.click_action == dock_prefs.ClickActionType.WIN_LIST: self.show_or_hide_win_list() elif self.click_action == dock_prefs.ClickActionType.COMPIZ: # get root window id if build_gtk2: # get_xid is non introspectable on gtk2, so use xwininfo # instead... rw_inf = subprocess.check_output(["xwininfo", "-root"]) rw_inf = rw_inf.split() rwin = int(rw_inf[3], 16) else: rwin = Gdk.Screen.get_default().get_root_window().get_xid() try: compiz_service = self.session_bus.get_object('org.freedesktop.compiz', '/org/freedesktop/compiz/scale/screen0/initiate_key') activate = compiz_service.get_dbus_method('activate', 'org.freedesktop.compiz') cn = window_control.get_wm_class_group_name(app.get_first_normal_win()) activate("root", rwin, "match", "class=%s" % cn) except dbus.exceptions.DBusException: # e.g. Compiz is not installed, or dbus or scale plugin not # enabled... # # fallback to built in window list self.show_or_hide_win_list() else: self.minimize_or_restore_windows(app) def show_or_hide_win_list(self): """ If the window list is visible, hide it, otherwise show it """ visible = (self.app_win_list is not None) and self.app_win_list.get_visible() if visible: self.hide_win_list() else: self.show_win_list() def show_win_list(self): """ Show the the list of open windows and for the currently highlighted app Get the currently highlighted app. If the highlighted app is being launched or a window list is already being displayed for it, or a user interaction has already dismissed the window list, then do nothing Otherwise, fill the window list, set the window position and set the screen areas where the mouse must remain or the window list will hide """ if build_gtk2: highlighted_app = self.app_with_mouse else: highlighted_app = self.get_app_under_mouse() self.right_clicked_app = highlighted_app # abive is needed so that actions invoked from the window list work correctly if highlighted_app is None: return # is the app being launched? if highlighted_app.is_pulsing: self.act_list_timer = None return False # always recreate the window list e.g. to account for windows being # opened/closed if self.app_win_list is not None: self.app_win_list.destroy() self.set_actions_for_app(highlighted_app) if build_gtk2: scroll_adj = 0 else: if self.panel_orient in ["top", "bottom"]: scroll_adj = self.scrolled_win.get_hadjustment().get_value() else: scroll_adj = self.scrolled_win.get_vadjustment().get_value() self.app_win_list = dock_win_list.DockWinList(self.wnck_screen, self.applet.get_orient(), scroll_adj) self.app_win_list.icontheme = self.icontheme # get the panel custom background colour (if any) and then set the # window list colours self.app_win_list.set_colours(self.get_applet_panel_custom_rgb()) self.app_win_list.the_app = highlighted_app # add any open windows if highlighted_app.is_running(): self.app_win_list.setup_list(self.win_from_cur_ws_only) self.app_win_list.clear_mouse_areas() applet_x, applet_y = self.get_dock_root_coords() applet_w = applet_h = highlighted_app.drawing_area_size self.app_win_list.set_applet_details(applet_x, applet_y, applet_w, applet_h) app_x, app_y = self.get_app_root_coords(highlighted_app) self.app_win_list.set_app_root_coords(app_x, app_y) self.app_win_list.show_all() self.app_win_list.set_opacity(0.9) self.act_list_timer = None return False def hide_win_list(self): """ Hide the window list """ if self.app_win_list is not None: self.app_win_list.hide() def hide_act_list(self): """ Hide the action list """ if self.app_act_list is not None: self.app_act_list.hide() def minimize_or_restore_windows(self, app, restore_topmost_only=False): """ Minimize or restore an app's windows the action to perform (minimizing, moving workspace, activating) is decided as follows: if (the app's windows are all minimized) or (the app has one or more unminimized window but is not the active app) then restore the app's last active window or all windows (based on the user's settings). If the active window is on a different workspace then activate that workspace else: the app is currently the active app so all of the app windows will be minimized but first, hide any app window list that is being shown and stop the window list timer Args: app: the docked app whose windows are to be minimized or restored restore_topmost_only: only the topmost window should be restored """ self.stop_act_list_timer() self.hide_win_list() restore_win = (not app.has_unminimized_windows()) or \ (app.has_unminimized_windows() and (app.is_active is False)) last_active_win = None if restore_win: last_active_win = app.last_active_win # the last active window may have been closed - if so set # last_active_window to None if last_active_win not in app.get_windows(): last_active_win = app.last_active_win = None # if there is no last active window then activate the app's first normal window # (related to https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1550392) if last_active_win is None: last_active_win = app.get_first_normal_win() # if we're restoring all windows, do this now before we finally # activate the last active window if restore_topmost_only is False: for win in app.get_windows(): win_type = win.get_window_type() if (win_type in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG] or win.is_user_visible()) and (win != last_active_win): window_control.activate_win(win) sleep(0.01) app.last_active_win = last_active_win if last_active_win is not None: wnck_win = Wnck.Window.get(last_active_win.get_xid()) if wnck_win is not None: wnck_aws = self.wnck_screen.get_active_workspace() wnck_ws = wnck_win.get_workspace() # the window's active workspace can be None if it is visible on # all workspaces or if it is not on any workspace (I'm looking at # you caja-desktop!!!!!) # (fix for https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1550392 and # https://bugs.launchpad.net/ubuntu-mate/+bug/1555336 (regarding software updater)) if wnck_aws is not None and wnck_ws is not None and \ (wnck_aws != wnck_ws): wnck_ws.activate(0) sleep(0.01) # rarely, the last active win does not end up as the active window # if we activate here, so instead a workaround which seems to do # the trick is use a timer as below # fix for #176, don't send the current event time to the activation # timer GObject.timeout_add(20, win_activation_timer, [last_active_win, 0]) else: # minimize all windows and do the last active window last of all last_active_win = app.last_active_win for win in app.get_windows(): win_type = win.get_window_type() if (win_type in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG] or win.is_user_visible()) and (win != last_active_win): window_control.minimise_win(win) sleep(0.01) app.last_active_win = last_active_win if last_active_win is not None: window_control.minimise_win(last_active_win) sleep(0.01) def activate_window(self, win): """ Activate a Bamf window, switching workspace as necessary Args: win : the Bamf.Window """ if win is not None: # if the window to be activated is not on the current workspace, # switchto that workspace wnck_win = Wnck.Window.get(win.get_xid()) wnck_aws = self.wnck_screen.get_active_workspace() wnck_ws = wnck_win.get_workspace() # the windows's current workspace can be None if it is pinned to all # workspaces or it is not on any at all... # (fix for https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1550392 and # https://bugs.launchpad.net/ubuntu-mate/+bug/1555336 (regarding software updater)) if (wnck_aws is not None) and (wnck_ws is not None) and \ (wnck_aws != wnck_ws): wnck_ws.activate(0) sleep(0.01) window_control.activate(win) def activate_first_window(self, app): """ Active the specified apps's first window, changing workspace as necessary """ if app is None: return win = app.get_first_window() if win is None: return self.activate_window(win) def do_window_scroll(self, scroll_dir, event_time, the_app=None): """ Scroll to the next/previous window of the currently active app This function is called in response to the mouse scroll event on the panel applet and also when an applet keyboard shortcut (e.g. 1) is used Depending on the scroll direction, make the next or previous window of the current app active. Scrolling will wrap around in both directions If the app only has one window or we don't know which window was last active (e.g. because the applet has only just started) then make the first window active If the new window is not on the current workspace, change to the relevant workspace Also, hide the app window list and stop any timer that might be running Args: scroll_dir : A GDK.ScrollDirection which indicates whether to go forwards or backwards through the window list event_time : the time scroll event occurred the_app : will indicate the app whose windows are to be scrolled when a keyboard shortcut has been used. """ if (scroll_dir != Gdk.ScrollDirection.UP) and \ (scroll_dir != Gdk.ScrollDirection.DOWN): return if the_app is None: # we've been called in response to a mouse scroll event, so we need to get # the app under the mouse app = self.get_app_under_mouse() if app is None: return else: app = the_app # if the app isn't running, there's nothing to do... if app.is_running() is False: return windows = app.get_windows() if (app.last_active_win is None) or (len(windows) == 1): new_index = 0 else: # work out which window we want to activate if app.last_active_win in windows: index = windows.index(app.last_active_win) else: index = 0 # in case of error activate the first window if scroll_dir == Gdk.ScrollDirection.UP: if index == 0: new_index = len(windows) - 1 else: new_index = index - 1 else: if index == len(windows) - 1: new_index = 0 else: new_index = index + 1 wnck_win = Wnck.Window.get(windows[new_index].get_xid()) # hide the window list and stop any timer self.hide_win_list() self.stop_act_list_timer() # if the new window is on a different workspace, we need to switch # workspace wnck_aws = self.wnck_screen.get_active_workspace() wnck_ws = wnck_win.get_workspace() if wnck_aws is not None and (wnck_aws != wnck_ws): wnck_ws.activate(0) sleep(0.01) # activate the new window window_control.activate_win(windows[new_index]) def get_dragee(self): """" Return the app which is currently marked as being dragged to a new position in the dock. Returns: a docked_app, or None is no apps are being dragged """ for app in self.app_list: if app.is_dragee is True: return app return None def start_drag_motion_timer(self, dragee): """ Create a timer to allow us to monitor the mouse position during the drag/drop and rearrange dock icons on the fly Args: dragee - the docked_app which is being dragged """ self.dm_timer = DragMotionTimer(dragee, self) def stop_drag_motion_timer(self): """ Stop the drag motion timer """ self.dm_timer.drag_ended = True def start_da_timer(self, app): """ Use a timer to before activating an app Args: app : the app to activate """ # first of all, stop any other timer da_timer that may be running if (self.da_timer is not None) and (self.da_timer.timer_id != 0): GObject.source_remove(self.da_timer.timer_id) # create a new timer self.da_timer = DragActivateTimer(self, app) def window_scroll(self, widget, event): self.scrolled_win.emit_stop_by_name("scroll-event") return False def get_app_under_mouse(self): """ Get the docked which is currently under the mouse cursor Returns : a docked_app, or None if the cursor is not over a docked_app """ # get the mouse device so we can obtain the root coordinates of the # the cursor display = Gdk.Display.get_default() manager = display.get_device_manager() mouse = manager.get_client_pointer() none, x, y = mouse.get_position() dx, dy = self.get_dock_root_coords() # convert to applet coords x = x - dx y = y - dy return self.get_app_at_mouse(x, y) def get_avail_panel_space(self): """ Gets the amount of space (w and h) that is available to the dock on the the panel Needs to be called after get_applet_panel_info Returns: Two ints, the required width and the height in pixels """ if self.panel_orient in ["top", "bottom"]: # horizontal panel ... return self.dock_fixed_size * self.get_app_icon_size(), self.panel_size else: return self.panel_size, self.dock_fixed_size * self.get_app_icon_size() def get_total_num_visible_apps(self): """ Get the total number of dock apps which are visible Returns: int """ num_vis = 0 for app in self.app_list: if app.is_visible: num_vis += 1 return num_vis def get_visible_app(self, app_no): """ Gets a visible app in the dock Params: app_no : the number of the visible docked app to get (0 = the first, 1 = the second etc.) Returns : a docked app, or None if e.g. app_no exceeds the number of visible apps """ count = 0 for app in self.app_list: if app.is_visible: if count == app_no: return app else: count += 1 return None def get_visible_app_index(self, vis_app): """ Get the index of the specified visible app in a list of all visible apps Param: vis_app : the docked app in question Returns : an int (the index) or None of the app could not be found """ vis_list = [] for app in self.app_list: if app.is_visible(): vis_list.append(app) if vis_app in vis_list: return vis_list.index(vis_app) else: return None def get_mutiny_fixed_size(self, icon_size=True): """ Temporary fix for sizing the dock in the Mutiny layout Called when the mutiny layout is being used and calculates either the fixed number of icons that can be displayed in the dock, or the panel size available to the dock before scrolling starts, based on the known size of the Mutiny panels and their applets Params: icon_size : whether to return the result as the number of icons, or as a size in pixels Returns : int - the maximum number of icons that can be displayed in the dock, or a panel size in pixels""" brisk_h = 48 top_panel_h = 28 trash_h = 48 result = (Gdk.Screen.get_default().get_height() - top_panel_h - brisk_h - trash_h) if icon_size: return result // self.get_app_icon_size() else: return result def set_dock_panel_size(self): """ Adjust the size of the dock to prevent it overlapping or expanding over other applets. Also, ensure that the size is set such that any partially visible dock icons at the end of the dock are not shown""" # get the number of icons which can be displayed num_icons = self.dock_fixed_size app_icon_size = self.get_app_icon_size() num_vis = self.get_total_num_visible_apps() if num_icons == -1: # we want to size the dock to the number of visible apps ps = num_vis * app_icon_size elif num_vis > num_icons: # we need to start scrolling ps = num_icons * app_icon_size self.scrolling = True self.enable_app_scrolling() else: # there's no need for scrolling right now ps = num_icons * app_icon_size if self.scrolling: self.scrolling = False self.set_app_scroll_dirs(False) alloc = self.applet.get_allocation() # Scale panel size to accomodate dock on HiDPI displays scale_factor = self.box.get_scale_factor() panel_size = int(self.panel_size / scale_factor) if self.panel_orient in ["top", "bottom"]: # first set the min content width to 0 in case the new maximum we're going # to set is less the current minimim (nastiness can occur...) self.scrolled_win.set_min_content_width(0) self.scrolled_win.set_max_content_width(ps) self.scrolled_win.set_min_content_width(ps) self.scrolled_win.set_min_content_height(0) self.scrolled_win.set_max_content_height(panel_size) self.scrolled_win.set_min_content_height(panel_size) else: self.scrolled_win.set_min_content_height(0) self.scrolled_win.set_max_content_height(ps) self.scrolled_win.set_min_content_height(ps) self.scrolled_win.set_min_content_width(0) self.scrolled_win.set_max_content_width(panel_size) self.scrolled_win.set_min_content_width(panel_size) def get_max_visible_apps(self): """ Gets the maximum number of whole app icons the dock can display in the available panel space Returns: int """ if not self.nice_sizing: if self.avail_panel_space == (1, 1): # can happen during applet startup return 1 pw, ph = self.avail_panel_space else: alloc = self.applet.get_allocation() pw = alloc.width ph = alloc.height app_size = self.get_app_icon_size() if self.panel_orient in ["top", "bottom"]: return pw // app_size else: return ph // app_size def adjust_minimise_pos(self, x, y): """ Adjust the x and y minimise coordinate so that it takes account of the current scroll position Ensure that windows related to dock icons that have been scrolled off the dock minimise to the relevant end of the dock and not beyond. Windows relating to visible dock icons should minimise to their icons Returns: two ints, the x and y of the top left corner where the dock icon should minimise to """ if not self.scrolling: return x, y # no adjustment necessary dx, dy = self.get_dock_root_coords() try: if self.panel_orient in ["top", "bottom"]: final_x = x - self.scrolled_win.get_hadjustment().get_value() if self.nice_sizing: max_w = self.applet.get_allocation().width else: max_w = self.scrolled_win.get_max_content_width() final_x = min(max(final_x, dx), dx + max_w) final_y = y else: final_y = y - self.scrolled_win.get_vadjustment().get_value() if self.nice_sizing: max_h = self.applet.get_allocation().height else: max_h = self.scrolled_win.get_max_content_height() final_y = min(max(final_y, dy), dy + max_h) final_x = x return final_x, final_y except: return x, y def get_app_icon_size(self): """ Gets the size of a single app icon Takes account of the row/column spacing in self.box and, if the panel is horizontal, the extra space (if any) required by the current indicator Returns : int - the size in pixels """ if self.panel_orient in ["top", "bottom"]: return self.panel_size + docked_app_helpers.ind_extra_s(self.indicator) + self.box.get_column_spacing() else: return self.panel_size + docked_app_helpers.ind_extra_s(self.indicator) + self.box.get_row_spacing() def enable_app_scrolling(self): """ Enables scrolling of docked apps """ self.scrolling = True self.scroll_index = 0 # make sure the appropriate app icons will indicate to the user that they can scroll self.set_app_scroll_dirs(True) def reset_scroll_position(self): """ Reset the scroll position back to the start of the dock """ self.scroll_index = 0 if self.panel_orient in ["top", "bottom"]: self.scrolled_win.get_hadjustment().set_value(0) else: self.scrolled_win.get_vadjustment().set_value(0) def set_app_scroll_dirs(self, can_scroll): """ Set the scroll_dir field of the first and last visible dock app If can_scroll is True the fields will be set to indicate scrolling can occur in the appropriate direction. If can_scroll is False, the fields will be set to SCROLL_NONE. Param : can_scroll - bool """ da1 = None da2 = None if self.nice_sizing and (not can_scroll): # check the scroll direction on all apps and set clear where necessary for app in self.app_list: if app.scroll_dir != docked_app.ScrollType.SCROLL_NONE: app.set_scroll_dir(docked_app.ScrollType.SCROLL_NONE) app.queue_draw() return if self.scroll_index != 0: da1 = self.get_visible_app(self.scroll_index) if not self.nice_sizing and (self.dock_fixed_size < 2): return if self.nice_sizing: max_vis = self.get_max_visible_apps() else: max_vis = self.dock_fixed_size if self.scroll_index + max_vis <= self.get_total_num_visible_apps() - 1: da2 = self.get_visible_app(self.scroll_index + max_vis - 1) if not can_scroll: if da1 is not None: da1.set_scroll_dir(docked_app.ScrollType.SCROLL_NONE) if da2 is not None: da2.set_scroll_dir(docked_app.ScrollType.SCROLL_NONE) else: if da1 is not None: da1.set_scroll_dir(docked_app.ScrollType.SCROLL_UP) if da2 is not None: da2.set_scroll_dir(docked_app.ScrollType.SCROLL_DOWN) if da1 is not None: da1.queue_draw() if da2 is not None: da2.queue_draw() def will_overflow(self, extra_space): """ Check to see if the applet will exceed the amount of space allocated to it on the panel if the specified extra space is allocated to it Args: extra_space : the extra space - will typically be the size of an app icon plus spacing specified by self.box and the current indicator type Returns: bool """ if self.avail_panel_space == (): # can happen during applet startup, so in this case just return false return False avail_w, avail_h = self.avail_panel_space if (avail_w <= 1) or (avail_h <= 1): return False alloc = self.applet.get_allocation() if self.applet.get_orient() in [MatePanelApplet.AppletOrient.UP, MatePanelApplet.AppletOrient.DOWN]: return (alloc.width + extra_space) > avail_w else: return (alloc.height + extra_space) > avail_h def unity_cb_handler(self, app_uri, args): """ Handler for Unity API dbus messages If the specified app is in the dock, forward the set the progress and/or count, and redraw the app's icon Args: app_uri : the basename of the .desktop file of the app args : the contents of the dbus message """ # remove the leading part of the app uri df = app_uri.split("://")[1] # search for the an app which has the same desktop file name for app in self.app_list: app_df_path, app_df = os.path.split(app.desktop_file) if app_df == df: # we've found the app - update it... if "count-visible" in args: app.set_counter_visible(args["count-visible"]) if "count" in args: app.set_counter_value(args["count"]) if "progress-visible" in args: app.set_progress_visible(args["progress-visible"]) if "progress" in args: app.set_progress_value(args["progress"]) break # TODO: could do with being a property def get_drag_coords(self): return self.drag_x, self.drag_y def set_drag_coords(self, x, y): self.drag_x = x self.drag_y = y def clear_drag_coords(self): self.drag_x = self.drag_y = -1 def win_activation_timer(args): """ Timer function to be called by GObject.timeout_add and which will activate a specified window Args: args - a tuple containing these items args[0] - the Bamf.Window to activate args[1] - the event time at which the timer was activated Returns: False - to cancel the timer """ window_control.activate_win(args[0], args[1]) sleep(0.01) return False mate-dock-applet-21.10.0/src/dock_about.in000077500000000000000000000375551411344606400202600ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide an about dialog for the MATE dock applet applet The dialog displays the following: applet name and version number licensing info (GPL3) hints and tips a close button """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") else: gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Pango class AboutWindow(Gtk.Window): """Provides the About window """ def __init__(self): """Init for the About window class Create the window and its contents """ super().__init__(title=_("About Dock Applet")) self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_skip_taskbar_hint(True) # we don't want to be in the taskbar self.__btn_close = Gtk.Button(label=_("Close"), stock=Gtk.STOCK_CLOSE) self.__btn_close.connect("button-press-event", self.close_button_press) self.__btn_hints = Gtk.ToggleButton.new_with_label(_("Hints & Tips")) self.__btn_hints.connect("toggled", self.hints_button_toggled) self.__btn_license = Gtk.ToggleButton.new_with_label(_("License")) self.__btn_license.connect("toggled", self.license_button_toggled) self.connect("delete-event", self.win_delete_event) self.set_border_width(5) # use a notebook widget to display the various pages of info self.__nb = Gtk.Notebook() self.__nb.set_show_tabs(False) self.__nb.set_show_border(False) # create a container for the dialog and others for the pages of the notebook if build_gtk2: self.__vbox = Gtk.VBox() self.__vbox.set_spacing(2) self.__vbox_dflt = Gtk.VBox() self.__vbox_dflt.set_spacing(8) self.__vbox_license = Gtk.VBox() self.__vbox_license.set_spacing(8) else: self.__vbox = Gtk.Box() self.__vbox.set_orientation(Gtk.Orientation.VERTICAL) self.__vbox.set_spacing(4) self.__vbox_dflt = Gtk.Box() self.__vbox_dflt.set_orientation(Gtk.Orientation.VERTICAL) self.__vbox_dflt.set_spacing(8) self.__vbox_license = Gtk.Box() self.__vbox_license.set_orientation(Gtk.Orientation.VERTICAL) self.__vbox_license.set_spacing(8) if build_gtk2: self.__hbx = Gtk.HButtonBox() else: self.__hbx = Gtk.ButtonBox() self.__hbx.orientation = Gtk.Orientation.HORIZONTAL self.__hbx.set_layout(Gtk.ButtonBoxStyle.END) self.__hbx.set_spacing(4) self.__hbx.pack_start(self.__btn_hints, False, False, 4) self.__hbx.pack_start(self.__btn_license, False, False, 4) self.__hbx.pack_start(self.__btn_close, False, False, 4) self.__lbl_blank1 = Gtk.Label() self.__image = Gtk.Image() self.__image.set_from_stock(Gtk.STOCK_ABOUT, Gtk.IconSize.DIALOG) self.__lbl_title = Gtk.Label() self.__lbl_title.set_use_markup(True) if build_gtk2: gtk_ver = "GTK2" else: gtk_ver = "GTK3" self.__lbl_title.set_markup("" + _("MATE Dock Applet") + "") self.__lbl_ver = Gtk.Label("V" + "@VERSION@ (" + gtk_ver + ")") self.__lbl_blank2 = Gtk.Label() self.__tb_gpl = Gtk.TextBuffer() self.__tag_size = self.__tb_gpl.create_tag("size") self.__tag_size.set_property("size-points", 9) iter_start = self.__tb_gpl.get_start_iter() self.__tb_gpl.insert_with_tags(iter_start, _("MATE Dock Applet is free software; you can redistribute it and/or modify it " + "under the terms of the GNU General Public Licence as published by the Free " + "Software Foundation; either version 3 of the Licence, or (at your option) " + "any later version. \n\nMATE Dock Applet 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 Licence for more details.\n\nYou should have received a copy of the " + "GNU General Public Licence along with the applet; if not, write to the Free " + "Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA " + "02110-1301 USA\n"), self.__tag_size) self.__tv_gpl = Gtk.TextView.new_with_buffer(self.__tb_gpl) self.__tv_gpl.set_wrap_mode(Gtk.WrapMode.WORD) self.__scrolled_win = Gtk.ScrolledWindow() self.__scrolled_win.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.__scrolled_win.add(self.__tv_gpl) self.__lbl_blurb1 = \ Gtk.Label(_("A dock applet for the MATE desktop")) self.__lbl_blurb2 = \ Gtk.Label(_("This program comes with ABSOLUTELY NO WARRANTY")) self.__lbl_blurb3 = Gtk.Label(_("and is distributed under the GNU General")) self.__lbl_blurb4 = Gtk.Label(_("Public License, version 3 or later")) if build_gtk2: self.__hbx_gpl = Gtk.HBox() else: self.__hbx_gpl = Gtk.Box() self.__hbx_gpl.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbx_gpl.set_spacing(0) self.__lbl_gpl1 = Gtk.Label(_("For details click")) self.__lb_gpl = \ Gtk.LinkButton.new_with_label("http://www.gnu.org/licenses/gpl-3.0.html", _("here")) self.__hbx_gpl.pack_start(self.__lbl_gpl1, False, False, 0) self.__hbx_gpl.pack_start(self.__lb_gpl, False, False, 0) if build_gtk2: self.__vbox_hints = Gtk.VBox() else: self.__vbox_hints = Gtk.Box() self.__vbox_hints.set_orientation(Gtk.Orientation.VERTICAL) # create widgets where where the hints text can be displayed... self.__hints_scrolled_win = Gtk.ScrolledWindow() self.__hints_scrolled_win.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.__tv_hints = Gtk.TextView() self.__tv_hints.set_wrap_mode(Gtk.WrapMode.WORD) self.__tv_hints.set_editable(False) self.__hints_text_buf = Gtk.TextBuffer() self.__tag_hint_bold = self.__hints_text_buf.create_tag("bold", weight=Pango.Weight.BOLD, scale=1.0, justification=Gtk.Justification.CENTER) self.__tag_hint_normal = self.__hints_text_buf.create_tag("normal") self.__tag_hint_normal.set_property("size-points", 9) self.__tv_hints.set_buffer(self.__hints_text_buf) self.set_hints_text() self.__hints_scrolled_win.add(self.__tv_hints) self.__vbox_dflt.pack_start(self.__lbl_ver, False, False, 0) self.__vbox_dflt.pack_start(self.__lbl_blank2, False, False, 0) self.__vbox_license.pack_start(self.__scrolled_win, True, True, 2) self.__vbox_hints.pack_start(self.__hints_scrolled_win, True, True, 0) self.__pg_dflt = self.__nb.append_page(self.__vbox_dflt) self.__pg_license = self.__nb.append_page(self.__vbox_license) self.__pg_hints = self.__nb.append_page(self.__vbox_hints) self.__vbox.pack_start(self.__lbl_blank1, False, False, 2) self.__vbox.pack_start(self.__image, False, False, 2) self.__vbox.pack_start(self.__lbl_title, False, False, 2) self.__vbox.pack_start(self.__nb, True, True, 2) self.__vbox.pack_end(self.__hbx, False, False, 2) self.add(self.__vbox) self.set_size_request(-1, 300) def set_hints_text(self): """ Sets the text which is to be displayed """ the_iter = self.__hints_text_buf.get_end_iter() if not build_gtk2: self.__hints_text_buf.insert_with_tags(the_iter, _("Drag and drop data between applications") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("To easily drag and drop data from one application " + "to another, drag the data from the first application onto the dock " + "icon of the second application. The dock will activate the second " + "application's window, allowing the data to be dragged onto it. " + "Note: the second application must already be running.") + "\n\n", self.__tag_hint_normal) self.__hints_text_buf.insert_with_tags(the_iter, _("Adding new applications to the dock") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("Applications can be dragged and dropped from any menu " + "applet (i.e. the Main Menu, Menu Bar, Advanced Menu, or Brisk Menu) " + "directly onto the dock.") + "\n\n", self.__tag_hint_normal) self.__hints_text_buf.insert_with_tags(the_iter, _("Activating apps with keyboard shortcuts") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("Holding down the (i.e. Windows) key and pressing " + "a number key will activate an app in the dock. For example, " + "pressing ""1"" will activate the first app, ""2"" the second etc. " + "Pressing ""0"" will activate the tenth app. To activate apps 11 to 20, " + "hold down the key as well as .") + "\n\n", self.__tag_hint_normal) self.__hints_text_buf.insert_with_tags(the_iter, _("Opening a new instance of a running application") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("To quickly open a new instance of a running " + "application either hold down the key while clicking the " + "application's dock icon, or middle click on the icon." + "\n\nNote: this works for most, but not all, apps.") + "\n\n", self.__tag_hint_normal) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("Window switching using the mouse wheel") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("To quickly switch between an application's open windows, move " + "the mouse cursor over the apps's dock icon and use the mouse " + "scroll wheel. This will activate and display each window in " + "turn, changing workspaces as necessary.") + "\n\n", self.__tag_hint_normal) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("Panel colour changing") + "\n", self.__tag_hint_bold) self.__hints_text_buf.insert_with_tags(the_iter, "\n" + _("When the applet sets the panel colour for the " + "first time, the result may not look exactly as expected. This is " + "because the default opacity of custom coloured MATE panels is set " + "extremely low, so that the panel appears almost transparent.\n\nTo remedy " + "this, simply right click the panel, select Properties and adjust the " + "panel opacity as required."), self.__tag_hint_normal) def win_delete_event(self, widget, event, data=None): """Callback for the about window delete event Note: the window is not deleted, it is hidden instead so that it can be shown again if required later """ self.hide() return True def close_button_press(self, widget, event): """ callback for the Close button on the About dialog The window is hidden so that it can be shown again """ self.hide() def license_button_toggled(self, widget): """ callback for when the license button is toggled Show the license info if the license button is active, otherwise restore the main or credits info as appropriate Params: widget: the togglebuton """ if self.__btn_license.get_active(): self.__nb.set_current_page(self.__pg_license) else: if self.__btn_hints.get_active(): self.__nb.set_current_page(self.__pg_hints) else: self.__nb.set_current_page(self.__pg_dflt) def hints_button_toggled(self, widget): """ callback for when the credits button is toggled Show the credits info if the credits button is active, otherwise restore the main or license info as appropriate Params: widget: the togglebuton """ if self.__btn_hints.get_active(): self.__nb.set_current_page(self.__pg_hints) else: if self.__btn_license.get_active(): self.__nb.set_current_page(self.__pg_license) else: self.__nb.set_current_page(self.__pg_dflt) def main(): """ main function - debugging code goes here """ about_dlg = AboutDialog() about_dlg.show_all() Gtk.main() return if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_action_list.in000066400000000000000000000230441411344606400214370ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide a window showing a list of an app's actions and allow the user to select one In addition to the actions defined in the app's .desktop file, a Pin/Unpin action will also be added as appropriate The window will function in a similar way to a tooltip i.e. it will appear when the mouse hovers over a dock icon and will disappear if the mouse moves away from the window or the dock applet. """ # # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") from gi.repository import Gtk from gi.repository import Wnck from gi.repository import GdkPixbuf from gi.repository import Gio from gi.repository import Gdk from gi.repository import GObject from gi.repository import MatePanelApplet from gi.repository import Pango from dock_popup import DockPopup from log_it import log_it as log_it CONST_MAX_TITLE_WIDTH = 300 # Max width of the action text CONST_SEP = "--------------------" # text which denotes tree view item is a separator class DockActionList(DockPopup): """ Descendent of Dockup to provide a list of a running app's open windows """ def __init__(self, wnck_screen, panel_orient, scroll_adj): """ create the window and its contents Args: wnck_screen: the wnck_screen of the applet panel_orient : the orientation of the panel scroll_adj : an adjustment to be applied to the window position because the dock has scrolling enabled """ # call the base classes constructor DockPopup.__init__(self, wnck_screen, panel_orient, scroll_adj) # we use a treeview to list each action, so initialise it and its # liststore self.__tree_view = Gtk.TreeView() if not build_gtk2: self.__tree_view.set_valign(Gtk.Align.START) self.__tree_view.set_halign(Gtk.Align.START) self.__tree_view.hexpand = True self.__tree_view.vexpand = True self.__tree_view.set_headers_visible(False) # turn grid lines off, although they still seem to appear in some # themes e.g. Menta self.__tree_view.set_grid_lines(Gtk.TreeViewGridLines.NONE) self.__tree_view.set_hover_selection(True) # the liststore needs to contain an icon, the action text, and the # action itself self.__list_store = Gtk.ListStore(str, Gtk.Action, GdkPixbuf.Pixbuf) self.__icon_renderer = Gtk.CellRendererPixbuf() self.__title_renderer = Gtk.CellRendererText() # set default cell colours and padding self.__title_renderer.set_padding(2, 6) self.set_bg_col(32, 32, 32) # create columns for the treeview self.__col_icon = Gtk.TreeViewColumn("", self.__icon_renderer, pixbuf=2) self.__col_title = Gtk.TreeViewColumn("", self.__title_renderer, text=0) self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) self.__col_title.set_expand(True) self.__col_title.set_max_width(CONST_MAX_TITLE_WIDTH) self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) # add the columns self.__tree_view.set_model(self.__list_store) self.__tree_view.append_column(self.__col_icon) self.__tree_view.append_column(self.__col_title) self.__tree_view.set_row_separator_func(self.check_sep) # add the treeview to the window self.set_main_widget(self.__tree_view) self.__tree_view.connect("button-release-event", self.button_release) self.__tree_view.connect("size-allocate", self.treeview_allocate) def treeview_allocate(self, widget, allocation): """ Event handler for the tree view size-allocate event If the title column has expanded to its maximum width, ellipsize the title text... """ if self.__col_title.get_width() == CONST_MAX_TITLE_WIDTH: self.__col_title.set_min_width(CONST_MAX_TITLE_WIDTH) self.__title_renderer.set_property("ellipsize", Pango.EllipsizeMode.END) self.__tree_view.get_selection().unselect_all() def set_colours(self, panel_colour): """ Sets the treeview colours (background, foreground and highlight) to match the window colours. Note : set_colours must have been called first so that the window colours are set correctly """ DockPopup.set_colours(self, panel_colour) # set strings used to set widget colours - we can't change # the highlight colour, only foreground and background r, g, b = self.bg_col bg_str = "#%.2x%.2x%.2x" % (r, g, b) r, g, b = self.fg_col fg_str = "#%.2x%.2x%.2x" % (r, g, b) # now set the treeview colours self.__title_renderer.set_property("cell-background", bg_str) self.__title_renderer.set_property("foreground", fg_str) self.__icon_renderer.set_property("cell-background", bg_str) def get_num_rows(self): """ Returns the number of rows of data in the list store """ return (self.__list_store.iter_n_children(None)) def button_release(self, widget, event): """ Handler for the button release event If the middle or right mouse button was pressed, do nothing Otherwise, activate the selected item's action Hide the action list Args: widget : the widget the received the signal i.e. our treeview event : the event parameters Returns: True: to stop any other handlers from being invoked """ if event.button != 1: return False # let other handlers run path, col, xrel, yrel = self.__tree_view.get_path_at_pos(event.x, event.y) sel_iter = self.__list_store.get_iter(path) action = self.__list_store.get_value(sel_iter, 1) title = self.__list_store.get_value(sel_iter, 0) if action is not None: self.hide() action.activate() return True def add_separator(self): """ Convenience method to add a separator to the list If there are no items currently in the list then the separator won't be added """ if len(self.__list_store) > 0: self.add_to_list(CONST_SEP, None, False) def add_to_list(self, title, action, show_icon): """ Add an item to the action list Args: title - the title of the window or the action action - a GTK Action to be activated if the item is clicked show_icon - if True the app's icon will be shown alongside the item in the list """ if show_icon: app_icon = self.app_pb else: app_icon = None self.__list_store.append([title, action, app_icon]) def clear_act_list(self): """ Clear the list of open windows """ self.__list_store.clear() def win_button_press(self, widget, event): """ this is for debug puposes only""" Gtk.main_quit() def check_sep(self, model, iter, data=None): """ Check to see if the current row is to be displayed as a separator Args : model : the treeview model (will be self.__list_store) iter : the row in the model we're interested in data : user defined data Returns: Bool """ title = model.get_value(iter, 0) return title == CONST_SEP def main(): """ main function - debugging code goes here """ # thewin = DockWinList() # thewin.set_app_name("Testing....") # thewin.add_to_list(None, False, "Win 1") # thewin.add_to_list(None, True, "Win 2 is active") # thewin.add_to_list(None, False, "Win 3") # thewin.show_all() # thewin.move(100, 110) # pos = thewin.get_position() # size = thewin.get_size() # print("pos %d %d" %(pos[0], pos[1])) # print("size %d %d" %(size[0], size[1])) # thewin.add_mouse_area(Gdk.Rectangle(pos[0]-15, pos[1]-15, size[0]+30, size[1]+30)) # thewin.add_mouse_area(Gdk.Rectangle(0, 0, 48, 500)) # thewin.add_mouse_area(Gdk.Rectangle(48, 110, 100, size[1])) # Gtk.main() return if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_applet.in000077500000000000000000000662511411344606400204260ustar00rootroot00000000000000#!/usr/bin/env python3 """Provide an application dock applet for the MATE panel Create a Mate panel applet and handle events generated by it Note: Functionality for docked apps is provided in docked_app.py Function for the dock is provided in dock.py """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") import os import sys import threading sys.path.insert(1, '@pythondir@') from Xlib.display import Display from Xlib import X, error from gi.repository import Gtk from gi.repository import MatePanelApplet from gi.repository import Gdk from gi.repository import Gio from gi.repository import GObject from gi.repository import GLib from gi.repository import Wnck import xdg.DesktopEntry as DesktopEntry from urllib.parse import urlparse import docked_app import dock from log_it import log_it as log_it drag_dropped = False # nasty global var used to keep track of whether or not a drag-drop event has occurred # define a list of keyboard shortcuts to be used to activate specific apps in the dock # '1' to '0' will correspond to apps 1 to 10 # '1' to '9' will correspond to apps 11 to 20 keyb_shortcuts = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] def applet_button_press(widget, event, the_dock): """Button press event for the applet Handle right button press events only Find the app that was right clicked and make a record of it Args: widget : the widget that was clicked event : the event args the_dock : the Dock object """ # we don't get click events for the right mouse button presumably # because the panel hijacks them in order to produce the context menu # However, we do get button press event for the right mouse button, # so we can do what we need to do here .... if event.button == 3: # right click, so save the app that was clicked because # the_dock.app_with_mouse is going to be set to None when the # right click menu appears and we move the mouse over the menu to # select an option app = the_dock.get_app_at_mouse(event.x, event.y) the_dock.right_clicked_app = app # because the right click menu is about to be shown, we need to hide # the window list the_dock.hide_win_list() the_dock.hide_act_list() elif event.button == 1: if the_dock.panel_expand is not True: # prevent the panel acting on the button press that we got # e.g. so that dragging dock icons does not also start a drag of the panel GObject.signal_stop_emission_by_name(widget, "button_press_event") dx, dy = the_dock.get_drag_coords() if (dx == -1) and (dy == -1): the_dock.set_drag_coords(event.x, event.y) def applet_button_release(widget, event, the_dock): """Button press event for the applet Handle left button release events only If the button is released over a non-running app, start the app If the button is released over a running app that isn't on the current workspace, change workspace If the button is released over a running app: If the app has only a single window open, activate it If window list is showing, hide it If the window list is not visible, show it Since the button has been released, make sure that icon dragging doesn't occur Args: widget : the widget that registered the release event event : the event args the_dock : the Dock object """ if event.button == 1: the_dock.clear_drag_coords() # hide popups the_dock.hide_act_list() app = the_dock.get_app_at_mouse(event.x, event.y) if app is not None: start_app = app.is_running() is False start_app = start_app | (event.state & Gdk.ModifierType.SHIFT_MASK) != 0 if start_app: app.start_app() else: if the_dock.win_switch_unity_style: if app.is_active and app.get_num_windows() > 1: the_dock.do_window_selection(app) else: the_dock.minimize_or_restore_windows(app, True) else: # if the app only has a single window minimize or restore it, otherwise # perform the action specified by the user if app.get_num_windows() == 1: the_dock.minimize_or_restore_windows(app) else: the_dock.do_window_selection(app) # See https://bugs.launchpad.net/ubuntu-mate/+bug/1554128 if event.button == 2: app = the_dock.get_app_at_mouse(event.x, event.y) if app is not None: the_dock.hide_win_list() the_dock.hide_act_list() app.start_app() def applet_enter_notify(widget, event, the_dock): """Enter notify event for the applet Brighten the icon of the app which the mouse is currently over If another app is currently brightened, darken it to normal Set up the right click menu for the dock based on the app which the mouse is currently over Start the timer for showing app window lists Args: widget : the widget that registered the event i.e. the applet event : the event args the_dock : the Dock object """ # get the app underneath the mouse cursor app = the_dock.get_app_at_mouse(event.x, event.y) # if an app is currently highlighted, de-highlight it if the_dock.app_with_mouse is not None: the_dock.app_with_mouse.has_mouse = False the_dock.app_with_mouse.queue_draw() the_dock.app_with_mouse = None # highlight the app under the mouse cursor if app is not None: app.has_mouse = True app.queue_draw() the_dock.app_with_mouse = app # set up the available options for the app the_dock.set_actions_for_app(app) else: the_dock.app_with_mouse = None def applet_leave_notify(widget, event, the_dock): """Leave notify event handle for the applet Unbright any brightened app icon Args: widget : the widget that registered the event i.e. the applet event : the event args the_dock : the Dock object """ if the_dock.app_with_mouse is not None: the_dock.app_with_mouse.has_mouse = False the_dock.app_with_mouse.queue_draw() the_dock.app_with_mouse = None the_dock.stop_act_list_timer() if the_dock.scrolling: the_dock.stop_scroll_timer() def applet_motion_notify(widget, event, the_dock): """Motion notify event for the applet If the docked app under the mouse cursor does not have its icon brightened and another app has a brightened icon then darken the other app # icon and reset the applet tooltip text Then, if the docked app under the mouse cursor does not have its icon brightened then brighten it and setup the applet right click menu Args: widget : the widget that registered the event i.e. the applet event : the event args the_dock : the Dock object """ app = the_dock.get_app_at_mouse(event.x, event.y) if (the_dock.app_with_mouse is not None) and \ (the_dock.app_with_mouse != app): the_dock.app_with_mouse.has_mouse = False the_dock.app_with_mouse.queue_draw() widget.queue_draw() # because a new app is highlighted reset the window list timer and hide # any currently open window list and action list the_dock.hide_win_list() the_dock.reset_act_list_timer() the_dock.hide_act_list() if app is not None: the_dock.app_with_mouse = app # reset the window list timer the_dock.reset_act_list_timer() if the_dock.scrolling and app.scroll_dir != docked_app.ScrollType.SCROLL_NONE: the_dock.reset_scroll_timer() if app.has_mouse is False: app.has_mouse = True app.queue_draw() the_dock.app_with_mouse = app the_dock.set_actions_for_app(app) else: the_dock.app_with_mouse = None the_dock.set_actions_for_app(None) dx, dy = the_dock.get_drag_coords() if (dx != -1) and (dy != -1) and not the_dock.dragging: # we may need to begin a drag operation if widget.drag_check_threshold(dx, dy, event.x, event.y): target_list = widget.drag_dest_get_target_list() context = widget.drag_begin_with_coordinates(target_list, Gdk.DragAction.MOVE, 1, event, -1, -1) applet_drag_begin(widget, context, the_dock) def applet_change_orient(applet, orient, the_dock): """Handler for applet change orientation event Set the dock to the new orientation and re-show the applet Args: applet : the widget that registered the event i.e. the applet orient : the new orientation the_dock : the Dock object """ the_dock.set_new_orientation(orient) the_dock.applet.show_all() the_dock.show_or_hide_app_icons() def applet_size_allocate(applet, allocation, the_dock): """ When the applet can play nicely with panel, ensure that it fits within the allocated space Args : applet : the applet allocation : a Gtk.Allocation - the space in which the applet must fit the_dock : the Dock object """ if the_dock.nice_sizing: the_dock.fit_to_alloc() return def applet_change_size(applet, size, the_dock): """Handler for the applet change size event Resize the icon and recalculate the minimize location of each app in the dock Args: applet : the widget that registered the event i.e. the applet size : the new applet size the_dock : the Dock object """ for app in the_dock.app_list: the_dock.set_app_icon(app, size) def applet_scroll_event(applet, event, the_dock): """ Handler for the scroll event Call the dock's function to move forward/backward through the active app's windows """ # with V0.81 the dock contains a scrolled window and we now only get # a ScrollDirection of SMOOTH here .... if event.direction == Gdk.ScrollDirection.SMOOTH: hasdeltas, dx, dy = event.get_scroll_deltas() if dy < 0: the_dock.do_window_scroll(Gdk.ScrollDirection.DOWN, event.time) elif dy > 0: the_dock.do_window_scroll(Gdk.ScrollDirection.UP, event.time) def applet_drag_begin(applet, context, the_dock): """ Let the dock know we're dragging an icon. Redraw the icon of the app that's being dragged so that the user has visual feedback that the drag has started Set the drag cursor to the app icon Start a timer to monitor the mouse x,y and move the dragged app icon around the dock accordingly """ # we can sometimes get spurious applet-leave events just before a drag # commences. This causes app_with_mouse to be set to None. Therefore we # may need to identify the app under the mouse ourselves... if the_dock.app_with_mouse is None: the_dock.app_with_mouse = the_dock.get_app_under_mouse() if the_dock.app_with_mouse is not None: the_dock.app_with_mouse.set_dragee(True) the_dock.app_with_mouse.queue_draw() Gtk.drag_set_icon_pixbuf(context, the_dock.app_with_mouse.app_pb, 0, 0) the_dock.start_drag_motion_timer(the_dock.app_with_mouse) the_dock.dragging = True # finally, hide the window list if it was being shown the_dock.hide_win_list() the_dock.hide_act_list() the_dock.stop_scroll_timer() def applet_drag_data_get(widget, drag_context, data, info, time): """ Handler the for drag-data-get event Set some dummy text as data for the drag and drop """ data.set_text("", -1) def applet_drag_drop(widget, context, x, y, time, the_dock): """ Handler for the drag-drop event The drag drop is over so: Call Gtk.drag-finish and indicate the drag and drop completed ok Let the dock know that the drag and drop has finished and redraw the dragged app's icon Stop the timer that monitors the mouse position """ app = the_dock.get_dragee() if app is not None: the_dock.stop_drag_motion_timer() app.set_dragee(False) app.queue_draw() Gtk.drag_finish(context, True, False, time) else: # set the drag_dropped module level var so that the drag_data_received event knows # the dnd needs to finish global drag_dropped drag_dropped = True target = widget.drag_dest_find_target(context, None) widget.drag_get_data(context, target, time) return True def applet_drag_data_received(widget, drag_context, x, y, data, info, time, the_dock): """ Called when data has been requested from an external source during a drag drop operation Examine the data - if it is a .desktop file and the app it relates to is not already in the dock, add it. If the data isn't a .desktop file and the app under the mouse cursor is running, activate it so that the dragged data can be dropped there... :param widget: the widget responsible for the event :param drag_context: the dnd context :param x: the x position of the mouse :param y: y the y position of the mouse :param data: the dragged data :param info: :param time: the time of the event :param the_dock: the dock .... """ # examine the data -did we get any uris ? uri_list = data.get_uris() if (uri_list is not None) and (len(uri_list) > 0): # when dragging .desktop files to the dock we only allow one to be added at # a time. Therefore we're only interested in the first item in the list uri = urlparse(uri_list[0]) if uri.scheme == "file": # we're looking for a .desktop file if (uri.path != "") and (os.path.split(uri.path)[1].endswith(".desktop")) and \ (os.path.exists(uri.path)): # we've got a .desktop file, so if it has been dropped we may need # to add it to the dock global drag_dropped if drag_dropped: # add the .desktop file to the dock if it is not already there,,, the_dock.add_app_to_dock(uri.path) # cancel the dnd Gtk.drag_finish(drag_context, True, False, time) drag_dropped = False return else: # the dnd continues .... Gdk.drag_status(drag_context, Gdk.DragAction.COPY, time) return # this is not a .desktop so we need to activate the app under the mouse tgt_app = the_dock.get_app_under_mouse() the_dock.start_da_timer(tgt_app) Gdk.drag_status(drag_context, Gdk.DragAction.COPY, time) def applet_drag_end(widget, context, the_dock): """ Handler for the drag-end event This will be triggered when e.g. the use drags an icon off the panel and releases the mouse button .... Let the dock know that the drag and drop has finished and redraw the dragged app's icon Stop the timer that monitors the mouse position """ the_dock.stop_drag_motion_timer() app = the_dock.get_dragee() if app is not None: app.set_dragee(False) app.queue_draw() the_dock.dragging = False the_dock.clear_drag_coords() def applet_drag_motion(widget, context, x, y, time, the_dock): """ Handler for the drag-motion event :param widget: - the applet :param context: - the dnd context :param x: - x coord of the mouse :param y: - y coord of the mouse :param time: - the time of the event :param the_dock - the dock :return: """ # if the applet isn't dragging an app icon, we may need to examine # the dragged data to see what we are dragging app = the_dock.get_dragee() if app is None: # examine the dragged data so we can decide what to do... tgts = context.list_targets() for t in tgts: if t.name() == "text/uri-list": # if the data contains uris, we need to request the data to # see if it contains a .desktop file widget.drag_get_data(context, t, time) return True # if the dragged data is anything other than a uri, we just need to activate the app under # the mouse... tgt_app = the_dock.get_app_under_mouse() the_dock.start_da_timer(tgt_app) return True else: # continue the dnd... Gdk.drag_status(context, Gdk.DragAction.COPY, time) return True def applet_shortcut_handler(keybinder, the_dock): """ Handler for global keyboard shortcut presses Start the app if it isn't already running If it is already runnning cycle through its windows ... :param keybinder: the keybinder object with the keystring which was pressed e.g. "4" :param the_dock: the dock... """ # get the position in the dock of the app we need to activate if keybinder.current_shortcut in keybinder.shortcuts: app_no = keybinder.shortcuts.index(keybinder.current_shortcut) app = the_dock.get_app_by_pos(app_no) if app is not None: start_app = app.is_running() is False if start_app: app.start_app() else: # if the app only has a single window minimize or restore it # otherwise scroll through all available windows if app.get_num_windows() == 1: the_dock.minimize_or_restore_windows(app) else: the_dock.do_window_scroll(Gdk.ScrollDirection.DOWN, 0, app) def applet_fill(applet): """ Create the applet Register the events that we're interested in getting events for and connect event handlers for them Create a dock and add it V/HBox to the applet Args: applet : the applet """ os.chdir(os.path.expanduser("~")) applet.set_events(applet.get_events() | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK | Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.STRUCTURE_MASK) the_dock = dock.Dock(applet) the_dock.setup_dock() if the_dock.nice_sizing: applet.set_flags(MatePanelApplet.AppletFlags.EXPAND_MAJOR | MatePanelApplet.AppletFlags.EXPAND_MINOR | MatePanelApplet.AppletFlags.FLAGS_NONE) else: applet.set_flags(AppletFlags.FLAGS_NONE) if build_gtk2: applet.add(the_dock.box) else: applet.add(the_dock.scrolled_win) applet.show_all() # make sure that apps pinned to specific workspaces other than the current one # are hidden the_dock.show_or_hide_app_icons() applet.connect("enter-notify-event", applet_enter_notify, the_dock) applet.connect("leave-notify-event", applet_leave_notify, the_dock) applet.connect("motion-notify-event", applet_motion_notify, the_dock) applet.connect("button-press-event", applet_button_press, the_dock) applet.connect("button-release-event", applet_button_release, the_dock) applet.connect("change-orient", applet_change_orient, the_dock) applet.connect("change-size", applet_change_size, the_dock) applet.connect("scroll-event", applet_scroll_event, the_dock) applet.connect("size-allocate", applet_size_allocate, the_dock) if not build_gtk2: # set up drag and drop - gtk3 only # NOTE: we don't get drag-motion events when dragging app icons within the # dock, making it difficult to tell where the mouse pointer is..... # To get around this, dock.py now contains a timer to monitor the # mouse x.y during these sorts of drag and drops. # drag-motion events do fire when dropping from other apps (e.g. caja) # and the drag-motion event is used in these cases # we allow .desktop files to be dropped on the applet, so.... drag_tgts = [Gtk.TargetEntry.new("text/uri-list", 0, 0)] applet.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, drag_tgts, Gdk.DragAction.MOVE) drag_tgts = [Gtk.TargetEntry.new("text/uri-list", 0, 0)] applet.drag_dest_set(Gtk.DestDefaults.MOTION, None, Gdk.DragAction.COPY) applet.drag_dest_add_image_targets() applet.drag_dest_add_text_targets() applet.drag_dest_add_uri_targets() applet.connect("drag-data-get", applet_drag_data_get) applet.connect("drag-drop", applet_drag_drop, the_dock) applet.connect("drag-end", applet_drag_end, the_dock) applet.connect("drag-motion", applet_drag_motion, the_dock) applet.connect("drag-data-received", applet_drag_data_received, the_dock) # set up keyboard shortcuts used to activate apps in the dock keybinder = GlobalKeyBinding() for shortcut in keyb_shortcuts: keybinder.grab(shortcut) keybinder.connect("activate", applet_shortcut_handler, the_dock) keybinder.start() applet.set_background_widget(applet) # hack for panel transparency def applet_factory(applet, iid, data): """Factory routine called when an applet needs to be created Create a dock applet if necessary Args: applet : the applet iid : the id of the applet that needs to be created data : Returns: True if we created a dock applet, False otherwise """ if iid != "DockApplet": return False applet_fill(applet) return True class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.keymap = Gdk.Keymap().get_default() self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.shortcuts = [] def get_mask_combinations(self, mask): return [x for x in range(mask + 1) if not (x & ~mask)] def map_modifiers(self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def grab(self, shortcut): keycode = None accelerator = shortcut.replace("", "") keyval, modifiers = Gtk.accelerator_parse(accelerator) try: keycode = self.keymap.get_entries_for_keyval(keyval).keys[0].keycode except AttributeError: # In older Gtk3 the get_entries_for_keyval() returns an unnamed tuple... keycode = self.keymap.get_entries_for_keyval(keyval)[1][0].keycode modifiers = int(modifiers) self.shortcuts.append([keycode, modifiers]) # Request to receive key press/release reports from other windows that may not be using modifiers catch = error.CatchError(error.BadWindow) self.window.change_attributes(onerror=catch, event_mask=X.KeyPressMask) if catch.get_error(): return False catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(keycode, mod, True, X.GrabModeAsync, X.GrabModeAsync, onerror=catch) self.display.flush() if catch.get_error(): return False return True def run(self): self.running = True while self.running: event = self.display.next_event() if (hasattr(event, 'state')): modifiers = event.state & self.known_modifiers_mask self.current_shortcut = None if event.type == X.KeyPress and [event.detail, modifiers] in self.shortcuts: # Track this shortcut to know which app to activate self.current_shortcut = [event.detail, modifiers] GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close() def ungrab(self): for shortcut in self.shortcuts: self.window.ungrab_key(shortcut[0], X.AnyModifier, self.window) MatePanelApplet.Applet.factory_main("DockAppletFactory", True, MatePanelApplet.Applet.__gtype__, applet_factory, None) def main(): """Main function. Debugging code can go here """ pass if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_color_changer.in000077500000000000000000000347401411344606400217440ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide notification when the desktop background changes to a new picture and, if necessary, change the color of the MATE panel(s) to the dominant color of the new image """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") else: gi.require_version("Gtk", "3.0") from gi.repository import Gtk from gi.repository import Gio import cairo import os import threading import dom_color from collections import namedtuple from time import sleep from log_it import log_it as log_it ChangeTup = namedtuple('ChangeTup', ['settings', 'ored', 'ogreen', 'oblue', 'step_red', 'step_green', 'step_blue']) class PanelColorChanger(object): """ Class to change the color of the MATE panel(s) to the dominant color of the wallpaper image Provide support for wallpaper images only - gradients, solid colours and slideshows will be ignored Change the panel color whenever the wallpaper image is changed Allow only a specific panel's color to be changed, rather than all panels """ def __init__(self, update_callback=None): """ Init for the PanelColorChanger class Use Gio.Settings to monitor the MATE desktop wallpaper setting Args: updated_callback - a function to be called after the panel colors have been updated """ super().__init__() self.update_cb = update_callback self.__red = self.__green = self.__blue = 0 # will hold rgb of the dom color of the wallpaper self.__bg_settings = Gio.Settings.new("org.mate.background") self.__pf = self.__bg_settings.get_string("picture-filename") self.__panel_settings = Gio.Settings.new("org.mate.panel") self.__event_handler_id = 0 self.__toplevel_id = "" def enable_color_change(self): """ Enable panel color changing Monitor the MATE wallpaper setting and create an event handler to be called whenever it changes """ self.__event_handler_id = self.__bg_settings.connect("changed", self.background_changed) def disable_color_change(self): """ Disable panel color changing Disconnect the event handler linked to wallpaper changes """ self.__bg_settings.disconnect(self.__event_handler_id) def do_change_panel_color(self): """ Change the panel colour Call the event handler ourselves in order to change the panel colour """ self.background_changed(None, "picture-filename") def set_single_panel(self, toplevel_id): """ Store the id of the panel which is the only one whose colour is to be changed Args: toplevel_id : the toplevel_id of the panel. If this is an empty string all panels are to have their colour changed """ self.__toplevel_id = toplevel_id def background_changed(self, settings, key): """ Callback for when the desktop wallpaper settings are changed """ if key == "picture-filename": new_pf = self.__bg_settings.get_string("picture-filename") self.__pf = new_pf # we're only interested if the wallpaper is an image file if (new_pf is not None) and (new_pf != ""): pic_ext = os.path.splitext(new_pf)[1] if pic_ext.upper() != ".XML": worker_thread = threading.Thread(target=self.change_panel_colors) worker_thread.start() def get_dom_color(self): """ Get the dominant color of the current desktop image """ colstr = dom_color.get_dom_color(self.__pf) self.__red = int(colstr[0:2], 16) self.__green = int(colstr[2:4], 16) self.__blue = int(colstr[4:6], 16) def change_panel_colors(self): """ Change panel colors to the rgb of the current dominant color Change the colour smoothly over an interval of 0.5 seconds """ self.get_dom_color() change_list = [] # initialise list of panels & settings we need to change # get the list of panels panel_list = self.__panel_settings.get_value("toplevel-id-list").unpack() for panel in panel_list: # do we want to change this panel? do_change = (self.__toplevel_id == "") or \ (self.__toplevel_id == panel) if do_change: # get the settings path for the current panel settings_path = "/org/mate/panel/toplevels/%s/background/" % panel # get this panel's settings psettings = Gio.Settings.new_with_path("org.mate.panel.toplevel.background", settings_path) # get the panel's original colour rgb components colstr = psettings.get_string("color") # the color can be stored as either a set of rgba values or as # an rgb hex ... store_rgba = False store_rgb = False if colstr.startswith("rgba"): store_rgba = True colstrip = colstr[4:255] colstrip = colstrip.strip("()") cols = colstrip.split(",") pr = int(cols[0]) pg = int(cols[1]) pb = int(cols[2]) po = float(cols[3]) elif colstr.startswith("rgb"): store_rgb = True colstrip = colstr.strip("rgb()") cols = colstrip.split(",") pr = int(cols[0]) pg = int(cols[1]) pb = int(cols[2]) else: pr = int(colstr[1:3], 16) pg = int(colstr[3:5], 16) pb = int(colstr[5:7], 16) # we're going to change the panel's color in 25 discrete steps, # so get the difference we need to apply to each color # component each step if pr > self.__red: rs = -(pr - self.__red) else: rs = self.__red - pr if pg > self.__green: gs = -(pg - self.__green) else: gs = self.__green - pg if pb > self.__blue: bs = -(pb - self.__blue) else: bs = self.__blue - pb rs /= 25 gs /= 25 bs /= 25 change_list.append(ChangeTup(settings=psettings, ored=pr, ogreen=pg, oblue=pb, step_red=rs, step_blue=bs, step_green=gs)) # now do the colour change for loop in range(1, 25): for change_item in change_list: if loop == 1: # make sure the panel in question is set to be a colour change_item.settings.set_string("type", "color") # work out new rgb values for this interval new_red = int(change_item.ored + (loop * change_item.step_red)) & 0xff new_blue = int(change_item.oblue + (loop * change_item.step_blue)) & 0xff new_green = int(change_item.ogreen + (loop * change_item.step_green)) & 0xff if store_rgba: change_item.settings.set_string("color", "rgba(%d,%d,%d,%0.6f)" % (new_red, new_green, new_blue, po)) elif store_rgb: change_item.settings.set_string("color", "rgb(%d,%d,%d)" % (new_red, new_green, new_blue)) else: change_item.settings.set_string("color", "#%.2x%.2x%.2x" % (new_red, new_green, new_blue)) # all panels have been changed for this step, so pause for a bit sleep(0.02) # now that we've had a smooth transition, set panel colours to the # final value for change_item in change_list: if store_rgba: change_item.settings.set_string("color", "rgba(%d,%d,%d,%0.6f)" % (self.__red, self.__green, self.__blue, po)) else: change_item.settings.set_string("color", "#%.2x%.2x%.2x" % (self.__red, self.__green, self.__blue)) # finally, call the callback function if self.update_cb is not None: self.update_cb() def wallpaper_filename(self): """ Get the desktop wallpaper image filename """ return self.__pf def panel_rgb(self): """ Get the rgb values of the colour we set the panel(s) to Returns: red, green, blue: integers """ return self.__red, self.__green, self.__blue class TestWindow(Gtk.Window): """Testing window for the color changer code""" def __init__(self): """Init for the Test window class Create the window and its contents """ super().__init__(title=_("Dock color changer test")) self.__button = Gtk.Button(label=_("Close"), stock=Gtk.STOCK_CLOSE) self.__button.connect("button-press-event", self.win_button_press) self.connect("delete-event", self.win_delete_event) self.pcc = PanelColorChanger(self.refresh_ui) self.set_border_width(5) self.__vbox = Gtk.VBox() self.__vbox.set_spacing(2) self.__vbox1 = Gtk.VBox() self.__vbox1.set_spacing(2) self.__hbox = Gtk.HBox() self.__hbox.set_spacing(2) self.__hbx = Gtk.HButtonBox() self.__hbx.set_layout(Gtk.ButtonBoxStyle.END) self.__hbx.pack_start(self.__button, False, False, 4) self.__da = Gtk.DrawingArea() self.__da.set_size_request(128, 128) self.__da.connect("expose-event", self.da_expose_event) self.__lbl_desktop_img = Gtk.Label() self.__lbl_desktop_img.set_use_markup(True) self.__red = self.__green = self.__blue = 0 self.__lbl_red = Gtk.Label(_("red:")) self.__lbl_green = Gtk.Label(_("green:")) self.__lbl_blue = Gtk.Label(_("blue:")) self.__vbox1.pack_start(self.__lbl_red, False, False, 2) self.__vbox1.pack_start(self.__lbl_green, False, False, 2) self.__vbox1.pack_start(self.__lbl_blue, False, False, 2) self.__hbox.pack_start(self.__da, False, False, 2) self.__hbox.pack_start(self.__vbox1, False, False, 2) self.__vbox.pack_start(self.__hbox, False, False, 2) self.__vbox.pack_start(self.__lbl_desktop_img, False, False, 0) self.__vbox.pack_end(self.__hbx, False, False, 5) self.add(self.__vbox) def da_expose_event(self, widget, event): """ Draw a rectangle filled withe dominant color of the image """ # there are lots of drawing operations to be done, so do them to an # offscreen surface and when all is finished copy this to the window offscreen_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 64, 64) ctx = cairo.Context(offscreen_surface) # convert the highlight values to their cairo equivalents self.__red, self.__green, self.__blue = self.pcc.panel_rgb() pfn = os.path.split(self.pcc.wallpaper_filename())[1] self.__lbl_desktop_img.set_markup("%s" % pfn) self.__lbl_red.set_text(_("red:") + " %s" % self.__red) self.__lbl_green.set_text(_("green:") + " %s" % self.__green) self.__lbl_blue.set_text(_("blue:") + " %s" % self.__blue) red = self.__red / 255 green = self.__green / 255 blue = self.__blue / 255 ctx.rectangle(0, 0, 64, 64) ctx.set_source_rgb(red, green, blue) ctx.fill() screen_ctx = self.__da.window.cairo_create() screen_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) screen_ctx.clip() screen_ctx.set_source_surface(offscreen_surface, 0, 0) screen_ctx.paint() ctx = None screen_ctx = None def refresh_ui(self): self.__da.queue_draw() def win_delete_event(self, widget, event, data=None): """Callback for the about window delete event """ Gtk.main_quit() return True def win_button_press(self, widget, event): """ callback for the Ok button on the About dialog """ Gtk.main_quit() def main(): """ main function - debugging code goes here """ test_win = TestWindow() test_win.show_all() Gtk.main() return if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_custom_launcher.in000066400000000000000000000450741411344606400223310ustar00rootroot00000000000000#!/usr/bin/env python3 """Provide a dialog for the dock panel applet that allows a custom app launcher to be added to the dock. Note: This is primarily intended for apps that do not create a .desktop file, or get installed into non-standard locations Mimic the layout of the standard MATE custom launcher dialog, allowing the user to specify the following: App name The command used to launch the app The icon to be displayed in the dock A comment When the Ok button is clicked, a .desktop file will be created in the ~/.local/share/applications and its name will be in the format 'mda_.desktop' to allow the applet to recognise and if necessary give priority to self created custom launchers """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") else: gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GdkPixbuf import os class DockCLWindow(Gtk.Window): """Class to provide the create custom launcher functionality Create and display the create custom launcher dialog Provide properties to get and set the custom launcher name, command, comment and icon """ def __init__(self, ok_callback): """ Constructor for the custom launchr window Create the window and its contents and display them set the callback for the ok button press Args: ok_callback : the method to be called when the ok button is is pressed """ super().__init__(title=_("Create Launcher")) self.set_skip_taskbar_hint(True) self.__icon_filename = "" self.connect("delete-event", self.win_delete_event) # setup the window contents self.set_border_width(5) if build_gtk2: self.__hbox = Gtk.HBox() self.__vbox = Gtk.VBox() else: self.__hbox = Gtk.Box() self.__hbox.set_orientation(Gtk.Orientation.HORIZONTAL) self.__vbox = Gtk.Box() self.__vbox.set_orientation(Gtk.Orientation.VERTICAL) self.__hbox.set_spacing(2) self.__vbox.set_spacing(2) self.__btn_help = Gtk.Button(label=_("Help"), stock=Gtk.STOCK_HELP) self.__btn_help.connect("button-press-event", self.help_btn_press) self.__btn_cancel = Gtk.Button(label=_("Cancel"), stock=Gtk.STOCK_CANCEL) self.__btn_cancel.connect("button-press-event", self.win_cancel_button_press) self.__btn_ok = Gtk.Button(label=_("Ok"), stock=Gtk.STOCK_OK) self.__btn_ok.connect("button-press-event", ok_callback) if build_gtk2: self.__hbox_btns = Gtk.HBox() self.__hbbx = Gtk.HButtonBox() else: self.__hbox_btns = Gtk.Box() self.__hbox_btns.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbbx = Gtk.ButtonBox() self.__hbbx.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbbx.set_spacing(4) self.__hbbx.set_layout(Gtk.ButtonBoxStyle.END) self.__hbbx.pack_start(self.__btn_cancel, False, False, 4) self.__hbbx.pack_end(self.__btn_ok, False, False, 4) if build_gtk2: self.__hbbx1 = Gtk.HButtonBox() else: self.__hbbx1 = Gtk.ButtonBox() self.__hbbx1.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbbx1.set_spacing(4) self.__hbbx1.set_layout(Gtk.ButtonBoxStyle.START) self.__hbbx1.pack_start(self.__btn_help, False, False, 4) self.__hbox_btns.pack_start(self.__hbbx1, False, False, 0) self.__hbox_btns.pack_end(self.__hbbx, False, False, 0) self.__btn_icon = Gtk.Button() self.__img_icon = Gtk.Image() self.__btn_icon.connect("button_press_event", self.img_button_press) self.__btn_icon.set_tooltip_text(_("Click to select an icon")) self.__btn_icon.add(self.__img_icon) if build_gtk2: self.__vbox1 = Gtk.VBox() else: self.__vbox1 = Gtk.Box() self.__vbox1.set_orientation(Gtk.Orientation.VERTICAL) self.__vbox1.set_spacing(4) self.__vbox1.pack_start(self.__btn_icon, False, False, 4) if build_gtk2: self.__table_layout = Gtk.Table(rows=4, columns=2, homogeneous=False) else: self.__table_layout = Gtk.Grid() self.__table_layout.set_column_spacing(2) self.__table_layout.set_row_spacing(2) self.__lbl_name = Gtk.Label() self.__lbl_name.set_use_markup(True) self.__lbl_name.set_label("" + _("Name:") + "") self.__lbl_name.set_alignment(1, 0.5) self.__entry_name = Gtk.Entry() if build_gtk2: self.__hbox_cmd = Gtk.HBox() else: self.__hbox_cmd = Gtk.Box() self.__hbox_cmd.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbox_cmd.set_spacing(2) self.__lbl_cmd = Gtk.Label() self.__lbl_cmd.set_use_markup(True) self.__lbl_cmd.set_label("" + _("Command:") + "") self.__lbl_cmd.set_alignment(1, 0.5) self.__entry_cmd = Gtk.Entry() self.__entry_cmd.set_width_chars(40) self.__btn_cmd = Gtk.Button(label=_("Browse...")) self.__btn_cmd.connect("button-press-event", self.cmd_button_press) self.__hbox_cmd.pack_start(self.__entry_cmd, True, True, 0) self.__hbox_cmd.pack_end(self.__btn_cmd, False, False, 0) self.__lbl_term = Gtk.Label() self.__lbl_term.set_use_markup(True) self.__lbl_term.set_label("" + _("Run in terminal:") + "") self.__lbl_term.set_alignment(1, 0.5) self.__cbtn_term = Gtk.CheckButton() self.__cbtn_term.set_alignment(1, 0.5) self.__lbl_comment = Gtk.Label() self.__lbl_comment.set_use_markup(True) self.__lbl_comment.set_label("" + _("Comment:") + "") self.__entry_comment = Gtk.Entry() self.__lbl_wm_class = Gtk.Label() self.__lbl_wm_class.set_use_markup(True) self.__lbl_wm_class.set_label("" + _("Window Class") + "") self.__entry_wm_class = Gtk.Entry() self.__entry_wm_class.set_sensitive(False) if build_gtk2: self.__table_layout.attach(self.__lbl_name, 0, 1, 0, 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__entry_name, 1, 2, 0, 1, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__lbl_cmd, 0, 1, 1, 2, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__hbox_cmd, 1, 2, 1, 2, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, 2, 2) # Code below can be uncommented if adding terminal apps to the dock # ever becomes a needed thing # self.__table_layout.attach(self.__lbl_term, 0, 1, 2, 3, # Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, # 2, 2) # # self.__table_layout.attach(self.__cbtn_term, 1, 2, 2, 3, # Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, # 2, 2) self.__table_layout.attach(self.__lbl_comment, 0, 1, 2, 3, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__entry_comment, 1, 2, 2, 3, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__lbl_wm_class, 0, 1, 3, 4, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_layout.attach(self.__entry_wm_class, 1, 2, 3, 4, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__table_layout.attach(self.__lbl_name, 0, 0, 1, 1) self.__table_layout.attach(self.__entry_name, 1, 0, 1, 1) self.__table_layout.attach(self.__lbl_cmd, 0, 1, 1, 1) self.__table_layout.attach(self.__hbox_cmd, 1, 1, 1, 1) self.__table_layout.attach(self.__lbl_comment, 0, 2, 1, 1) self.__table_layout.attach(self.__entry_comment, 1, 2, 1, 1) self.__table_layout.attach(self.__lbl_wm_class, 0, 3, 1, 1) self.__table_layout.attach(self.__entry_wm_class, 1, 3, 1, 1) self.__hbox.pack_start(self.__vbox1, False, False, 0) self.__hbox.pack_end(self.__table_layout, True, True, 0) self.__vbox.pack_start(self.__hbox, True, True, 0) self.__vbox.pack_start(Gtk.HSeparator(), False, False, 4) self.__vbox.pack_end(self.__hbox_btns, False, False, 0) self.add(self.__vbox) self.set_default_values() self.show_all() def set_default_values(self): """ Set the window to its default state Clear all text entry fields Set the icon to Gtk.STOCK_EXECUTE """ self.__img_icon.set_from_stock(Gtk.STOCK_EXECUTE, Gtk.IconSize.DIALOG) self.__entry_comment.set_text("") self.__entry_cmd.set_text("") self.__entry_name.set_text("") self.__entry_wm_class.set_text("") def win_delete_event(self, widget, event, data=None): """Callback for the preferences window delete event Do not delete the window, hide it instead so that it can be shown again later if needed """ self.hide() return True def win_cancel_button_press(self, widget, event): """Callback for the preferences window Cancel button press Hide the window """ self.hide() def set_cmd(self, cmd): """ Set the command line used by the launcher Args: cmd : the command line to use """ self.__entry_cmd.set_text(cmd) def get_cmd(self): """ Get the command line used by the launcher Returns: A string containing the command line """ return self.__entry_cmd.get_text() command = property(get_cmd, set_cmd) def cmd_button_press(self, widget, event): """Callback for the browse commands button Show a FileChooserDialog to allow the user to select a command to be associated with the laucher """ fdc_cmd = Gtk.FileChooserDialog(title=_("Choose an application..."), action=Gtk.FileChooserAction.OPEN) # set the working directory of the dialog cmd = self.get_cmd() if cmd is not None: path, filename = os.path.split(cmd) if path is not None: fdc_cmd.set_current_folder(path) btn_cancel = fdc_cmd.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) fdc_cmd.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK) btn_cancel.grab_default() response = fdc_cmd.run() if response == Gtk.ResponseType.OK: self.set_cmd(fdc_cmd.get_filename()) fdc_cmd.destroy() def set_icon_filename(self, filename): """ Set the filename of the icon to be used with the launcher and update the image on the icon selection button Args: filename - the filename """ pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) pixbuf = pixbuf.scale_simple(48, 48, GdkPixbuf.InterpType.BILINEAR) self.__img_icon.set_from_pixbuf(pixbuf) self.__icon_filename = filename def get_icon_filename(self): """ Get the filename of the icon to be used with the launcher Returns: A string containing the filename """ return self.__icon_filename icon_filename = property(get_icon_filename, set_icon_filename) def img_button_press(self, widget, event): """ Callback for the icon button press Show a FileChooserDialog allowing the user to select an icon for the launcher """ fdc_icon = Gtk.FileChooserDialog(title=_("Choose an application..."), action=Gtk.FileChooserAction.OPEN) ff_graphic = Gtk.FileFilter() ff_graphic.set_name(_("Image files")) ff_graphic.add_pattern("*.svg") ff_graphic.add_pattern("*.png") ff_graphic.add_pattern("*.SVG") ff_graphic.add_pattern("*.PNG") ff_graphic.add_pattern("*.xpm") ff_graphic.add_pattern("*.XPM") fdc_icon.add_filter(ff_graphic) # set the working directory of the dialog icon_fn = self.get_icon_filename() if icon_fn is not None: path, filename = os.path.split(icon_fn) if path is not None: fdc_icon.set_current_folder(path) btn_cancel = fdc_icon.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) fdc_icon.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK) btn_cancel.grab_default() response = fdc_icon.run() if response == Gtk.ResponseType.OK: self.set_icon_filename(fdc_icon.get_filename()) fdc_icon.destroy() def get_comment(self): """ Get the comment associated with the launcher Returns: string """ return self.__entry_comment.get_text() def set_comment(self, comment): """ Set the comment associated with the launcher Args: comment - a string containing the comment """ self.__entry_comment.set_text(comment) comment = property(get_comment, set_comment) def get_name(self): """ Get the name associated with the launcher Returns: a string containing the name """ return self.__entry_name.get_text() def set_name(self, name): """ Set the name associated with the launcher Args: name - a string """ self.__entry_name.set_text(name) name = property(get_name, set_name) def get_wm_class(self): """ Get the window wm_class_name Returns: A string containing the wm_class_name """ return self.__entry_wm_class.get_text() def set_wm_class(self, wm_class): """ Set the text of the launcher's wm_class entry widget Args: wm_class : The wm_class name """ self.__entry_wm_class.set_text(wm_class) wm_class = property(get_wm_class, set_wm_class) def get_is_term(self): """ Gets whether or not the app is meant to be run in a terminal Returns: True if the app is to be run in a terminal, False otherwise """ return self.__cbtn_term.get_active() def set_is_term(self, is_term): """ Sets whether or not the app is to be run in a terminal Args: is_term: boolean """ self.__cbtn_term.set_active(is_term) is_terminal_app = property(get_is_term, set_is_term) def help_btn_press(self, widget, event): """ Event handler for the Help button press event Display an explanation of the usages of custom launchers in a dialog box """ md = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, None) md.set_markup('' + _("Custom Launchers") + '') info_text = _("Custom launchers are an advanced feature meant to be used only with apps " +\ "that the dock does not recognise (i.e. they display the wrong name or icon). \n\n" + \ "Normally, this will only happen when the apps have been installed " + \ "to a non standard location within the file system, so for the vast majority of " + \ "apps this feature is not needed.\n\n" + \ "Note: if an app is running when a custom launcher is created for it, the app will " + \ "need to be closed and restarted for the dock to recognise it.") md.format_secondary_text(info_text) md.run() md.destroy() def main(): """main function - debug code can go here""" dclw = DockCLWindow(Gtk.main_quit) dclw.set_skip_taskbar_hint(False) dclw.name = "My test launcher" dclw.comment = "This is a comment" dclw.icon_filename = "/usr/share/pixmaps/ericWeb.png" dclw.command = "/usr/bin/gvim" dclw.wm_class = "Dock_custom_launcher.py" dclw.is_terminal_app = False Gtk.main() if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_info.in000077500000000000000000000161411411344606400200650ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide hints and tips dialog for the MATE dock applet The applet displays the following: useful hints, tips, and information about use of the dock that may not be immediately apparent to the user a close button """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") else: gi.require_version("Gtk", "3.0") import sys from gi.repository import Gtk, Pango class InfoWindow(Gtk.Window): """Provides the Info window""" def __init__(self, running_from_about=False): """Init for the Info window class Create the window and its contents Args: running_from_about - boolean, if True indicates that the window was invoked from the About dialog, and therefore there is no need to show info about how to show this window from the About dialog... (needed in case the window ever has to be invoked by a different method e.g. from the applet's right click menu) """ super().__init__(title=_("Dock Applet hints, tips, and information")) self.__rfa = running_from_about self.__button = Gtk.Button(label=_("Close"), stock=Gtk.STOCK_CLOSE) self.__button.connect("button-press-event", self.win_button_press) self.set_border_width(5) if build_gtk2: self.__vbox = Gtk.VBox() else: self.__vbox = Gtk.Box() self.__vbox.set_orientation(Gtk.Orientation.VERTICAL) self.__vbox.set_spacing(2) if build_gtk2: self.__hbx = Gtk.HButtonBox() else: self.__hbx = Gtk.ButtonBox() self.__hbx.set_orientation = Gtk.Orientation.HORIZONTAL self.__hbx.set_layout(Gtk.ButtonBoxStyle.END) self.__hbx.pack_start(self.__button, False, False, 4) # create widgets where where the info text can be displayed... self.__scrolled_win = Gtk.ScrolledWindow() self.__scrolled_win.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.__tv_info = Gtk.TextView() self.__tv_info.set_wrap_mode(Gtk.WrapMode.WORD) self.__tv_info.set_editable(False) self.__info_text_buf = Gtk.TextBuffer() self.__tag_bold = self.__info_text_buf.create_tag("bold", weight=Pango.Weight.BOLD, scale=1.3, justification=Gtk.Justification.CENTER) self.__tv_info.set_buffer(self.__info_text_buf) self.set_info_text() self.__scrolled_win.add(self.__tv_info) self.__vbox.pack_start(self.__scrolled_win, True, True, 4) self.__vbox.pack_start(Gtk.HSeparator(), False, False, 2) self.__vbox.pack_end(self.__hbx, False, False, 5) self.add(self.__vbox) self.set_size_request(500, 300) # self.set_skip_taskbar_hint(True) # self.set_urgency_hint(True) def set_info_text(self): """ Sets the text which is to be displayed """ the_iter = self.__info_text_buf.get_end_iter() self.__info_text_buf.insert_with_tags(the_iter, "Opening a new instance of a running application" + "\n", self.__tag_bold) self.__info_text_buf.insert(the_iter, "\n" + _("To quickly open a new instance of a running " + "application either hold down the key while clicking the " + "application's dock icon, or middle click on the icon." + "\n\nNote: this works for most, but not all, apps.") + "\n\n") self.__info_text_buf.insert_with_tags(the_iter, "\n" + _("Window switching using the mouse wheel") + "\n", self.__tag_bold) self.__info_text_buf.insert(the_iter, "\n" + _("To quickly switch between an application's open windows, move " + "the mouse cursor over the apps's dock icon and use the mouse " + "scroll wheel. This will activate and display each window in " + "turn, changing workspaces as necessary.") + "\n\n") self.__info_text_buf.insert_with_tags(the_iter, "\n" + _("Panel colour changing") + "\n", self.__tag_bold) self.__info_text_buf.insert(the_iter, "\n" + _("When the applet sets the panel colour for the " + "first time, the result may not look exactly as expected. This is " + "because the default opacity of custom coloured MATE panels is set " + "extremely low, so that the panel appears almost transparent.\n\nTo remedy " + "this, simply right click the panel, select Properties and adjust the " + "panel opacity as required.")) if not self.__rfa: self.__info_text_buf.insert_with_tags(the_iter, "\n" + _("Click the 'Hints & Tips' button on the applet 'About' dialog box " "to see this information again."), self.__tag_bold) def win_button_press(self, widget, event): """ callback for the Ok button on the Info dialog The window is hidden so that it can be shown again """ self.destroy() def main(): """ main function - debugging code goes here """ info_win = InfoWindow(True) info_win.show_all() Gtk.main() return if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_popup.in000066400000000000000000001211221411344606400202660ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide a base class for the dock's popup windows. Such a window will function in a similar way to a tooltip i.e. it will appear when the mouse hovers over a dock icon and will disappear if the mouse moves away from the window or the dock applet. The window's foreground/background colours will be set from the current theme or if the dock applet is setting the panel colour, the panel colours The will use a grid/table to display a border around the window contents, and descendant classes will need to create and set the window's main widget/container """ # # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") from gi.repository import Gtk from gi.repository import GdkPixbuf from gi.repository import Gio from gi.repository import Gdk from gi.repository import GObject from gi.repository import MatePanelApplet import cairo from time import sleep import docked_app from math import pi CONST_TIMER_DELAY = 1000 CONST_ICON_SIZE = 16 class DockPopup(Gtk.Window): """ Attributes : __mouse_areas : a list containing Gdk.Rectangle objects - used when the window has been shown and defines the on screen areas in which the mouse pointer must stay, otherwise the window list will be hidden. The rectangles should therefore include the applet, the area between the window and the applet, and the window itself with a suitable buffer area around it __timer_id : a ref to a timer used for periodically checking the mouse cursor position to see if it is within the areas specified in __mouse_areas __the_app : the docked_app to which the window list relates __icontheme : used for drawing app icons in the popup. This is set from the Gtk.Icon used by the dock, and will therefore track changes to the icon theme whilst the dock is running __icon_size : the size in pixels at which app icons will be drawn __win_w : the width of the window __win_h : the height of the window __bgr, __bgg, __bgb : the r,g,b panel colour components (0-255) __fgr, __fgg, __fgb : the r,g,b foreground colour components __hlr, __hlg, __hlb : the r,g,b highlight colour components The attributes below are used for positioning this window relative to the applet and it's panel: __app_x : the x position of the docked app in root coordinates __app_y : the y position of the docked app in root coordinates __applet_x : the x position of the applet in root coordinates __applet_y : the y position of the applet in root coordinates __applet_w : the width of the applet in pixels __applet_h : the height of the applet in pixels __panel_orient : the orienation of the MATE panel the applet is on __do_window_shaping : whether or not the window can be shaped, e.g. have rounded corners. Depends on Gtk3 and gi module >= 3.26.0 """ def __init__(self, wnck_screen, panel_orient, scroll_adj): """ create the window and its contents Args: wnck_screen: the wnck_screen of the applet panel_orient : the orientation of the panel scroll_adj : an adjustment to be applied to the window position because the dock has scrolling enabled """ def create_drawing_area(width, height, draw_event): # convenience func to create a drawing area with a specified # width, height and draw event da = Gtk.DrawingArea() da.set_size_request(width, height) if build_gtk2: da.connect("expose-event", draw_event) else: da.connect("draw", draw_event) return da super().__init__(title="") self.wnck_screen = wnck_screen self.set_decorated(False) # we don't want a titlebar.. self.set_skip_taskbar_hint(True) # we don't want to be in the taskbar self.set_accept_focus(False) self.set_keep_above(True) self.__scroll_adj = scroll_adj self.__icontheme = None self.__icon_size = 16 # small default icon size self.__timer_id = None self.__dismissed = False self.__the_app = None self.__app_pb = None self.__win_w = 0 self.__win_h = 0 self.__app_x = 0 self.__app_y = 0 self.__panel_orient = panel_orient self.__bgr = 0 self.__bgg = 0 self.__bgb = 0 self.__fgr = 0 self.__fgg = 0 self.__fgb = 0 self.__hlr = 0 self.__hlg = 0 self.__hlb = 0 self.__applet_x = 0 self.__applet_y = 0 self.__applet_w = 0 self.__applet_y = 0 self.__applet_h = 0 # create ui if build_gtk2: self.__grid = Gtk.VBox() self.__grid.set_spacing(0) self.__grid = Gtk.Table(rows=3, columns=3) self.__grid.set_row_spacings(0) self.__grid.set_col_spacings(0) else: self.__grid = Gtk.Grid() self.__grid.set_orientation(Gtk.Orientation.VERTICAL) self.__grid.hexpand = True self.__grid.vexpand = True self.hexpand = True self.vexpand = True # set vars used when drawing the window border self.__border_width = 15 self.__border_line = 4 self.__line_width = 2 self.__line_curve = 5.0 self.__pointer_size = 16 # add drawing areas to all outsides of the 3x3 grid # if we're showing shaped windows then the drawing area nearest the panel # needs to be expanded so that that portion of the window it can be shaped # into a pointer to the app icon if build_gtk2: self.__do_window_shaping = False else: gi_ver = GObject.pygobject_version self.__do_window_shaping = gi_ver[0] > 3 or ((gi_ver[0] == 3) and (gi_ver[1] >= 26)) da_height = self.__border_width if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: da_height += self.__pointer_size self.__da_top = create_drawing_area(self.__border_width, da_height, self.draw_top_border) da_width = self.__border_width if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: da_width += self.__pointer_size self.__da_left = create_drawing_area(da_width, self.__border_width, self.draw_left_border) da_width = self.__border_width if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: da_width += self.__pointer_size self.__da_right = create_drawing_area(da_width, self.__border_width, self.draw_right_border) da_height = self.__border_width if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.UP: da_height += self.__pointer_size self.__da_bottom = create_drawing_area(self.__border_width, da_height, self.draw_bottom_border) if build_gtk2: self.__grid.attach(self.__da_top, 0, 3, 0, 1, xpadding=0, ypadding=0) self.__grid.attach(self.__da_left, 0, 1, 1, 2) self.__grid.attach(self.__da_right, 2, 3, 1, 2) self.__grid.attach(self.__da_bottom, 0, 3, 2, 3) else: self.__grid.attach(self.__da_top, 0, 0, 3, 1) self.__grid.attach(self.__da_left, 0, 1, 1, 1) self.__grid.attach(self.__da_right, 2, 1, 1, 1) self.__grid.attach(self.__da_bottom, 0, 2, 3, 1) self.add(self.__grid) self.__mouse_areas = [] # connect handlers for the show and hide events self.connect("show", self.win_shown) self.connect("hide", self.win_hidden) self.connect("configure-event", self.win_configure) self.connect("size-allocate", self.size_allocate) def set_main_widget(self, widget): """ Attaches the main component (a widget or container) to the center position of the grid Args: widget : the widget or container to add """ if build_gtk2: self.__grid.attach(widget, 1, 2, 1, 2) else: self.__grid.attach(widget, 1, 1, 1, 1) def set_colours(self, panel_colour): """ Sets the window background, foreground and highlight colours to default values The background colour will match the panel containing the applet. If a custom colour is set for the panel, use that for the background and set the foreground colour to be either full white or black (depending on the background colour). The highlight colour will Also be set depending on the background colour. For Gtk3, if the panel is set to use the theme's colours, the background, foreground and highilight colours will all be set from the current theme For Gtk2, where we can't access the styles associated with the current theme because of introspection errors, set the background to black, foreground to white and the highlight colour to a dark grey Args: panel_colour : If a custom panel colour has been set, this will be a tuple of 3 x int - the r, g, b colour components. Otherwise it will be None """ if panel_colour is None: if build_gtk2: self.__bgr = self.__bgg = self.__bgb = 0 self.__fgr = self.__fgg = self.__fgb = 255 self.__hlr = self.__hlg = self.__hlb = 64 else: context = self.get_style_context() state = Gtk.StateType.NORMAL # we want the colors for the MATE panel (preferably), or the # Gnome menu bar # context.add_class("gnome-panel-menu-bar") # context.add_class("mate-panel-menu-bar") # background c_info = context.lookup_color("dark_bg_color") if c_info[0]: bgcol = c_info[1] self.__bgr = int(bgcol.red * 255) self.__bgg = int(bgcol.green * 255) self.__bgb = int(bgcol.blue * 255) c_info = context.lookup_color("dark_fg_color") if c_info[0]: fcol = c_info[1] self.__fgr = int(fcol.red * 255) self.__fgg = int(fcol.green * 255) self.__fgb = int(fcol.blue * 255) sel_bg = context.lookup_color("theme_selected_bg_color") if sel_bg[0]: hcol = sel_bg[1] self.__hlr = int(hcol.red * 255) self.__hlg = int(hcol.green * 255) self.__hlb = int(hcol.blue * 255) else: # assume what is hopefully a decent looking highlight # colour self.__hlr = (self.__bgr + 64) % 256 self.__hlg = (self.__bgg + 64) % 256 self.__hlb = (self.__bgb + 64) % 256 else: # custom panel colour... self.__bgr = panel_colour[0] self.__bgg = panel_colour[1] self.__bgb = panel_colour[2] # set foreground colour according to the background colour # 384 equates to average rgb values of 128 per colour component and # therefore represents a mid value if (self.__bgr + self.__bgg + self.__bgb) > 384: self.__fgr = self.__fgg = self.__fgb = 0 # dark fg colour else: self.__fgr = self.__fgg = self.__fgb = 255 # light fg color # highlight colour self.__hlr = (self.__bgr + 64) % 256 self.__hlg = (self.__bgg + 64) % 256 self.__hlb = (self.__bgb + 64) % 256 def set_bg_col(self, bgr, bgg, bgb): """ Sets the background colour of the window Also, set a foreground colour that will contrast with the background colour (so we can read text etc...) Args: bgr, bgg, bgb : the background rgb colour components """ self.__bgr = bgr self.__bgg = bgg self.__bgb = bgb # set foreground colour according to the background colour if (bgr + bgg + bgb) > 384: # 384 equates to average rgb values of 128 # per colour component and therefore # represents a mid value self.__fgr = self.__fgg = self.__fgb = 0 # dark fg colour else: self.__fgr = self.__fgg = self.__fgb = 255 # light fg color def set_fg_col(self, fgr, fgg, fgb): """ Put some stuff here... """ self.__fgr = fgr self.__fgg = fgg self.__fgb = fgb def win_shown(self, widget): """ Event handler for the window's show event Get the window's size so that its position can be set and mouse areas created """ if build_gtk2: self.set_win_position() else: if (self.__win_w == 0) or (self.__win_h == 0): self.__win_w, self.__win_h = self.get_size() self.start_mouse_area_timer() def set_win_position(self): """ Move the window so that it appears near the panel and centered on the app (has to be done here for Gtk3 reasons) Create mouse areas as required so we can check when the mouse leaves the window Instantiate a timer to periodically check the mouse cursor position """ def create_rect(x, y, w, h): """ Convenience function to create and return a Gdk.Rectangle (needed with Gtk3) """ if build_gtk2: rect = Gdk.Rectangle(0, 0, 0, 0) else: rect = Gdk.Rectangle() rect.x = x rect.y = y rect.width = w rect.height = h return rect # set how many pixels away from the panel the window list will appear if self.__do_window_shaping: panel_space = 5 else: panel_space = 10 # size of the border (in pixels) around the window # list where the mouse must remain, outside of which # the window list will hide win_border = 15 screen = self.get_screen() # get the monitor that the applet is on # Note: we can't rely on the panel's dconf settings for this # as the monitor setting there doesn't seem to work reliably monitor = screen.get_monitor_at_point(self.__applet_x, self.__applet_y) if build_gtk2: mon_geom = create_rect(0, 0, 0, 0) screen.get_monitor_geometry(monitor, mon_geom) else: mon_geom = screen.get_monitor_geometry(monitor) # if the size of the window hasnt been set (because the configure-event # doesn't always fire if the window list is empty) use an alternative # method to get the window width and height # work out where to place the window - adjacent to the panel and # centered on the highlighted dock app and add appropriate mouse areas # first, a mouse area to cover the entire applet self.add_mouse_area(create_rect(self.__applet_x, self.__applet_y, self.__applet_w, self.__applet_h)) app_alloc = self.__the_app.drawing_area.get_allocation() if self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: centre_pos = self.__app_y + (app_alloc.height / 2) - self.__scroll_adj win_x = self.__applet_x + self.__applet_w + panel_space win_y = centre_pos - (self.__win_h / 2) # adjust win_y in case we're off the top the screen, or the # monitor ... if win_y < mon_geom.y + panel_space: win_y = panel_space # adjust win_y if case the window list extends beyound the end of # the panel .. if (win_y + self.__win_h) > mon_geom.y + mon_geom.height: win_y = mon_geom.y + mon_geom.height - self.__win_h - panel_space # setup a new mouse area covering the window (minus a border) and # extending to the panel self.add_mouse_area(create_rect(self.__applet_x, win_y - win_border, win_x + self.__win_w + win_border, self.__win_h + (2 * win_border))) elif self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: centre_pos = self.__app_y + (app_alloc.height / 2) - self.__scroll_adj win_x = self.__applet_x - panel_space - self.__win_w win_y = centre_pos - (self.__win_h / 2) # adjust win_y in case we're off the top the screen... if win_y < mon_geom.y + panel_space: win_y = mon_geom.y + panel_space # adjust win_y if case the window list extends beyound the end of # the panel .. if (win_y + self.__win_h) > mon_geom.y + mon_geom.height: win_y = mon_geom.y + mon_geom.height - self.__win_h - panel_space # setup a new mouse area covering the window (minus a border) and # extending to the panel self.add_mouse_area(create_rect(win_x - win_border, win_y - win_border, (self.__win_w + win_border + panel_space + app_alloc.width), self.__win_h + (2 * win_border))) elif self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: centre_pos = (self.__app_x + app_alloc.width / 2) - self.__scroll_adj win_x = centre_pos - (self.__win_w / 2) win_y = self.__applet_y + self.__applet_h + panel_space # adjust win_x in case we're off the left of the screen... if win_x < mon_geom.x + panel_space: win_x = mon_geom.x + panel_space # adjust win_x if case the window list extends beyond the end of # the panel .. if (win_x + self.__win_w) > mon_geom.x + mon_geom.width: win_x = mon_geom.x + mon_geom.width - self.__win_w - panel_space # setup a new mouse area covering the window (minus a border) and # extending to the panel self.add_mouse_area(create_rect(win_x - win_border, self.__applet_y, self.__win_w + (2 * win_border), win_y + self.__win_h + win_border)) else: centre_pos = (self.__app_x + app_alloc.width / 2) - self.__scroll_adj win_x = centre_pos - (self.__win_w / 2) win_y = self.__applet_y - panel_space - self.__win_h # adjust win_x in case we're off the left of the screen... if win_x < mon_geom.x + panel_space: win_x = mon_geom.x + panel_space # adjust win_x if case the window list extends beyond the end of # the panel .. if (win_x + self.__win_w) > mon_geom.x + mon_geom.width: win_x = mon_geom.x + mon_geom.width - self.__win_w - panel_space # setup a new mouse area covering the window (minus a border) and # extendingto the panel self.add_mouse_area(create_rect(win_x - win_border, win_y - win_border, self.__win_w + (2 * win_border), self.__win_h + win_border + panel_space + app_alloc.height)) self.move(win_x, win_y) def start_mouse_area_timer(self): """ Start the timer that that monitors the mouse position """ # remove any old timer... self.stop_mouse_area_timer() self.__timer_id = GObject.timeout_add(CONST_TIMER_DELAY, self.do_timer) def stop_mouse_area_timer(self): """ Stop the timer that monitors the mouse position """ # if self.__timer_id is not None: GObject.source_remove(self.__timer_id) self.__timer_id = None def win_configure(self, widget, event): """ Event handler for the window's configure event Stores the new width and height of the window Args: widget : the widget that caused the event (i.e. self) event : the event parameters """ # if the new size of the window isn't the same as the old one, we need # to recaclulate the window position and mouse areas return def size_allocate(self, widget, event): def draw_rounded(cr, area, radius): """ draws rectangles with rounded (circular arc) corners """ # Attribution: https://gist.github.com/kamiller/3013605 a, b, c, d = area cr.arc(a + radius, c + radius, radius, 2 * (pi / 2), 3 * (pi / 2)) cr.arc(b - radius, c + radius, radius, 3 * (pi / 2), 4 * (pi / 2)) cr.arc(b - radius, d - radius, radius, 0 * (pi / 2), 1 * (pi / 2)) # ;o) cr.arc(a + radius, d - radius, radius, 1 * (pi / 2), 2 * (pi / 2)) cr.close_path() cr.stroke_preserve() if (event.width != self.__win_w) or (event.height != self.__win_h): self.__win_w = event.width self.__win_h = event.height self.set_win_position() if not self.__do_window_shaping: return # round the corners of the portion of the window containing the widget and border allocation = self.get_allocation() surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, allocation.width, allocation.height) ctx = cairo.Context(surface) if self.__panel_orient == MatePanelApplet.AppletOrient.UP: draw_rounded(ctx, [allocation.x, allocation.width, allocation.y, allocation.height - self.__pointer_size], 10) elif self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: draw_rounded(ctx, [allocation.x, allocation.width, allocation.y + self.__pointer_size, allocation.height], 10) elif self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: draw_rounded(ctx, [self.__pointer_size + 1, allocation.width, allocation.y, allocation.height], 10) else: draw_rounded(ctx, [allocation.x, allocation.width - self.__pointer_size, allocation.y, allocation.height], 10) ctx.set_source_rgba(1, 1, 1, 1) ctx.fill() # now create a pointer to the app's icon in the dock if self.__panel_orient == MatePanelApplet.AppletOrient.UP: ctx.move_to(allocation.width / 2 - self.__pointer_size, allocation.height - self.__pointer_size) ctx.line_to(allocation.width / 2, allocation.height) ctx.line_to(allocation.width / 2 + self.__pointer_size, allocation.height - self.__pointer_size) elif self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: ctx.move_to(allocation.width / 2 - self.__pointer_size, self.__pointer_size) ctx.line_to(allocation.width / 2, 0) ctx.line_to(allocation.width / 2 + self.__pointer_size, self.__pointer_size) elif self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: ctx.move_to(self.__pointer_size, allocation.height / 2 - self.__pointer_size) ctx.line_to(0, allocation.height / 2) ctx.line_to(self.__pointer_size, allocation.height / 2 + self.__pointer_size) else: ctx.move_to(allocation.width - self.__pointer_size, allocation.height / 2 - self.__pointer_size) ctx.line_to(allocation.width, allocation.height / 2) ctx.line_to(allocation.width - self.__pointer_size, allocation.height / 2 + self.__pointer_size) ctx.stroke_preserve() ctx.set_source_rgba(1, 1, 1, 1) ctx.fill() region = Gdk.cairo_region_create_from_surface(surface) self.shape_combine_region(region) def draw_top_border(self, drawing_area, event): """ Draw the top of a rectangle with rounded corners to provide a border for the window """ # in gtk3 the last param is a cairo context, in gtk2 we need to # create one if build_gtk2: ctx = drawing_area.window.cairo_create() ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) ctx.clip() else: ctx = event alloc = drawing_area.get_allocation() # fill with background the background colour first ctx.rectangle(0, 0, alloc.width, alloc.height) ctx.set_source_rgb(self.__bgr / 255, self.__bgg / 255, self.__bgb / 255) ctx.fill() # do the actual drawing ctx.set_operator(cairo.OPERATOR_OVER) ctx.set_line_width(self.__line_width) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_source_rgb(self.__fgr / 255, self.__fgg / 255, self.__fgb / 255) # the position of the top, left and right border lines depend on whether or not we need to # shape the window into a pointer to the app icon top_extent = left_extent = self.__border_line right_extent = alloc.width - self.__border_line if self.__do_window_shaping: if self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: top_extent += self.__pointer_size elif self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: left_extent += self.__pointer_size elif self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: right_extent -= self.__pointer_size ctx.move_to(left_extent, alloc.height) ctx.line_to(left_extent, top_extent + self.__line_curve) ctx.curve_to(left_extent, top_extent + self.__line_curve, left_extent, top_extent, left_extent + self.__line_curve, top_extent) if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.DOWN: # extend the border line into the pointer ctx.line_to(alloc.width / 2 - self.__pointer_size + 1, top_extent) ctx.line_to(alloc.width / 2, top_extent - self.__pointer_size + 1) ctx.line_to(alloc.width / 2 + self.__pointer_size - 1, top_extent) ctx.line_to(right_extent - self.__line_curve, top_extent) ctx.curve_to(right_extent - self.__line_curve, top_extent, right_extent, top_extent, right_extent, top_extent + self.__line_curve) ctx.line_to(right_extent, alloc.height) ctx.stroke() def draw_left_border(self, drawing_area, event): """ Draw the left hand side of the window border """ if build_gtk2: ctx = drawing_area.window.cairo_create() ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) ctx.clip() else: ctx = event alloc = drawing_area.get_allocation() # fill with background colour ctx.rectangle(0, 0, alloc.width, alloc.height) ctx.set_source_rgb(self.__bgr / 255, self.__bgg / 255, self.__bgb / 255) ctx.fill() ctx.set_operator(cairo.OPERATOR_OVER) ctx.set_line_width(self.__line_width) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_source_rgb(self.__fgr / 255, self.__fgg / 255, self.__fgb / 255) left_extent = self.__border_line # the position of the left border depends on whether or not we're doing window shaping if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: left_extent += self.__pointer_size ctx.move_to(left_extent, 0) if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: # extend the border line into the pointer ctx.line_to(left_extent, alloc.height / 2 - self.__pointer_size + 1) ctx.line_to(self.__border_line, alloc.height / 2) ctx.line_to(left_extent, alloc.height / 2 + self.__pointer_size - 1) ctx.line_to(left_extent, alloc.height) ctx.stroke() def draw_right_border(self, drawing_area, event): """ Draw the right hand side of the window border """ if build_gtk2: ctx = drawing_area.window.cairo_create() ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) ctx.clip() else: ctx = event alloc = drawing_area.get_allocation() ctx.rectangle(0, 0, alloc.width, alloc.height) ctx.set_source_rgb(self.__bgr / 255, self.__bgg / 255, self.__bgb / 255) ctx.fill() ctx.set_operator(cairo.OPERATOR_OVER) ctx.set_line_width(self.__line_width) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_source_rgb(self.__fgr / 255, self.__fgg / 255, self.__fgb / 255) right_extent = alloc.width - self.__border_line if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: right_extent -= self.__pointer_size ctx.move_to(right_extent, 0) if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: # extend the border line into the pointer ctx.line_to(right_extent, alloc.height / 2 - self.__pointer_size + 1) ctx.line_to(alloc.width - self.__border_line, alloc.height / 2) ctx.line_to(right_extent, alloc.height / 2 + self.__pointer_size - 1) ctx.line_to(right_extent, alloc.height) ctx.stroke() def draw_bottom_border(self, drawing_area, event): """ Draw the bottom of the window border with rounded corners """ if build_gtk2: ctx = drawing_area.window.cairo_create() ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) ctx.clip() else: ctx = event alloc = drawing_area.get_allocation() ctx.rectangle(0, 0, alloc.width, alloc.height) ctx.set_source_rgb(self.__bgr / 255, self.__bgg / 255, self.__bgb / 255) ctx.fill() ctx.set_operator(cairo.OPERATOR_OVER) ctx.set_line_width(self.__line_width) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_source_rgb(self.__fgr / 255, self.__fgg / 255, self.__fgb / 255) # the lower, right and left extents of the border depend on whether or not we need to # shape the window into a pointer to the app icon left_extent = self.__border_line right_extent = alloc.width - self.__border_line lower_extent = alloc.height - self.__border_line if self.__do_window_shaping: if self.__panel_orient == MatePanelApplet.AppletOrient.UP: lower_extent -= self.__pointer_size elif self.__panel_orient == MatePanelApplet.AppletOrient.RIGHT: left_extent += self.__pointer_size elif self.__panel_orient == MatePanelApplet.AppletOrient.LEFT: right_extent -= self.__pointer_size ctx.move_to(left_extent, 0) ctx.line_to(left_extent, lower_extent - self.__line_curve) ctx.curve_to(left_extent, lower_extent - self.__line_curve, left_extent, lower_extent, left_extent + self.__line_curve, lower_extent) if self.__do_window_shaping and self.__panel_orient == MatePanelApplet.AppletOrient.UP: # draw a pointer ctx.line_to(alloc.width / 2 - self.__pointer_size + 1, lower_extent) ctx.line_to(alloc.width / 2, lower_extent + self.__pointer_size - 1) ctx.line_to(alloc.width / 2 + self.__pointer_size - 1, lower_extent) ctx.line_to(right_extent - self.__line_curve, lower_extent) ctx.curve_to(right_extent - self.__line_curve, lower_extent, right_extent, lower_extent, right_extent, lower_extent - self.__line_curve) ctx.line_to(right_extent, 0) ctx.stroke() def win_hidden(self, widget): """ Event handler for the window's hide event Delete the timer object """ self.stop_mouse_area_timer() def do_timer(self): """ Check the current mouse position and if it is not within any of the rectangles in self.__mouse_area hide the window """ # get the mouse x y root_win, x, y, mask = self.get_screen().get_root_window().get_pointer() if not self.point_is_in_mouse_areas(x, y): self.hide() self.__timer_id = None return False return True def clear_mouse_areas(self): """ Clear the mouse areas list """ self.__mouse_areas = [] def add_mouse_area(self, rect): """ Add a rectangle to the __mouse_area_list Args: rect - a Gdk.Rectangle """ self.__mouse_areas.append(rect) def point_is_in_mouse_areas(self, x, y): """ Checks to see if a specified position on the screen is within any of the self.__mouse_areas rectangle list Args: x : the x position y : the y position Returns: True if the position is within one of the rectangles in self.__mouse_areas, False otherwise """ for rect in self.__mouse_areas: if ((x >= rect.x) and (x <= rect.x + rect.width)) and \ ((y >= rect.y) and (y <= rect.y + rect.height)): return True return False def get_app(self): """ Return the docked app the window list refers to Returns: A docked_app """ return self.__the_app def set_app(self, app): """ Set the docked app the window list refers to Draw the app icon at an appropriate size for the list Args : app - a docked_app """ self.__the_app = app if self.__icontheme is not None: self.get_app_icon() the_app = property(get_app, set_app) def get_app_icon(self): """ Draws the app icon and stores it in self.__app_pb for later use self.__the_app and the icon theme and size must have been set before this is called.... """ if self.__icontheme.has_icon(self.__the_app.icon_name): # draw the app icon at the size we want icon_info = self.__icontheme.choose_icon([self.__the_app.icon_name, None], self.__icon_size, 0) try: pixbuf = icon_info.load_icon() except GLib.GError: # default to a stock icon if we couldn't load the app # icon pixbuf = self.render_icon(Gtk.STOCK_EXECUTE, Gtk.IconSize.DND, None) else: pixbuf = self.the_app.app_pb.scale_simple(self.__icon_size, self.__icon_size, GdkPixbuf.InterpType.BILINEAR) self.__app_pb = pixbuf @property def bg_col(self): return self.__bgr, self.__bgg, self.__bgb @property def fg_col(self): return self.__fgr, self.__fgg, self.__fgb @property def hl_col(self): return self.__hlr, self.__hlg, self.__hlb @property def icon_size(self): return self.__icon_size @icon_size.setter def icon_size(self, size_in_pixels): self.__icon_size = size_in_pixels if (self.__the_app is not None) and \ (self.__icontheme is not None): self.__get_app_icon() @property def app_pb(self): return self.__app_pb def get_icontheme(self): """ Return the icontheme Returns: A Gtk.Icontheme """ return self.__icontheme def set_icontheme(self, the_icontheme): """ Sets the icontheme currently being used Args : the_icontheme """ self.__icontheme = the_icontheme icontheme = property(get_icontheme, set_icontheme) def da_pointer_draw(self, drawing_area, event): ctx = event alloc = drawing_area.get_allocation() ctx.rectangle(0, 0, alloc.width, alloc.height) ctx.set_source_rgb(self.__bgr / 255, self.__bgg / 255, self.__bgb / 255) ctx.fill() def set_app_root_coords(self, x, y): """ Sets the x and y root coords of the app """ self.__app_x = x self.__app_y = y def set_applet_details(self, applet_x, applet_y, applet_w, applet_h): """ Sets the variables which record the root coords and size of the applet Args: applet_x : the x position of the top left of the applet (root coords) applet_y : the y position of the top left of the applet (root coords) applet_w : the width of the applet applet_h : the height of the applet """ self.__applet_x = applet_x self.__applet_y = applet_y self.__applet_w = applet_w self.__applet_h = applet_h mate-dock-applet-21.10.0/src/dock_prefs.in000066400000000000000000001565141411344606400202570ustar00rootroot00000000000000#!/usr/bin/env python3 """Provide a configuration dialog for the dock panel applet Allow the user to view and set various configuration options """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") else: gi.require_version("Gtk", "3.0") gi.require_version("MatePanelApplet", "4.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import MatePanelApplet from gi.repository import GdkPixbuf from random import randint from docked_app_helpers import * class AttentionType: """Class to specify the ways in which docked apps can shoe that they need attention """ BLINK = 0 SHOW_BADGE = 1 class ClickActionType: """ Class to define the ways in which running apps may perorm window selection""" WIN_LIST = 0 # show the applet's window list COMPIZ = 1 # display compiz window spread MINMAX = 2 # minimize/maximize the app's open windows class ThemeType: """ Class to define the various themes that may be set """ DEFAULT = 0 UNITY = 1 UNITY_FLAT = 2 SUBWAY = 3 CUSTOM = 4 def create_frame(caption): """ Convenience function to create a Gtk.Frame with a desired caption in bold text Returns: frame - the Gtk.Frame we created """ frame = Gtk.Frame(label="aaaa") lbl = frame.get_label_widget() lbl.set_use_markup(True) lbl.set_label("%s" % caption) frame.set_shadow_type(Gtk.ShadowType.NONE) frame.set_border_width(4) return frame class DockPrefsWindow(Gtk.Window): """Class to provide the preferences window functionality Create and display the preferences window Provide methods to get and set: the type of indicator to be used by the dock applet whether pinned/unpinned apps are to be displayed from all workspaces or just the current workspace whether the colour of the MATE panels is to be set to the dominant colour of the current wallpaper image """ def __init__(self, ok_callback, app): """ Constructor for the preferences window Create the window and its contents and display them set the callback for the ok button press Args: ok_callback : the method to be called when the ok button is is pressed app : a docked_app from which we can generate previews of the various indicator and background settings preview_size : the size of preview of indicator and background settings """ super().__init__(title=_("Preferences")) self.set_skip_taskbar_hint(True) # we don't want to be in the taskbar self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) # define the size of the preview window drawing area. It should match_bamf_app_to_dock_app # the size of the dock apps drawing areas self.PREVIEW_SIZE = app.drawing_area_size self.connect("delete-event", self.win_delete_event) self.__app = app self.__app_pb = app.app_pb # setup the window contents self.set_border_width(5) if build_gtk2: self.__hbox = Gtk.HBox() else: self.__hbox = Gtk.Box() self.__hbox.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbox.set_spacing(2) if build_gtk2: self.__hbox1 = Gtk.HBox() else: self.__hbox1 = Gtk.Box() self.__hbox1.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbox1.set_spacing(2) self.__vbox = self.create_vbox() self.__vbox.set_spacing(2) self.__cancel_btn = Gtk.Button(label=_("Cancel"), stock=Gtk.STOCK_CANCEL) self.__cancel_btn.connect("button-press-event", self.win_cancel_button_press) self.__ok_btn = Gtk.Button(label=_("Ok"), stock=Gtk.STOCK_OK) self.__ok_btn.connect("button-press-event", ok_callback) if build_gtk2: self.__hbbx = Gtk.HButtonBox() else: self.__hbbx = Gtk.ButtonBox() self.__hbbx.set_orientation(Gtk.Orientation.HORIZONTAL) self.__hbbx.set_spacing(4) self.__hbbx.set_layout(Gtk.ButtonBoxStyle.END) self.__hbbx.pack_start(self.__ok_btn, False, False, 4) self.__hbbx.pack_start(self.__cancel_btn, False, False, 4) self.__notebook = Gtk.Notebook() if build_gtk2: self.__appearance_tbl = Gtk.Table(rows=4, columns=1, homogeneous=False) else: self.__appearance_tbl = Gtk.Grid() self.__appearance_tbl.set_column_spacing(4) self.__appearance_tbl.set_row_spacing(4) self.__appearance_tbl.set_row_homogeneous(False) self.__frame_theme = create_frame(_("Theme")) self.__frame_theme_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_theme_align.set_padding(0, 0, 12, 0) self.__frame_theme_align.set_padding(0, 0, 12, 0) self.__theme_options = [_("Default"), _("Unity"), _("Unity Flat"), _("Subway"), _("Custom")] self.__cbt_theme = self.create_textcombo(self.__theme_options) self.__cbt_theme.connect("changed", self.theme_changed) self.__frame_theme_align.add(self.__cbt_theme) self.__frame_theme.add(self.__frame_theme_align) self.__frame_ind_type = create_frame(_("Indicator Type")) self.__indicator_options = [_("Default light"), _("Default dark"), _("Single bar"), _("Circle"), _("Square"), _("Triangle"), _("Diamond"), _("Subway"), _("None")] self.__cbt_ind_type = self.create_textcombo(self.__indicator_options) self.__cbt_ind_type.connect("changed", self.setting_toggled) self.__frame_preview = create_frame(_("Preview")) self.__frame_preview.set_shadow_type(Gtk.ShadowType.NONE) self.__hbox_preview = self.create_hbox() self.__hbox_preview.set_spacing(0) self.__da_preview = Gtk.DrawingArea() self.__da_preview.set_size_request(self.PREVIEW_SIZE * 3, self.PREVIEW_SIZE) self.__frame_preview_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_preview_align.set_padding(0, 0, 12, 0) self.__hbox_preview.pack_start(self.__da_preview, False, False, 0) self.__frame_preview_align.add(self.__hbox_preview) self.__vbox_preview = self.create_vbox() self.__vbox.set_spacing(2) self.__vbox_preview.pack_start(self.__frame_preview_align, False, False, 4) self.__frame_preview.add(self.__vbox_preview) # connect an event handler to draw the dark indicator if build_gtk2: self.__da_preview.connect("expose-event", self.draw_preview) else: self.__da_preview.connect("draw", self.draw_preview) # create ui elements for multiple indicators for open windows self.__cb_multi_ind = Gtk.CheckButton(label=_("Display an indicator for each open window")) self.__cb_multi_ind.set_tooltip_text(_("Display an indicator (max 4) for each open window")) self.__cb_multi_ind.connect("toggled", self.setting_toggled) if build_gtk2: self.__tbl_ind_type = Gtk.Table(rows=1, columns=2, homogeneous=False) self.__tbl_ind_type.attach(self.__cbt_ind_type, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__tbl_ind_type = Gtk.Grid() self.__tbl_ind_type.set_row_spacing(2) self.__tbl_ind_type.set_column_spacing(2) self.__tbl_ind_type.attach(self.__cbt_ind_type, 0, 0, 1, 1) self.__frame_ind_type_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_ind_type_align.set_padding(0, 0, 12, 0) self.__frame_ind_type_align.add(self.__tbl_ind_type) self.__frame_ind_type.add(self.__frame_ind_type_align) self.__frame_bg = create_frame(_("Icon Background")) self.__bg_options = [_("Gradient fill"), _("Solid fill"), _("Unity"), _("Unity Flat")] self.__cbt_icon_bg = self.create_textcombo(self.__bg_options) self.__cbt_icon_bg.connect("changed", self.setting_toggled) self.__frame_bg_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_bg_align.set_padding(0, 0, 12, 0) self.__frame_bg_align.add(self.__cbt_icon_bg) self.__frame_bg.add(self.__frame_bg_align) if build_gtk2: self.__appearance_tbl.attach(self.__frame_preview, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__appearance_tbl.attach(self.__frame_ind_type, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__appearance_tbl.attach(self.__cb_multi_ind, 0, 1, 2, 3, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__appearance_tbl.attach(self.__frame_bg, 0, 1, 3, 4, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__appearance_tbl.attach(self.__frame_preview, 0, 0, 1, 1) self.__appearance_tbl.attach(self.__frame_theme, 0, 1, 1, 1) #self.__appearance_tbl.attach(self.__frame_them_fb_bar_col, 0, 1, 1, 1) self.__appearance_tbl.attach(self.__frame_ind_type, 0, 2, 1, 1) self.__appearance_tbl.attach(self.__cb_multi_ind, 0, 3, 1, 1) self.__appearance_tbl.attach(self.__frame_bg, 0, 4, 1, 1) self.__frame_pinned_apps = create_frame(_("Pinned application dock icons")) self.__rb_pinned_all_ws = Gtk.RadioButton(label=_("Display on all workspaces")) self.__rb_pinned_pin_ws = Gtk.RadioButton(label=_("Display only on the workspace the app was pinned"), group=self.__rb_pinned_all_ws) if build_gtk2: self.__table_pinned_apps = Gtk.Table(rows=2, columns=1, homogeneous=False) self.__table_pinned_apps.attach(self.__rb_pinned_all_ws, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_pinned_apps.attach(self.__rb_pinned_pin_ws, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__table_pinned_apps = Gtk.Grid() self.__table_pinned_apps.set_row_spacing(2) self.__table_pinned_apps.set_column_spacing(2) self.__table_pinned_apps.attach(self.__rb_pinned_all_ws, 0, 0, 1, 1) self.__table_pinned_apps.attach(self.__rb_pinned_pin_ws, 0, 1, 1, 1) self.__frame_pinned_apps_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_pinned_apps_align.set_padding(0, 0, 12, 0) self.__frame_pinned_apps_align.add(self.__table_pinned_apps) self.__frame_pinned_apps.add(self.__frame_pinned_apps_align) self.__frame_unpinned_apps = create_frame(_("Unpinned application dock icons")) self.__rb_unpinned_all_ws = Gtk.RadioButton(label=_("Display unpinned apps from all workspaces")) self.__rb_unpinned_cur_ws = Gtk.RadioButton(group=self.__rb_unpinned_all_ws, label=_("Display unpinned apps only from current workspace")) if build_gtk2: self.__table_unpinned_apps = Gtk.Table(rows=2, columns=1, homogeneous=False) self.__table_unpinned_apps.attach(self.__rb_unpinned_all_ws, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_unpinned_apps.attach(self.__rb_unpinned_cur_ws, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__table_unpinned_apps = Gtk.Grid() self.__table_unpinned_apps.set_row_spacing(2) self.__table_unpinned_apps.set_column_spacing(2) self.__table_unpinned_apps.attach(self.__rb_unpinned_all_ws, 0, 0, 1, 1) self.__table_unpinned_apps.attach(self.__rb_unpinned_cur_ws, 0, 1, 1, 1) self.__frame_unpinned_apps_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_unpinned_apps_align.set_padding(0, 0, 12, 0) self.__frame_unpinned_apps_align.add(self.__table_unpinned_apps) self.__frame_unpinned_apps.add(self.__frame_unpinned_apps_align) self.__cb_win_cur_ws = Gtk.CheckButton(label=_("Display indicators/window list items for current workspace only")) self.__notes_scrolled_win = Gtk.ScrolledWindow() self.__notes_scrolled_win.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.__tv_notes = Gtk.TextView() self.__tv_notes.set_wrap_mode(Gtk.WrapMode.WORD) self.__tv_notes.set_editable(False) self.__notes_text_buf = Gtk.TextBuffer() self.__tv_notes.set_buffer(self.__notes_text_buf) iter = self.__notes_text_buf.get_start_iter() self.__notes_text_buf.insert(iter, _("Note: when displaying pinned apps only on the workspace where they were " + "created, it is a good idea to also select the 'Display unpinned apps' " + "and 'Display indicators/window list' items for the current workspace " + "only options.")) self.__ws_vbox = self.create_vbox() self.__ws_vbox.set_spacing(2) self.__ws_vbox.pack_start(self.__frame_pinned_apps, False, False, 4) self.__ws_vbox.pack_start(self.__frame_unpinned_apps, False, False, 4) self.__ws_vbox.pack_start(self.__cb_win_cur_ws, False, False, 4) self.__ws_vbox.pack_end(self.__tv_notes, False, False, 4) self.__frame_behaviour = create_frame(_("Left clicking a running app's icon will:")) self.__rb_win_list = Gtk.RadioButton(label=_("Display a list of the app's windows")) self.__rb_win_thumb = Gtk.RadioButton(group=self.__rb_win_list, label=_("Show thumbnail previews of the app's windows")) self.__rb_win_minmax = Gtk.RadioButton(group=self.__rb_win_list, label=_("Minimize/restore all of the app's windows")) self.__table_behaviour = Gtk.Table(rows=3, columns=1, homogeneous=False) self.__table_behaviour.attach(self.__rb_win_list, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_behaviour.attach(self.__rb_win_thumb, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_behaviour.attach(self.__rb_win_minmax, 0, 1, 2, 3, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__cb_win_switch_unity_style = Gtk.CheckButton(label=_("Unity-style behaviour (always focus app on first click)")) self.__notes_behaviour_tv = Gtk.TextView() self.__notes_behaviour_tv.set_wrap_mode(Gtk.WrapMode.WORD) self.__notes_behaviour_tv.set_editable(False) self.__notes_behaviour_tb = Gtk.TextBuffer() self.__notes_behaviour_tv.set_buffer(self.__notes_behaviour_tb) iter = self.__notes_behaviour_tb.get_start_iter() self.__notes_behaviour_tb.insert(iter, _("\nNotes:\nIf an app has only a single window open, a window list will not be " + "displayed. Instead the window will be minimized/restored.\n" + "Window thumbnail previews require Compiz")) self.__frame_behaviour_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_behaviour_align.set_padding(0, 0, 12, 0) self.__frame_behaviour_align.add(self.__table_behaviour) self.__frame_behaviour.add(self.__frame_behaviour_align) self.__behaviour_vbox = self.create_vbox() self.__behaviour_vbox.set_spacing(2) self.__behaviour_vbox.pack_start(self.__frame_behaviour, False, False, 4) self.__behaviour_vbox.pack_start(self.__cb_win_switch_unity_style, False, False, 4) self.__behaviour_vbox.pack_start(self.__notes_behaviour_tv, False, False, 4) self.__frame_spc = create_frame(_("App spacing")) if build_gtk2: self.__sb_spc = Gtk.SpinButton() self.__sb_spc.set_adjustment(Gtk.Adjustment(0, 0, 7, 1, 4)) else: self.__sb_spc = Gtk.SpinButton.new_with_range(0, 7, 1) self.__sb_spc.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID) self.__sb_spc.set_numeric(True) self.__sb_spc.set_max_length(1) self.__sb_spc.set_snap_to_ticks(True) self.__frame_spc_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_spc_align.set_padding(0, 0, 12, 300) self.__frame_spc_align.add(self.__sb_spc) self.__frame_spc.add(self.__frame_spc_align) self.__frame_color_change = create_frame(_("Panel colour")) self.__cb_panel_color_change = Gtk.CheckButton(label=_("Change panel colour to match wallpaper")) self.__cb_panel_color_change.connect("toggled", self.color_change_toggled) self.__cb_dock_panel_only = Gtk.CheckButton(label=_("Change colour of dock's panel only")) if build_gtk2: self.__table_color_change = Gtk.Table(rows=3, columns=1, homogeneous=False) self.__table_color_change.attach(self.__cb_panel_color_change, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_color_change.attach(self.__cb_dock_panel_only, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__table_color_change = Gtk.Grid() self.__table_color_change.set_row_spacing(2) self.__table_color_change.set_column_spacing(2) self.__table_color_change.attach(self.__cb_panel_color_change, 0, 0, 1, 1) self.__table_color_change.attach(self.__cb_dock_panel_only, 0, 1, 1, 1) self.__frame_color_change_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_color_change_align.set_padding(0, 0, 12, 0) self.__frame_color_change_align.add(self.__table_color_change) self.__frame_color_change.add(self.__frame_color_change_align) if not build_gtk2: self.__frame_dock_size = create_frame(_("Dock size")) self.__rb_variable_ds = Gtk.RadioButton(label=_("Variable - expand or contract as necessary")) self.__rb_fixed_ds = Gtk.RadioButton(group=self.__rb_variable_ds, label=_("Fixed")) self.__lbl_fixed_size = Gtk.Label(_("Display up to ")) self.__sb_fixed_size = Gtk.SpinButton.new_with_range(2, 64, 1) self.__sb_fixed_size.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID) self.__sb_fixed_size.set_numeric(True) self.__sb_fixed_size.set_max_length(2) self.__sb_fixed_size.set_snap_to_ticks(True) self.__lbl_fixed_size1 = Gtk.Label(_(" app icons")) self.__hbox_fixed_size = self.create_hbox() self.__hbox_fixed_size.set_spacing(2) self.__hbox_fixed_size.pack_start(self.__lbl_fixed_size, False, False, 4) self.__hbox_fixed_size.pack_start(self.__sb_fixed_size, False, False, 4) self.__hbox_fixed_size.pack_start(self.__lbl_fixed_size1, False, False, 4) self.__table_dock_size = Gtk.Grid() self.__table_dock_size.set_row_spacing(2) self.__table_dock_size.set_column_spacing(2) self.__table_dock_size.attach(self.__rb_variable_ds, 0, 0, 1, 1) self.__table_dock_size.attach(self.__rb_fixed_ds, 0, 1, 1, 1) self.__table_dock_size.attach(self.__hbox_fixed_size, 0, 2, 1, 1) self.__frame_dock_size_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_dock_size_align.set_padding(0, 0, 12, 0) self.__frame_dock_size_align.add(self.__table_dock_size) self.__frame_dock_size.add(self.__frame_dock_size_align) self.__cb_pan_act = Gtk.CheckButton(label=_("Disable popup action list " + "and show app actions\non panel " + "right click menu only")) self.__panel_vbox = self.create_vbox() self.__panel_vbox.set_spacing(2) self.__panel_vbox.pack_start(self.__frame_spc, False, False, 4) self.__panel_vbox.pack_start(self.__frame_color_change, False, False, 4) if not build_gtk2: self.__panel_vbox.pack_start(self.__frame_dock_size, False, False, 4) self.__panel_vbox.pack_start(self.__cb_pan_act, False, False, 4) self.__frame_fb_bar_col = create_frame(_("Fallback bar indicator colour")) self.__lbl_fb_bar_col = Gtk.Label(_("Colour")) self.__cbtn_fb_bar_col = Gtk.ColorButton() self.__cbtn_fb_bar_col.set_tooltip_text(_("Colour used for drawing bar indicators when theme colour " + "cannot be determined or when using Gtk2")) self.__fb_bar_col_hbox = self.create_hbox() self.__fb_bar_col_hbox.set_spacing(2) self.__fb_bar_col_hbox.pack_start(self.__lbl_fb_bar_col, False, False, 2) self.__fb_bar_col_hbox.pack_start(self.__cbtn_fb_bar_col, True, True, 2) self.__fb_bar_col_vbox = self.create_vbox() self.__fb_bar_col_vbox.set_spacing(2) self.__fb_bar_col_vbox.pack_start(self.__fb_bar_col_hbox, False, False, 2) self.__frame_fb_bar_col.add(self.__fb_bar_col_vbox) self.__frame_attention_type = create_frame(_("Action when apps need attention")) self.__rb_attention_blink = Gtk.RadioButton(label=_("Blink the app icon")) self.__rb_attention_badge = Gtk.RadioButton(label=_("Show a badge on the app icon"), group=self.__rb_attention_blink) if build_gtk2: self.__table_attention_type = Gtk.Table(rows=2, columns=1, homogeneous=False) self.__table_attention_type.attach(self.__rb_attention_blink, 0, 1, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) self.__table_attention_type.attach(self.__rb_attention_badge, 0, 1, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK, 2, 2) else: self.__table_attention_type = Gtk.Grid() self.__table_attention_type.set_row_spacing(2) self.__table_attention_type.set_column_spacing(2) self.__table_attention_type.attach(self.__rb_attention_blink, 0, 0, 1, 1) self.__table_attention_type.attach(self.__rb_attention_badge, 0, 1, 1, 1) self.__frame_attention_type_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_attention_type_align.set_padding(0, 0, 12, 0) self.__frame_attention_type_align.add(self.__table_attention_type) self.__frame_attention_type.add(self.__frame_attention_type_align) self.__frame_pdel = create_frame(_("Popup Delay(s)")) if build_gtk2: self.__sb_pdel = Gtk.SpinButton() self.__sb_pdel.set_adjustment(Gtk.Adjustment(1.0, 0.1, 5.0, 0.1, 1)) else: self.__sb_pdel = Gtk.SpinButton.new_with_range(0.1, 5, 0.1) self.__sb_pdel.set_digits(1) self.__sb_pdel.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID) self.__sb_pdel.set_numeric(True) self.__sb_pdel.set_max_length(3) self.__sb_pdel.set_snap_to_ticks(True) self.__frame_pdel_align = Gtk.Alignment(xalign=0.5, yalign=0.5, xscale=1.0, yscale=1.0) self.__frame_pdel_align.set_padding(0, 0, 12, 300) self.__frame_pdel_align.add(self.__sb_pdel) self.__frame_pdel.add(self.__frame_pdel_align) self.__misc_vbox = self.create_vbox() self.__misc_vbox.set_spacing(2) self.__misc_vbox.pack_start(self.__frame_fb_bar_col, False, False, 4) self.__misc_vbox.pack_start(self.__frame_attention_type, False, False, 4) self.__misc_vbox.pack_start(self.__frame_pdel, False, False, 4) self.__vbox.pack_start(self.__notebook, True, True, 4) self.__notebook.append_page(self.__appearance_tbl, Gtk.Label(_("Appearance"))) self.__notebook.append_page(self.__ws_vbox, Gtk.Label(_("Workspaces"))) self.__notebook.append_page(self.__behaviour_vbox, Gtk.Label(_("Behaviour"))) self.__notebook.append_page(self.__panel_vbox, Gtk.Label(_("Panel Options"))) self.__notebook.append_page(self.__misc_vbox, Gtk.Label(_("Misc"))) self.__vbox.pack_start(Gtk.HSeparator(), True, True, 4) self.__vbox.pack_start(self.__hbbx, False, False, 0) self.set_fallback_bar_col([192, 128, 0]) self.add(self.__vbox) self.show_all() def create_textcombo(self, items): """ Convenience function to create a text combo box with a set of specified items Args: items : a list of the items the box is to contain Returns: a ComboBoxText """ cbt = Gtk.ComboBoxText() # add the items for item in items: cbt.append_text(item) return cbt def set_combo_selected_item(self, cbt, item, items): """ Sets a ComboBoxText's selected item Args: cbt : the ComboBoxText item : a string, the selected item items : the list of items the combo contains """ index = items.index(item) cbt.set_active(index) def create_vbox(self): """ Convenience function to create a Gtk2 VBox or a Gtk3 Box oriented vertically Returns: the vbox/box we created """ if build_gtk2: vbox = Gtk.VBox() else: vbox = Gtk.Box() vbox.set_orientation(Gtk.Orientation.VERTICAL) return vbox def create_hbox(self): """ Convenience function to create a Gtk2 HBox or a Gtk3 Box oriented horizontally Returns: the hbox/box we created """ if build_gtk2: hbox = Gtk.HBox() else: hbox = Gtk.Box() hbox.set_orientation(Gtk.Orientation.HORIZONTAL) return hbox def win_delete_event(self, widget, event, data=None): """Callback for the preferences window delete event Do not delete the window, hide it instead so that it can be shown again later if needed """ self.hide() return True def win_cancel_button_press(self, widget, event): """Callback for the preferences window Cancel button press Hide the window """ self.hide() def draw_preview(self, drawing_area, event): """Draw a preview of the current appearance settings Draw a dark or light background to represent the panel Draw an appropriate type of active background Draw an app icon Draw the appropriate type of indicator (or no indicator) Args: drawing_area : our drawing area that caused the event event : the event parameters """ if build_gtk2: tgt_ctx = self.__da_preview.window.cairo_create() else: tgt_ctx = event # if a dark indicator is set, we draw a light background, otherwise a # dark background indicator = self.get_indicator_type() if indicator == IndicatorType.DARK: tgt_ctx.set_source_rgb(0.85, 0.85, 0.85) else: tgt_ctx.set_source_rgb(0.15, 0.21, 0.15) tgt_ctx.rectangle(0, 0, self.PREVIEW_SIZE * 3, self.PREVIEW_SIZE) tgt_ctx.fill() app_size = self.PREVIEW_SIZE + ind_extra_s(self.get_indicator_type()) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, app_size, self.PREVIEW_SIZE) ctx = cairo.Context(surface) # draw the active background - first we need the colour of the background in cairo # values red = self.__app.highlight_color.r / 255 green = self.__app.highlight_color.g / 255 blue = self.__app.highlight_color.b / 255 bg_type = self.get_bg() if bg_type == IconBgType.GRADIENT: bgd = DefaultBackgroundDrawer(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, red, green, blue) elif bg_type == IconBgType.ALPHAFILL: bgd = AlphaFillBackgroundDrawer(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, red, green, blue, 0.5) elif bg_type == IconBgType.UNITY: bgd = UnityBackgroundDrawer(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, red, green, blue, False, 1) else: bgd = UnityFlatBackgroundDrawer(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, red, green, blue, True, 1) bgd.draw() # draw the app icon if bg_type in [IconBgType.UNITY, IconBgType.UNITY_FLAT]: pb_size = self.PREVIEW_SIZE pb_size = self.PREVIEW_SIZE * 3 / 4 # create scale down pixbuf and use that pb_small = self.__app_pb.scale_simple(pb_size, pb_size, GdkPixbuf.InterpType.BILINEAR) offset = self.PREVIEW_SIZE / 2 - pb_size / 2 Gdk.cairo_set_source_pixbuf(ctx, pb_small, offset, offset) else: offset = self.PREVIEW_SIZE / 2 - self.__app_pb.props.width / 2 Gdk.cairo_set_source_pixbuf(ctx, self.__app_pb, offset, offset) ctx.paint() if bg_type in [IconBgType.UNITY, IconBgType.UNITY_FLAT]: bgd.draw_shine() # draw the indicator(s) if necessary if indicator != IndicatorType.NONE: # if we're showing indicators for each open window, show multiple indicators if self.__cb_multi_ind.get_active(): num_ind = randint(2, 4) else: num_ind = 1 if indicator == IndicatorType.LIGHT: ind = DefaultLightInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, num_ind) elif indicator == IndicatorType.DARK: ind = DefaultDarkInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, num_ind) elif indicator == IndicatorType.TBAR: ind = ThemeBarInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet) elif indicator == IndicatorType.TCIRC: ind = ThemeCircleInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet, num_ind) elif indicator == IndicatorType.TSQUARE: ind = ThemeSquareInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet, num_ind) elif indicator == IndicatorType.TTRI: ind = ThemeTriInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet, num_ind) elif indicator == IndicatorType.TDIA: ind = ThemeDiaInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet, num_ind) elif indicator == IndicatorType.SUBWAY: active = bool(randint(0, 1)) ind = SubwayInd(ctx, self.PREVIEW_SIZE, MatePanelApplet.AppletOrient.UP, self.__app.applet, num_ind, surface, active) else: ind = None if ind is not None: ind.draw() # now draw to the screen if build_gtk2: tgt_ctx = drawing_area.window.cairo_create() tgt_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) tgt_ctx.clip() tgt_ctx.set_source_surface(surface, self.PREVIEW_SIZE, 0) tgt_ctx.paint() tgt_ctx = None else: tgt_ctx.set_source_surface(surface, self.PREVIEW_SIZE, 0) tgt_ctx.paint() ctx = None def color_change_toggled(self, widget): """Handler for the panel color change checkbox toggled event If the panel colour change option is selected, enable the checkbox that specifies whether or not all panels are to change colour """ self.__cb_dock_panel_only.set_sensitive(self.__cb_panel_color_change.get_active()) def theme_changed(self, widget): """ Handler for the theme being changed Set the appropriate indicator and background type for the theme """ theme = self.get_theme() # enable or disable the indicator and backgroud type combos depending # on whether or not we are using a custom themes self.__cbt_ind_type.set_sensitive(theme == ThemeType.CUSTOM) self.__cbt_icon_bg.set_sensitive(theme == ThemeType.CUSTOM) if theme == ThemeType.DEFAULT: self.set_indicator(IndicatorType.LIGHT) self.set_bg(IconBgType.GRADIENT) elif theme == ThemeType.UNITY: self.set_indicator(IndicatorType.TTRI) self.set_bg(IconBgType.UNITY) elif theme == ThemeType.UNITY_FLAT: self.set_indicator(IndicatorType.TTRI) self.set_bg(IconBgType.UNITY_FLAT) elif theme == ThemeType.SUBWAY: self.set_indicator(IndicatorType.SUBWAY) self.set_bg(IconBgType.ALPHAFILL) # if a custom def setting_toggled(self, widget): """ Handler for updating the preview when appearance settings are changed Args: widget: the widget the caused the event """ self.__da_preview.queue_draw() def rb_no_ind_toggled(self, widget): """ Handler for the no indicator radio button toggled event, also used by the bar indicator rb If either the no indicator or bar indicator ption is selected, disable the multiple indicator checkbox Update the appearance preview """ self.__cb_multi_ind.set_sensitive(self.__rb_no_ind.get_active() is not True and self.__rb_bar_ind.get_active() is not True) self.setting_toggled(widget) def set_dock_size_visible(self, vis): """ Set whether the dock size frame is visible or not Params: vis - bool """ self.__frame_dock_size.set_visible(vis) def get_indicator_type(self): """Get the indicator type specified in the preferences window. Returns : IndicatorType """ indicator = self.__cbt_ind_type.get_active_text() if indicator == _("Default light"): return IndicatorType.LIGHT elif indicator == _("Default dark"): return IndicatorType.DARK elif indicator == _("Single bar"): return IndicatorType.TBAR elif indicator == _("Circle"): return IndicatorType.TCIRC elif indicator == _("Square"): return IndicatorType.TSQUARE elif indicator == _("Triangle"): return IndicatorType.TTRI elif indicator == _("Diamond"): return IndicatorType.TDIA elif indicator == _("Subway"): return IndicatorType.SUBWAY else: return IndicatorType.NONE def set_indicator(self, indicator): """Set the indicator type Args : indicator - an IndicatorType """ if indicator == IndicatorType.LIGHT: self.set_combo_selected_item(self.__cbt_ind_type, _("Default light"), self.__indicator_options) elif indicator == IndicatorType.DARK: self.set_combo_selected_item(self.__cbt_ind_type, _("Default dark"), self.__indicator_options) elif indicator == IndicatorType.TBAR: self.set_combo_selected_item(self.__cbt_ind_type, _("Single bar"), self.__indicator_options) elif indicator == IndicatorType.TCIRC: self.set_combo_selected_item(self.__cbt_ind_type, _("Circle"), self.__indicator_options) elif indicator == IndicatorType.TSQUARE: self.set_combo_selected_item(self.__cbt_ind_type, _("Square"), self.__indicator_options) elif indicator == IndicatorType.TTRI: self.set_combo_selected_item(self.__cbt_ind_type, _("Triangle"), self.__indicator_options) elif indicator == IndicatorType.TDIA: self.set_combo_selected_item(self.__cbt_ind_type, _("Diamond"), self.__indicator_options) elif indicator == IndicatorType.SUBWAY: self.set_combo_selected_item(self.__cbt_ind_type, _("Subway"), self.__indicator_options) else: self.set_combo_selected_item(self.__cbt_ind_type, _("None"), self.__indicator_options) def get_multi_ind(self): """Gets whether or not to use display an indicator for each open window that a docked app has Returns: boolean """ return self.__cb_multi_ind.get_active() def set_bg(self, bg): """ Set the active icon background type Args : bg - an IconBgType """ if bg == IconBgType.GRADIENT: self.set_combo_selected_item(self.__cbt_icon_bg, _("Gradient fill"), self.__bg_options) elif bg == IconBgType.ALPHAFILL: self.set_combo_selected_item(self.__cbt_icon_bg, _("Solid fill"), self.__bg_options) elif bg == IconBgType.UNITY: self.set_combo_selected_item(self.__cbt_icon_bg, _("Unity"), self.__bg_options) else: self.set_combo_selected_item(self.__cbt_icon_bg, _("Unity Flat"), self.__bg_options) def get_bg(self): """ Gets the currently selected active icon background type Returns: An IconBgType """ bg = self.__cbt_icon_bg.get_active_text() if bg == _("Gradient fill"): return IconBgType.GRADIENT elif bg == _("Solid fill"): return IconBgType.ALPHAFILL elif bg == _("Unity"): return IconBgType.UNITY else: return IconBgType.UNITY_FLAT def set_theme(self, theme): """ Sets the theme Args: theme : a ThemeType """ if theme == ThemeType.DEFAULT: self.set_combo_selected_item(self.__cbt_theme, _("Default"), self.__theme_options) elif theme == ThemeType.UNITY: self.set_combo_selected_item(self.__cbt_theme, _("Unity"), self.__theme_options) elif theme == ThemeType.UNITY_FLAT: self.set_combo_selected_item(self.__cbt_theme, _("Unity Flat"), self.__theme_options) elif theme == ThemeType.SUBWAY: self.set_combo_selected_item(self.__cbt_theme, _("Subway"), self.__theme_options) elif theme == ThemeType.CUSTOM: self.set_combo_selected_item(self.__cbt_theme, _("Custom"), self.__theme_options) def get_theme(self): """ Get the currently selected theme Returns : a ThemeType """ theme = self.__cbt_theme.get_active_text() if theme == _("Default"): return ThemeType.DEFAULT elif theme == _("Unity"): return ThemeType.UNITY elif theme == _("Unity Flat"): return ThemeType.UNITY_FLAT elif theme == _("Subway"): return ThemeType.SUBWAY else: return ThemeType.CUSTOM def set_multi_ind(self, use_multi_ind): """Sets whether or not to display multiple indicators Args: use_multi_ind - boolean """ self.__cb_multi_ind.set_active(use_multi_ind) def get_show_unpinned_apps_on_all_ws(self): """Gets whether unpinned apps are displayed in the dock on all workspaces Returns: boolean """ return self.__rb_unpinned_all_ws.get_active() def set_show_unpinned_apps_on_all_ws(self, show_on_all): """Sets whether unpinned apps are displayed in the dock on all workspaces Args: show_on_all - boolean """ if show_on_all: self.__rb_unpinned_all_ws.set_active(True) else: self.__rb_unpinned_cur_ws.set_active(True) def get_show_pinned_apps_on_all_ws(self): """Gets whether pinned apps are displayed in the dock on all workspaces or just on the workspace where they were created Returns: boolean """ return self.__rb_pinned_all_ws.get_active() def set_show_pinned_apps_on_all_ws(self, show_on_all): """Sets whether pinned apps are displayed in the dock on all workspaces Args: show_on_all - boolean """ if show_on_all: self.__rb_pinned_all_ws.set_active(True) else: self.__rb_pinned_pin_ws.set_active(True) def get_pan_act(self): """ Gets whether or not the show the action list on the panel right click menu, rather than as a popup Returns: boolean """ return self.__cb_pan_act.get_active() def set_pan_act(self, pan_act): """ Sets whether or not the show the action list on the panel right click menu, rather than as a popup Args: pan_act: boolean """ self.__cb_pan_act.set_active(pan_act) def get_use_win_list(self): """Gets whether to use the dock's window list to select windows or whether to use Compiz thumbnail previews Returns: boolean """ return self.__rb_win_list.get_active() def set_use_win_list(self, use_win_list): """Sets whether to use the dock's window list to select windows or whether to use Compiz thumbnail previews Args: use_win_list - boolean """ if use_win_list: self.__rb_win_list.set_active(True) else: self.__rb_win_thumb.set_active(True) def get_click_action(self): """ Gets the action specified for when a running app's dock icon is clicked Returns : a ClickActionType value """ if self.__rb_win_list.get_active(): return ClickActionType.WIN_LIST elif self.__rb_win_thumb.get_active(): return ClickActionType.COMPIZ else: return ClickActionType.MINMAX def set_click_action(self, click_action): """ Sets the action specified for when a running app's dock icon is clicked Args: click_action: A ClickActionType value """ if click_action == ClickActionType.WIN_LIST: self.__rb_win_list.set_active(True) elif click_action == ClickActionType.COMPIZ: self.__rb_win_thumb.set_active(True) else: self.__rb_win_minmax.set_active(True) def get_change_panel_color(self): """ Get whether the panel colour is to be changed according to the current wallpaper Returns: boolean """ return self.__cb_panel_color_change.get_active() def set_change_panel_color(self, change_color): """ Sets whether the panel color is to be changed according to the current wallpaper Args: change_color - boolean """ self.__cb_panel_color_change.set_active(change_color) def get_change_dock_color_only(self): """ Get whether only the panel containing the dock is to be changed when setting the panel colour according to the current wallpaper Returns: boolean """ return self.__cb_dock_panel_only.get_active() def rb_variable_ds_toggled(self, widget): """ Handler for dock fixed size setting changes Update the ui according to the current settingd """ size_fixed = self.__rb_fixed_ds.get_active() # enable/diable the parts of the interface that allow the user to set the number # of app icons in a fixed size dock self.__lbl_fixed_size.set_sensitive(size_fixed) self.__sb_fixed_size.set_sensitive(size_fixed) self.__lbl_fixed_size1.set_sensitive(size_fixed) def set_fixed_size(self, fixed_size, num_icons, mutiny_layout): """ Set whether or not the dock is a fixed size, and if so the number of app icons it can contain before enabling scrolling If we're using the Mutiny panel layout, disable all fixed size settings Params: fixed_size : bool - whether or not the dock is a fixed size num_icons : int - the number of icons mutiny_layout : bool - indicates whether or not we're using the Mutiny layour """ self.__rb_variable_ds.connect("toggled", self.rb_variable_ds_toggled) if not mutiny_layout: self.__rb_fixed_ds.set_sensitive(True) self.__rb_variable_ds.set_sensitive(True) self.__rb_fixed_ds.set_active(fixed_size) self.rb_variable_ds_toggled(self.__rb_variable_ds) # ensure ui is updated self.__sb_fixed_size.set_value(num_icons) else: self.__lbl_fixed_size.set_sensitive(False) self.__sb_fixed_size.set_sensitive(False) self.__lbl_fixed_size1.set_sensitive(False) self.__rb_fixed_ds.set_sensitive(False) self.__rb_variable_ds.set_sensitive(False) self.__rb_variable_ds.disconnect_by_func(self.rb_variable_ds_toggled) self.__rb_fixed_ds.set_active(fixed_size) def get_fixed_size(self): """ Get the dock fixed size settings Note: the dock should not call this method if the Mutiny panel layout is being used Returns: a bool - whether or not the dock is of fixed size an int - the number of app icons in a fixed size dock """ return self.__rb_fixed_ds.get_active(), self.__sb_fixed_size.get_value() def set_change_dock_color_only(self, dock_only): """ Sets whether only the panel containing the dock is to be changed when settings the panel colour according to the current wallpaper Args: dock_only - boolean """ self.__cb_dock_panel_only.set_active(dock_only) def get_win_cur_ws_only(self): """ Gets whether the dock will show indicators/window list items for the current workspace only Returns: boolean """ return self.__cb_win_cur_ws.get_active() def set_win_cur_ws_only(self, win_cur_ws_only): """ Sets whether the dock will show indicators/window list items for the current workspace only Args: win_cur_ws_only - boolean """ self.__cb_win_cur_ws.set_active(win_cur_ws_only) def get_win_switch_unity_style(self): """ Gets whether the dock will focus the last active window of an app on first click Returns: boolean """ return self.__cb_win_switch_unity_style.get_active() def set_win_switch_unity_style(self, win_switch_unity_style): """ Sets whether the dock will focus the last active window of an app on first click Args: win_switch_unity_style - boolean """ self.__cb_win_switch_unity_style.set_active(win_switch_unity_style) def set_fallback_bar_col(self, colrgb): """ Set the colour of the fallback bar indicator colour button Args: colrgb : a list containing the r,g and b colour components(0-255) as strings """ colstr = "#%0.2X%0.2X%0.2X" % (int(colrgb[0]), int(colrgb[1]), int(colrgb[2])) if build_gtk2: cbrgba = Gdk.color_parse(colstr) self.__cbtn_fb_bar_col.set_color(color=cbrgba) else: cbrgba = Gdk.RGBA() cbrgba.parse(colstr) self.__cbtn_fb_bar_col.set_rgba(cbrgba) self.__cbtn_fb_bar_col.set_use_alpha(False) def get_fallback_bar_col(self): """ Get the colour of the fallback bar indicator colour button Returns: a list containing the r, g, and b colour components(0-255) """ if build_gtk2: cbrgba = self.__cbtn_fb_bar_col.get_color() else: cbrgba = self.__cbtn_fb_bar_col.get_rgba().to_color() return ["%s" % int(cbrgba.red / 256), "%s" % int(cbrgba.green / 256), "%s" % int(cbrgba.blue / 256)] def set_app_spacing(self, spacing): """ Set the amount of space between icons in the dock """ self.__sb_spc.set_value(spacing) def get_app_spacing(self): """ Get the amount of space between icons in the dock :return: int """ self.__sb_spc.update() return self.__sb_spc.get_value() def set_attention_type(self, attention_type): """ Set the attention type Args : attention_type: An AttentionType e.g. AttentionType.BLINK """ if attention_type == AttentionType.BLINK: self.__rb_attention_blink.set_active(True) else: self.__rb_attention_badge.set_active(True) def get_attention_type(self): """ Get the attention type Returns: An AttentionType """ if self.__rb_attention_blink.get_active(): return AttentionType.BLINK else: return AttentionType.SHOW_BADGE def set_popup_delay(self, delay): """ Set the amount of space between icons in the dock Args: delay: the delay in ms """ # convert delay to seconds self.__sb_pdel.set_value(delay / 1000) def get_popup_delay(self): """ Get the popup delay, converting from seconds to ms :return: int """ self.__sb_pdel.update() return int(self.__sb_pdel.get_value() * 1000) def main(): """main function - debug code can go here""" # dpw = DockPrefsWindow(Gtk.main_quit) # Gtk.main() if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_win_list.in000066400000000000000000000505031411344606400207570ustar00rootroot00000000000000#!/usr/bin/env python3 """ Provide a window showing a list of an app's open windows and also allow the user to select a window from the list and switch to it. Each item in the list will an indicator to show if the window is currently active, the window's title, and a close icon allowing the window to be closed. The window will function in a similar way to a tooltip i.e. it will appear when the mouse hovers over a dock icon and will disappear if the mouse moves away from the window or the dock applet. """ # # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") gi.require_version("Bamf", "3") from gi.repository import Gtk from gi.repository import Wnck from gi.repository import GdkPixbuf from gi.repository import Gdk from gi.repository import Pango from gi.repository import Bamf import os import cairo import tempfile from time import sleep from dock_popup import DockPopup import window_control from log_it import log_it as log_it CONST_CLOSE_ICON_SIZE = 16 CONST_SEP = "--------------------" # text which denotes tree view item is a separator CONST_MAX_TITLE_WIDTH = 400 # max width of the title column in pixels CONST__ACTIVE_TEXT = "•" class DockWinList(DockPopup): """ Descendant of Dockup to provide a list of a running app's open windows """ def __init__(self, wnck_screen, panel_orient, scroll_adj): """ create the window and its contents Args: wnck_screen: the wnck_screen of the applet panel_orient : the orientation of the panel scroll_adj : an adjustment to be applied to the window position because the dock has scrolling enabled """ # call the base classes constructor DockPopup.__init__(self, wnck_screen, panel_orient, scroll_adj) self.menu = None self.__bg_str = "" self.__fg_str = "" # we use a treeview to list each window, so initialise it and its # liststore self.__tree_view = Gtk.TreeView() if not build_gtk2: self.__tree_view.set_valign(Gtk.Align.START) self.__tree_view.set_halign(Gtk.Align.START) self.__tree_view.hexpand = True self.__tree_view.vexpand = True self.__tree_view.set_headers_visible(False) # turn grid lines off, although they still seem to appear in some # themes e.g. Menta self.__tree_view.set_grid_lines(Gtk.TreeViewGridLines.NONE) self.__tree_view.set_hover_selection(True) # the list consists of open windows (click to select the window) # the liststore therefore needs to contain an active indictor, # window title/item text, a Bamf window, a GdxPixbuf # (an icon for the user to click to close the window, self.__list_store = Gtk.ListStore(str, str, GdkPixbuf.Pixbuf, Bamf.Window, GdkPixbuf.Pixbuf) self.__active_renderer = Gtk.CellRendererText() self.__icon_renderer = Gtk.CellRendererPixbuf() self.__title_renderer = Gtk.CellRendererText() self.__close_renderer = Gtk.CellRendererPixbuf() self.__close_renderer.set_alignment(1, 0.0) # align to to topright of the cell # set default cell colours and padding self.__title_renderer.set_padding(2, 6) self.set_bg_col(32, 32, 32) # create columns for the treeview self.__col_icon = Gtk.TreeViewColumn("", self.__icon_renderer, pixbuf=4) self.__col_active = Gtk.TreeViewColumn("", self.__active_renderer, text=0) self.__col_active.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) self.__col_active.set_min_width(0) self.__col_title = Gtk.TreeViewColumn("", self.__title_renderer, text=1) self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) self.__col_title.set_expand(True) self.__col_title.set_max_width(CONST_MAX_TITLE_WIDTH) # assign an event handler to the title column so that we can use it # to display the active window title in bold text self.__col_title.set_cell_data_func(self.__title_renderer, self.draw_title) self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) self.__col_close = Gtk.TreeViewColumn("", self.__close_renderer, pixbuf=2) self.__col_close.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY) self.__col_close.set_max_width(32) # add the columns self.__tree_view.set_model(self.__list_store) self.__tree_view.append_column(self.__col_icon) self.__tree_view.append_column(self.__col_title) self.__tree_view.append_column(self.__col_close) self.__tree_view.set_row_separator_func(self.check_sep) # add the treeview to the window self.set_main_widget(self.__tree_view) self.__tree_view.set_has_tooltip(True) self.__tree_view.connect("query-tooltip", self.query_tooltip) self.__tree_view.connect("button-release-event", self.button_release) self.__tree_view.connect("size-allocate", self.treeview_allocate) self.__pb_close = None self.__pb_active = None def create_close_pixbuf(self): """ Create a 'close' icon (based on the stock close icon) for use in the treeview """ # create a pixbuf for holding the 'close' icon pb_close = self.render_icon(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU, None) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, CONST_CLOSE_ICON_SIZE, CONST_CLOSE_ICON_SIZE) ctx = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(ctx, pb_close, 0, 0) ctx.paint() # we now need to copy the cairo surface to a pixbuf. The best way to do # this would be by calling GdkPixbuf.Pixbuf.new_from_data as in these # 64) # comments. Unfortunately this function does not seem to be # introspectable (Gtk2) or not implemented yet (Gtk3) and therefore # doesn't work. # # self.__pb_close = GdkPixbuf.Pixbuf.new_from_data(surface.get_data(), # GdkPixbuf.Colorspace.RGB, # True, 8, pb_close.get_width(), # pb_close.get_height(), # Therefore we have to resort to writing the surface to a temporary # .png file and then loading it into our pixbuf ... handle, tempfn = tempfile.mkstemp() surface.write_to_png(tempfn) self.__pb_close = GdkPixbuf.Pixbuf.new_from_file(tempfn) os.remove(tempfn) def create_active_pixbuf(self): """ Create an active window icon (based on the stock forward icon) for use in the treeview See the comments in create-close-pixbuf """ # create a pixbuf for holding the icon pb_active = self.render_icon(Gtk.STOCK_GO_FORWARD, Gtk.IconSize.MENU, None) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, CONST_CLOSE_ICON_SIZE, CONST_CLOSE_ICON_SIZE) ctx = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(ctx, pb_active, 0, 0) ctx.paint() handle, tempfn = tempfile.mkstemp() surface.write_to_png(tempfn) self.__pb_active = GdkPixbuf.Pixbuf.new_from_file(tempfn) os.remove(tempfn) def treeview_allocate(self, widget, allocation): """ Event handler for the tree view size-allocate event If the title column has expanded to its maximum width, ellipsize the title text... """ if self.__col_title.get_width() == CONST_MAX_TITLE_WIDTH: self.__col_title.set_min_width(CONST_MAX_TITLE_WIDTH) self.__title_renderer.set_property("ellipsize", Pango.EllipsizeMode.END) self.__tree_view.get_selection().unselect_all() def set_colours(self, panel_colour): """ Sets the treeview colours (background, foreground and highlight) to match the window colours. Note : set_colours must have been called first so that the window colours are set correctly """ DockPopup.set_colours(self, panel_colour) # set strings used to set widget colours - we can't change # the highlight colour, only foreground and background r, g, b = self.bg_col self.__bg_str = "#%.2x%.2x%.2x" % (r, g, b) r, g, b = self.fg_col self.__fg_str = "#%.2x%.2x%.2x" % (r, g, b) # now set the treeview colours self.__title_renderer.set_property("cell-background", self.__bg_str) self.__title_renderer.set_property("foreground", self.__fg_str) self.__active_renderer.set_property("cell-background", self.__bg_str) self.__active_renderer.set_property("foreground", self.__fg_str) self.__close_renderer.set_property("cell-background", self.__bg_str) self.__icon_renderer.set_property("cell-background", self.__bg_str) def query_tooltip(self, widget, x, y, keyboard_mode, tooltip): """ Handler for the query-tooltip event to determine whether or not to show the 'Close window' tooltip If the tooltip was triggered by the keyboard, don't do anything Get the mouse coords, and if the mouse if located over the close icon show the tooltip Args: widget - the widget that received the event i.e. our treeview x - mouse x coord y - mouse y coord keyboard_mode - was the tooltip triggered by the keyboard? tooltip - the tooltip object that will be displayed Returns: True to display the tooltip, False otherwise """ if keyboard_mode: return False return False # path, col, xrel, yrel = self.__tree_view.get_path_at_pos(x, y) # if col == self.__col_close: # cell_area = self.__tree_view.get_cell_area(path, col) # if (x >= cell_area.x + cell_area.width - CONST_CLOSE_ICON_SIZE) and \ # (y <= cell_area.y + CONST_CLOSE_ICON_SIZE): # tooltip.set_text(_("Close window")) # return True # else: # return False # else: # return False def button_release(self, widget, event): """ Handler for the button release event If the middle or right mouse button was pressed, do nothing If the mouse button was released over the close icon, close the associated window If the mouse button was released over any other part of the tree view item, activate the associated window Finally, hide the window list Args: widget : the widget the received the signal i.e. our treeview event : the event parameters Returns: True: to stop any other handlers from being invoked """ if event.button != 1: return False # let other handlers run path, col, xrel, yrel = self.__tree_view.get_path_at_pos(event.x, event.y) sel_iter = self.__list_store.get_iter(path) win = self.__list_store.get_value(sel_iter, 3) if (win is None) and (action is None): # this will allow e.g. an item to be added which just contains # the name of app, which can then be clicked to launch the app self.hide() self.the_app.start_app() return True if col == self.__col_close: cell_area = self.__tree_view.get_cell_area(path, col) if (event.x >= cell_area.x + cell_area.width - CONST_CLOSE_ICON_SIZE) and \ (event.y <= cell_area.y + CONST_CLOSE_ICON_SIZE): window_control.close_win(win, event.time) self.hide() return True if win is not None: # if the window to be activated is not on the current workspace, # switchto that workspace wnck_win = Wnck.Window.get(win.get_xid()) wnck_aws = self.wnck_screen.get_active_workspace() wnck_ws = wnck_win.get_workspace() # the windows's current workspace can be None if it is pinned to all # workspaces or it is not on any at all... # (fix for https://bugs.launchpad.net/ubuntu/+source/mate-dock-applet/+bug/1550392 and # https://bugs.launchpad.net/ubuntu-mate/+bug/1555336 (regarding software updater)) if (wnck_aws is not None) and (wnck_ws is not None) and \ (wnck_aws != wnck_ws): wnck_ws.activate(0) sleep(0.01) wnck_win.activate(0) # set the active indicator on the newly activated window # first, reset the current active indicator in the list store for list_item in self.__list_store: list_item[0] = "" list_item[4] = None # now set active indicator on the current item self.__list_store.set_value(sel_iter, 0, CONST__ACTIVE_TEXT) self.__list_store.set_value(sel_iter, 4, self.__pb_active) return True return True def add_separator(self): """ Convenience method to add a separator to the list If there are no items currently in the list then the separator won't be added """ if len(self.__list_store) > 0: self.add_to_list(False, CONST_SEP, None) def add_to_list(self, is_active, title, window): """ Add an item to the window list Args: is_active - True if this is a window and it is active, False otherwise title - the title of the window or the action window - the wnck window relating to the app (can be None) """ # set the active indicator if is_active: active_text = CONST__ACTIVE_TEXT else: active_text = "" if active_text != "": app_icon = self.__pb_active else: app_icon = None if window is None: close_icon = None else: close_icon = self.__pb_close self.__list_store.append([active_text, title, close_icon, window, app_icon]) def clear_win_list(self): """ Clear the list of open windows """ self.__list_store.clear() def win_button_press(self, widget, event): """ this is for debug puposes only""" Gtk.main_quit() def setup_list(self, win_on_cur_ws_only): """ Setup the app list Set the app name Re-create the close icon in case the icon theme has changed For every window the app has open add an entry containing the app icon, window title, an indicator if the window is the active window, and a close icon Args: win_on_cur_ws_only : boolean - whether to show only windows which are on the current workspace, or show windows for all workspaces """ self.create_close_pixbuf() self.create_active_pixbuf() # reduce the size of the window - it will autosize to fit the contents if build_gtk2: self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.FIXED) self.__col_title.set_fixed_width(150) self.resize(100, 10) self.__col_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) win_list = self.the_app.get_windows() wnck_aws = self.wnck_screen.get_active_workspace() add_to_list = False for win in win_list: win_type = win.get_window_type() if (win_type == Bamf.WindowType.NORMAL) or (win_type == Bamf.WindowType.DIALOG): if win_on_cur_ws_only: # only add if the window is on the current workspace wnck_win = Wnck.Window.get(win.get_xid()) add_to_list = wnck_win.is_on_workspace(wnck_aws) else: add_to_list = True if add_to_list: is_active = (len(win_list) == 1) or \ (win == self.the_app.last_active_win) self.add_to_list(is_active, win.get_name(), win) def mbutton_press(self, widget, event): """ this is for debug purposes only and demonstrates that menu.popup does not work with Gtk 2 """ self.menu = Gtk.Menu() menu_item = Gtk.MenuItem("A menu item") self.menu.append(menu_item) menu_item.show() self.menu.popup(None, None, None, event.button, event.time) def draw_title(self, column, cell_renderer, tree_model, tree_iter, data): title = tree_model.get_value(tree_iter, 1) if tree_model.get_value(tree_iter, 0) == CONST__ACTIVE_TEXT: cell_renderer.set_property('markup', "%s" % title) else: # if there is no window of action associatied with this item # it must be the name of a non running app. Format the title # differently if so... if (tree_model.get_value(tree_iter, 3) is None) and \ (tree_model.get_value(tree_iter, 4) is None): cell_renderer.set_property('markup', "%s" % title) else: cell_renderer.set_property('markup', title) def check_sep(self, model, iter, data=None): """ Check to see if the current row is to be displayed as a separator Args : model : the treeview model (will be self.__list_store) iter : the roow in the model we're interested in data : user defined data Returns: Bool """ title = model.get_value(iter, 1) return title == CONST_SEP def main(): """ main function - debugging code goes here """ # thewin = DockWinList() # thewin.set_app_name("Testing....") # thewin.add_to_list(None, False, "Win 1") # thewin.add_to_list(None, True, "Win 2 is active") # thewin.add_to_list(None, False, "Win 3") # thewin.show_all() # thewin.move(100, 110) # pos = thewin.get_position() # size = thewin.get_size() # print("pos %d %d" %(pos[0], pos[1])) # print("size %d %d" %(size[0], size[1])) # thewin.add_mouse_area(Gdk.Rectangle(pos[0]-15, pos[1]-15, size[0]+30, size[1]+30)) # thewin.add_mouse_area(Gdk.Rectangle(0, 0, 48, 500)) # thewin.add_mouse_area(Gdk.Rectangle(48, 110, 100, size[1])) # Gtk.main() return if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/dock_xml.in000066400000000000000000000433131411344606400177300ustar00rootroot00000000000000#!/usr/bin/env python3 """Read and write applet xml configuration files. Provide functionality allowing the dock applets configuration to be saved and loaded. Store the configuration in a specified XML file The file will contain the following information: : a list of all pinned app's .desktop files : the indicator type (light or dark) : whether unpinned apps from all workspaces are to be displayed : whether an indicator for each open window is to be displayed Also provide the ability to read an xml file which contains details of apps which are difficult to match with their respective .desktop files and which specifies the .desktop file to use """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson import xml.etree.ElementTree as ET import sys import os import distro as distribution def write_xml(filename, desktop_files, light_ind, show_all_apps, multi_ind, use_dock_win_list, win_from_cur_ws_only, win_switch_unity_style, panel_change_color, dock_panel_change_only, use_panel_act_list, active_icon_bg, fallback_bar_col, spacing, attention_type, popup_delay, saved_configs, pa_on_all_ws, dock_fixed_size, click_action, theme): """ Write the xml file using the specified information The xml file will be in e.g. the following format: etc... int True or False True or False True or False True or False True or False True or False ... ... Args: filename : the filename to use. If the file already exists it will be overwritten. desktop_files: a list containing the names of the applet's pinned app's .desktop files e.g. ['pluma.desktop'] light_ind : int - the indicator type e.g. light, dark, bar to be used show_all_apps : boolean - Whether or not unpinned apps from all workspaces are to be shown multi_ind : Whether indicators for each of an app's open windows are to be shown use_dock_win_list: : boolean : whether to use the applet's own window list for switching between an app's open windows, or to use Compiz thumbnail previews win_from_cur_ws_only: boolean - If True, indicators and window list items will only be shown for windows on the current workspace (new with V0.67) win_switch_unity_style : boolean - If True, first left-click on a dock-icon switches to the last focused window first, in case the app has multiple windows opened panel_change_color : boolean - Whether or not MATE panels are to change color according to the desktop wallpaper (new with V0.66) dock_panel_change_only : boolean - whether panel colour changes are to limited to only the panel containing the dock applet (new with V0.66) use_panel_act_list: boolean - whether to display an app's actions in the panel right click menu, as opposed to using the popup action list active_icon_bg: int - the type of active icon background e.g. gradient or fill fallback_bar_col: a list of the rgb elements of a color to be used for bar indicators when the theme highlight colour cannot be read spacing : the amount of spacing (0-8) between apps in the dock attention_type : how a docked app notifies the user when an app requires attention popup_delay : the delay before action/windows lists appear saved_configs : a list of tuples containing details of the pinned app config. Each tuple contains the following: string - the config name string - the workspace (if any) the config is to be automatically loaded for string - a .desktop filename specifying a pinned app string - another .desktop filename etc. etc. pa_on_all_ws : boolean - whether pinned apps appear on all workspaces or only the workspace where they were pinned dock_fixed_size : the fixed size (if any) that the dock is to maintain click_action : what the applet does when a running app's icon is clicked theme : int - the theme used by the dock Returns: Boolean - True if the file was written successfully, False otherwise """ root = ET.Element("root") pa_el = ET.Element("pinned_apps") for df in desktop_files: df_el = ET.Element("desktop_file", name=df) pa_el.append(df_el) ind_el = ET.Element("ind_type", light="%d" % light_ind) sa_el = ET.Element("show_all", show_all="%s" % show_all_apps) mi_el = ET.Element("multi_ind", show_multi="%s" % multi_ind) uwl_el = ET.Element("use_win_list", uwl="%s" % use_dock_win_list) wcw_el = ET.Element("win_from_cur_ws_only", wcw="%s" % win_from_cur_ws_only) wsu_el = ET.Element("win_switch_unity_style", wsu="%s" % win_switch_unity_style) pcc_el = ET.Element("panel_change_color", pcc="%s" % panel_change_color) dcc_el = ET.Element("dock_panel_change_only", dcc="%s" % dock_panel_change_only) pal_el = ET.Element("panel_act_list", pal="%s" % use_panel_act_list) abg_el = ET.Element("bg_type", type="%d" % active_icon_bg) fbc_el = ET.Element("fallback_bar_col") for col in fallback_bar_col: col_el = ET.Element("col", value="%s" % col) fbc_el.append(col_el) spc_el = ET.Element("spacing", spc="%d" % spacing) att_el = ET.Element("attention_type", type="%d" % attention_type) pdy_el = ET.Element("popup_delay", delay="%d" % popup_delay) sc_el = ET.Element("saved_configs") for config in saved_configs: cfg_el = ET.Element("config") cnm_el = ET.Element("config_name", name=config[0]) cws_el = ET.Element("workspace_name", workspace=config[1]) cpa_el = ET.Element("pinned_apps") for loop in range(2, len(config)): df = config[loop] df_el = ET.Element("desktop_file", name=df) cpa_el.append(df_el) cfg_el.append(cnm_el) cfg_el.append(cws_el) cfg_el.append(cpa_el) sc_el.append(cfg_el) paoaws_el = ET.Element("pinned_apps_all_workspaces", paoaws="%s" % pa_on_all_ws) dfs_el = ET.Element("dock_fixed_size", dfs="%d" % dock_fixed_size) ct_el = ET.Element("click_action", ca="%d" % click_action) theme_el = ET.Element("theme", id="%d" % theme) root.append(pa_el) root.append(ind_el) root.append(sa_el) root.append(mi_el) root.append(uwl_el) root.append(wcw_el) root.append(wsu_el) root.append(pcc_el) root.append(dcc_el) root.append(pal_el) root.append(abg_el) root.append(fbc_el) root.append(spc_el) root.append(att_el) root.append(pdy_el) root.append(sc_el) root.append(paoaws_el) root.append(dfs_el) root.append(ct_el) root.append(theme_el) try: ET.ElementTree(root).write(filename, xml_declaration=True) except FileNotFoundError: return False # invalid file or path name return True def read_xml(filename): """ Reads an xml file created using the write_xml method Args: filename - the filename to read. Returns: boolean : True if the file was read successfully, False otherwise A tuple containing the following: a list of the .desktop files in the file (i.e. the pinned apps) an integer - the indicator setting a boolean - the show_all_apps setting a boolean - the multiple indicators setting a boolean - the use window list setting a boolean - the show indicators and win list items from the current workspace only setting a boolean - the change panel colour setting a boolean - the change dock panel color only setting a boolean - the use panel action list setting an integer - the active icon background type a list of the r,g and b values (as strings) of the fallback bar indicator colour an integer - the spacing between apps an integer - the delay before window and acion lists popup a list of the workspace indexes that pinned apps are pinned to a tuple containing the details of pinned app configs - format is the same used when saving... a boolean - the pinned apps on all workspaces setting an int - the fixed size (if any) of the dock an int - the type of action to perform when a running app's icon is clicked an int - the theme being useed by the dock """ try: root = ET.parse(filename) except FileNotFoundError: return [False] df_list = [] pinned_apps = root.find("pinned_apps") if pinned_apps is not None: for df in pinned_apps: df_list.append(df.get("name")) # note - values may be missing from the config file e.g. if a new version # of the applet adds a new configuration settings. If this happens, we just # assume a default option rather than reporting an error ind_el = root.find("ind_type") if ind_el is not None: light_ind = int(ind_el.get("light")) else: light_ind = 0 ufa_el = root.find("show_all") if ufa_el is not None: show_all = ufa_el.get("show_all") == "True" else: show_all = True mi_el = root.find("multi_ind") if mi_el is not None: multi_ind = mi_el.get("show_multi") == "True" else: multi_ind = False uwl_el = root.find("use_win_list") if uwl_el is not None: use_win_list = uwl_el.get("uwl") == "True" else: use_win_list = True crla_el = root.find("win_from_cur_ws_only") if crla_el is not None: win_from_cur_ws_only = crla_el.get("wcw") == "True" else: win_from_cur_ws_only = True wsu_el = root.find("win_switch_unity_style") if wsu_el is not None: win_switch_unity_style = wsu_el.get("wsu") == "True" else: win_switch_unity_style = False crla_el = root.find("panel_change_color") if crla_el is not None: panel_change_color = crla_el.get("pcc") == "True" else: panel_change_color = False crla_el = root.find("dock_panel_change_only") if crla_el is not None: dock_panel_change_only = crla_el.get("dcc") == "True" else: dock_panel_change_only = False pal_el = root.find("panel_act_list") if pal_el is not None: use_panel_act_list = pal_el.get("pal") == "True" else: use_panel_act_list = False abg_el = root.find("bg_type") if abg_el is not None: bg_type = int(abg_el.get("type")) else: bg_type = 0 fallback_bar_col = [] fbc_col = root.find("fallback_bar_col") if fbc_col is not None and (len(fbc_col) == 3): for col in fbc_col: fallback_bar_col.append(col.get("value")) else: fallback_bar_col = ["128", "128", "128"] spc_el = root.find("spacing") if spc_el is not None: spacing = int(spc_el.get("spc")) else: spacing = 0 att_el = root.find("attention_type") if att_el is not None: attention_type = int(att_el.get("type")) else: attention_type = 0 pdy_el = root.find("popup_delay") if pdy_el is not None: popup_delay = int(pdy_el.get("delay")) else: popup_delay = 1000 saved_configs = [] sc_el = root.find("saved_configs") if sc_el is not None: for config in sc_el: config_name = "" config_ws = "" cnm_el = config.find("config_name") if cnm_el is not None: config_name = cnm_el.get("name") cws_el = config.find("workspace_name") if cws_el is not None: config_ws = cws_el.get("workspace") conf = [] conf.append(config_name) conf.append(config_ws) pinned_apps = config.find("pinned_apps") if pinned_apps is not None: for app in pinned_apps: conf.append(app.get("name")) saved_configs.append(conf) paoaws_el = root.find("pinned_apps_all_workspaces") if paoaws_el is not None: pinned_apps_all_workspaces = paoaws_el.get("paoaws") == "True" else: pinned_apps_all_workspaces = True dfs_el = root.find("dock_fixed_size") if dfs_el is not None: dock_fixed_size = int(dfs_el.get("dfs")) else: dock_fixed_size = -1 # the dock has no fixed size ct_el = root.find("click_action") if ct_el is not None: click_action = int(ct_el.get("ca")) else: click_action = 0 theme_el = root.find("theme") if theme_el is not None: theme = int(theme_el.get("id")) else: theme = 0 return [True, df_list, light_ind, show_all, multi_ind, use_win_list, win_from_cur_ws_only, win_switch_unity_style, panel_change_color, dock_panel_change_only, use_panel_act_list, bg_type, fallback_bar_col, spacing, attention_type, popup_delay, saved_configs, pinned_apps_all_workspaces, dock_fixed_size, click_action, theme] def read_app_xml(filename): """ Reads the xml file containing the list of hard to match apps Args: filename - the filename to read. Returns: boolean : True if the file was read successfully, False otherwise A list of tuples containing the following: a string - the app name (as identified by wnck) a string - the window class of the app (as identified by wnck) a string - the .desktop file to be used for this app Note: the list will only contain enries relating to the distro the app is running on """ distro = distribution.name() release = distribution.version() did = distribution.codename() if (distro is None) or (distro == ""): return [False] try: tree = ET.parse(filename) root = tree.getroot() except FileNotFoundError: return [False] app_list = [] for entry in root.findall("app"): add_it = False if (distro is not None) and (distro != ""): if distro == entry.find("distro").text: if (release is not None) and (release != ""): target_rel = entry.find("release").text if target_rel is None: # if we specify a release but the distro doesn't have one, just assume a match add_it = True else: add_it = release in target_rel else: add_it = True if add_it: app_list.append([entry.find("name").text, entry.find("class").text, entry.find("desktop").text]) return [True, app_list] def main(): """Main function. Debugging code can go here """ print(os.path.dirname(sys.argv[0])) results = read_app_xml("src/app_match.xml") if results[0]: for app in results[1]: print("App name = %s" % app[0]) print("App class = %s" % app[1]) print("App desktop = %s" % app[2]) else: print("could not read app_match.xml") # write_xml ("/home/robin/tmp/text.xml", ["thing.desktop","blah.desktop"], 99, False, True, False, False, True, False) # results = read_xml ("/home/robin/tmp/text.xml") # if results[0] == True: # for df in results[1]: # print ("Desktop file found: %s" %df) # print ("Use light ind = %d" %results[2]) # print ("Show unpinned on all = %s" %results[3]) # print ("Multi ind = %s" %results[4]) # print ("Click restore all = %s" %results[5]) # print ("pinned on all = %s" %results[6]) # print ("panel change color = %s" %results[7]) # print ("dock panel only = %s" %results[8]) # else: # print ("Error reading file....") if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/docked_app.in000066400000000000000000001747221411344606400202320ustar00rootroot00000000000000#!/usr/bin/env python3 """Provide functionality relating to an app in a dock. Allow info relating to a running app (e.g. icon, command line, .desktop file location, running processes, open windows etc) to be obtained from the information that libWnck provides Provide a surface on which the application's icon and the running indicator can be drawn Ensure that the app's icon and indicator are always drawn correctly according to the size and orientation of the panel Provide visual feedback to the user when an app is launched by pulsating the app's icon Draw a highlight around the app's icon if it is the foreground app Maintain a list of all of the app's running processes and their windows Ensure that the application's windows visually minimise to the application's icon on the dock """ # # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") gi.require_version("MatePanelApplet", "4.0") gi.require_version("Bamf", "3") from gi.repository import Gtk from gi.repository import MatePanelApplet from gi.repository import Gdk from gi.repository import GdkPixbuf from gi.repository import Wnck from gi.repository import Gio from gi.repository import GObject from gi.repository import GLib from gi.repository import Bamf import cairo import math import xdg.DesktopEntry as DesktopEntry import xdg.BaseDirectory as BaseDirectory import os import os.path import subprocess import re import colorsys from collections import namedtuple import dock_prefs from docked_app_helpers import * import window_control from log_it import log_it as log_it ColorTup = namedtuple('ColorTup', ['r', 'g', 'b']) def get_backlight_color(pixbuf): """ Read all of the pixel values in a pixbuf and calculate an appropriate colour to use as an icon backlight Code adapated from Unity desktop (https://code.launchpad.net/~unity-team/unity/trunk) specifically from LauncherIcon::ColorForIcon in LauncherIcon.cpp Args: pixbuf : a pixbuf object containing the image Returns: a tuple of r,g,b value (0-255) """ width = pixbuf.props.width rowstride = pixbuf.props.rowstride height = pixbuf.props.height num_channels = pixbuf.get_n_channels() has_alpha = pixbuf.get_has_alpha() img = pixbuf.get_pixels() r_total = g_total = b_total = 0 total = 0.0 for w_count in range(width): for h_count in range(height): pix_index = (h_count * rowstride + w_count * num_channels) pix_r = img[pix_index] pix_g = img[pix_index + 1] pix_b = img[pix_index + 2] if has_alpha: pix_a = img[pix_index + 3] else: pix_a = 255 saturation = float(max(pix_r, max(pix_g, pix_b)) - min(pix_r, min(pix_g, pix_b))) / 255.0 relevance = .1 + .9 * (float(pix_a) / 255) * saturation r_total += (pix_r * relevance) % 256 g_total += (pix_g * relevance) % 256 b_total += (pix_b * relevance) % 256 total += relevance * 255.0 r = r_total / total g = g_total / total b = b_total / total h, s, v = colorsys.rgb_to_hsv(r, g, b) if s > 0.15: s = 0.65 v = 0.6666 # Note: Unty uses v = 0.9, but this produces a very bright value which # is reduced elsewhere. We use 0.6666 to reduce it here br, bg, bb = colorsys.hsv_to_rgb(h, s, v) v = 1.0 gr, gg, gb = colorsys.hsv_to_rgb(h, s, v) return int(br * 255), int(bg * 255), int(bb * 255) def get_avg_color(pixbuf): """calculate the average colour of a pixbuf. Read all of the pixel values in a pixbuf (excluding those which are below a certain alpha value) and calculate the average colour of all the contained colours Args: pixbuf : a pixbuf object containing the image Returns: a tuple of r,g,b values (0-255) """ width = pixbuf.props.width rowstride = pixbuf.props.rowstride height = pixbuf.props.height has_alpha = pixbuf.get_has_alpha() pixels = pixbuf.get_pixels() nchannels = pixbuf.get_n_channels() # convert the pixels into an rgb array with alpha channel data = [] for y_count in range(height - 1): x_count = 0 while x_count < (width * nchannels): pix_red = pixels[x_count + (rowstride * y_count)] pix_green = pixels[x_count + 1 + (rowstride * y_count)] pix_blue = pixels[x_count + 2 + (rowstride * y_count)] if has_alpha: pix_alpha = pixels[x_count + 3 + (rowstride * y_count)] else: pix_alpha = 255 data.append([pix_red, pix_green, pix_blue, pix_alpha]) x_count += nchannels red = 0 green = 0 blue = 0 num_counted = 0 for pixels in range(len(data)): if data[pixels][3] > 200: # only count pixel if alpha above this # level red += data[pixels][0] green += data[pixels][1] blue += data[pixels][2] num_counted += 1 if num_counted > 0: ravg = int(red / num_counted) gavg = int(green / num_counted) bavg = int(blue / num_counted) else: # in case of a bad icon assume a grey average colour # this should fix a division by zero error that occurred at this point # in the code, but which I've only seen once and never been able to # duplicate ravg = gavg = bavg = 128 return ravg, gavg, bavg CONST_PULSE_STEPS = 20 CONST_PULSE_DELAY = 40 class ScrollType: """ Class to define the ways in which the docked apps may scroll in the dock""" SCROLL_NONE = 0 SCROLL_UP = 1 # for horizontal applets 'up' equates to 'left' SCROLL_DOWN = 2 # or, for horizontal applets, right... class PulseTimer(object): """Class to help provide feedback when a user launches an app from the dock. Instantiates a timer which periodically redraws an application in the dock at various transparency levels until the timer has been run a certain number of times Attributes: app = the DockedApp object which we want to pulsate timer_id = the id of the timer that is instantiated """ def __init__(self, app, once_only=False): """Init for the PulseTimer class. Sets everything up by creating the timer, setting a reference to the DockedApp and telling the app that it is pulsing Arguments: app : the DockedApp object once_only : do only one iteration of pulsing, then stop """ self.timer_count = 0 self.app = app self.app.pulse_step = 0 self.app.is_pulsing = True self.once_only = once_only self.timer_id = GObject.timeout_add(CONST_PULSE_DELAY, self.do_timer) def do_timer(self): """The timer function. Increments the number of times the time function has been called. If it hasn't reached the maximum number, increment the app's pulse counter. If the maximum number has been reached, stop the app pulsing and delete the timer. Redraw the app's icon """ # the docked app may indicate it no longer wants to pulse... if not self.app.is_pulsing: self.remove_timer() self.app.queue_draw() return False self.timer_count += 1 if self.timer_count / int(1000 / CONST_PULSE_DELAY) == 45: # we've been pulsing for long enough, the user will be getting a headache self.remove_timer() self.app.queue_draw() return False if self.app.pulse_step != CONST_PULSE_STEPS: self.app.pulse_step += 1 else: # if we're starting the app for the first time (with startup notification) # and it still hasn't finished loading, carry on pulsing if (self.app.startup_id is not None) and not self.once_only: self.app.pulse_step = 0 else: # we only want the icon to pulse once # if we have a startup_id the notification needs to be cancelled if self.app.startup_id is not None: self.app.cancel_startup_notification() self.remove_timer() self.app.queue_draw() return True def remove_timer(self): """ Cancel the timer and stop the app icon pulsing... """ self.app.is_pulsing = False GObject.source_remove(self.timer_id) CONST_BLINK_DELAY = 330 class AttentionTimer(object): """Class to help provide visual feedback when an app requries user attention. Instantiates a timer which periodically checks whether or not the app still needs attention. If the app is blinking, it toggles the blink state on and off until the app no longer needs attention Attributes: app = the DockedApp object that needs attentions timer_id = the id of the timer that is instantiated """ def __init__(self, app): """Init for the AttentionTimer class. Sets everything up by creating the timer, setting a reference to the DockedApp and setting the inital flash state to off Arguments: app : the DockedApp object """ self.app = app self.app.needs_attention = True self.app.attention_blink_on = False self.timer_id = GObject.timeout_add(CONST_BLINK_DELAY, self.do_timer) # make the app redraw itself app.queue_draw() def do_timer(self): """The timer function. If the app no longer needs attention, stop it flashing and delete the timer. Otherwise, invert the flash. Finally,Redraw the app's icon """ if self.app.needs_attention: self.app.attention_blink_on = not self.app.attention_blink_on else: GObject.source_remove(self.timer_id) self.app.queue_draw() return True class DockedApp(object): """Provide a docked app class Attributes: bamf_app : the Bamf.Applications related to the running app app_name : e.g. Google Chrome, used for tooltips and the applet right click menu etc rc_actions : A list of strings containing the names of the additional application actions suppported by the app cmd_line : the command line and arguments used to start the app icon_name : name of the app icon icon_filename : the filename of the app icon desktop_file : the filename of the app's .desktop file desktop_ai : a Gio.GDesktopAppInfo read from the .desktop file startup_id : id used for startup notifications applet_win : the Gdk.Window of the panel applet applet : the panel applet applet_orient : the applet orientation drawing_area: Gtk.Label- provides a surface on which the app icon can be drawn drawing_area_size : the base size in pixels (height AND width) that we have to draw in - note that some indicators require more and must specify this... is_pulsing : boolean - True if the app is pulsing pulse_step : a count of how far we are through the pulse animation app_pb : a pixbuf of the app's icon app_surface : a surface of the app's icon highlight_colour : ColorTup of the colours used to highlight the app when it is foreground is_active : boolean - True = the app is the foreground app has_mouse : boolean - True = the mouse is over the app's icon is_pinned : boolean - Whether or not the app is pinned to the dock indicator : the type of indictor (e.g. light or dark) to draw under running apps active_bg : the type of background (e.g. gradient or solid fill) to be drawn when the app is the active app ind_ws : wnck_workspace or None - if set, indicators are to be drawn for windows on the specified workspace last_active_win : the Bamf.Window of the app's last active window is_dragee : boolean - indicates whether or not the app's icon is being dragged to a new position on the dock show_progress : boolean - indicates whether or not to display a progress indicator on the app's icon progress_val : the progress value( 0 to 1.0) show_count : boolean - indicates whether or not to display a count value on the app's icon count_val : the value of the count needs_attention: whether or not the app needs the user's attention attention_type : how the docked app indicates to the user that the app needs attention attention_blink_on : when an app blinks when it needs attention, this specfies the state scroll_dir : indicates the way that the dock may be scrolled (if any) if the mouse hovers over this app. Also used to draw the app icon in such a way as to indicate that scrolling is available """ def __init__(self): """ Init for the DockApplet class. Create a surface to draw the app icon on Set detault values """ super().__init__() self.bamf_app = None self.app_info = [] self.app_name = "" self.rc_actions = [] self.cmd_line = "" self.icon_name = "" self.icon_filename = "" self.desktop_file = "" self.desktop_ai = None self.icon_geometry_set = False self.applet_win = None self.applet_orient = None self.ind_ws = None self.startup_id = None self.applet = None # all drawing is done to a Gtk.Label rather than e.g. a drawing area # or event box this allows panel transparency/custom backgrounds to be # honoured # However, the downside of this is that mouse events cannot be handled # by this class and instead have to be done by the applet itself self.drawing_area = Gtk.Label() self.drawing_area.set_app_paintable(True) self.drawing_area_size = 0 self.is_pulsing = False self.pulse_step = 0 self.needs_attention = False self.attention_blink_on = False self.app_pb = None self.app_surface = None self.highlight_color = ColorTup(r=0.0, g=0.0, b=0.0) self.is_active = False self.has_mouse = False self.is_pinned = False # set defaults self.indicator = IndicatorType.LIGHT # light indicator self.multi_ind = False # single indicator self.active_bg = IconBgType.GRADIENT self.attention_type = dock_prefs.AttentionType.BLINK self.last_active_win = None # set up event handler for the draw/expose event if build_gtk2: self.drawing_area.connect("expose-event", self.do_expose_event) else: self.drawing_area.connect("draw", self.do_expose_event) self.is_dragee = False self.show_progress = False self.progress_val = 0.0 self.show_count = False self.count_val = 0 self.scroll_dir = ScrollType.SCROLL_NONE def set_bamf_app(self, b_app): """ Sets the Bamf.Application related to this docked app Params: b_app : the Bamf.Application to be added """ self.bamf_app = b_app def clear_bamf_app(self): """ Unsets the Bamf.Application related to this docked app Params: b_app : the Bamf.Application to removed """ self.bamf_app = None def has_bamf_app(self, b_app): """ Returns True if b_app is associated with this docked_app, False otherwise Params: b_app - a Bamf.Application """ return b_app == self.bamf_app def get_windows(self): """ Convenience function to return a list of the app's Bamf.Windows Returns : an empty list if the app is not running or if self.bamf_app is None, otherwise the window list """ ret_val = [] if (self.bamf_app is not None) and (self.bamf_app.is_running() or self.bamf_app.is_starting()): ret_val = self.bamf_app.get_windows() return ret_val def get_first_normal_win(self): """ Returns the app's first 'normal' window i.e. a window or dialog Returns: a Bamf.Window, or None """ if (self.bamf_app is not None) and (self.bamf_app.is_running()): for win in self.get_windows(): win_type = win.get_window_type() if win_type in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG] or win.is_user_visible(): return win return None def has_wnck_app(self, wnck_app): """ see if this app has a process with the specified wnck_app Returns True if the wnck_app is found, False otherwise """ ret_val = False for aai in self.app_info: if aai.app == wnck_app: ret_val = True break return ret_val def has_bamf_window(self, win): """ Checks to see if a window belongs to the app Params: win : the Bamf.Window we're interested in Returns: True if the window belongs to the app, False otherwise """ windows = self.get_windows() return win in windows def setup_from_bamf(self, app_match_list): """ Setup an already running app using info from self.bamf_app This is only called when bamf cannot match an app with it's .desktop file, so we can also do some extra checking from the list of hard to match .desktop files """ # get a list of all the possible locations of .desktop files data_dirs = BaseDirectory.xdg_data_dirs app_dirs = [] for dir in data_dirs: app_dirs.append(os.path.join(dir, "applications/")) # search for a match in for app in app_match_list: if self.bamf_app.get_name() == app[0]: for dir in app_dirs: desktop_file = os.path.join(dir, app[2]) if os.path.exists(desktop_file): self.desktop_file = desktop_file if self.read_info_from_desktop_file(): return # no match, so just get basic info self.app_name = self.bamf_app.get_name() self.icon_name = "wnck" # indicate we want to get the app icon from wnck def set_app_name(self, app_name): """sets the app name. Stores the entire name, which may or may not also contain a document title or other app specific info. This will need to be parsed when necessary to obtain the actual app name Args: The app name """ self.app_name = app_name def set_urgency(self, urgent): """ Sets the app's urgency state Params : urgent - bool, whether or not the app is signalling urgency """ if urgent: if not self.needs_attention: self.needs_attention = True self.attention_blink_on = False # initial blink state = off timer = AttentionTimer(self) if not self.is_visible(): self.show_icon() else: if self.needs_attention: # we need to turn flashing off self.needs_attention = False self.queue_draw() # the timer will handle the rest .... # hiding the icon (if necessary) will be taken care of next # time the user changes workspace def get_cmdline_from_pid(self, pid): """ Find the command line and arguments used to launch the app Use the ps command to return the command line and arguments for the specified pid Set self.path to the full command line Args: pid - a process id """ cmdstr = "xargs -0 < /proc/%d/cmdline" % pid cmd = subprocess.Popen(cmdstr, shell=True, stdout=subprocess.PIPE) for line in cmd.stdout: pass if line is not None: self.cmd_line = line.decode("utf-8") def has_windows_on_workspace(self, wnck_workspace): """ test whether the app has at least one window open on a specified workspace Args: wnck_workspace - the workspace to check for Returns: boolean """ for win in self.get_windows(): wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is not None: win_ws = wnck_win.get_workspace() if win_ws == wnck_workspace: return True return False def has_unminimized_windows(self): """ test whether the app has at least one unminimized window Returns: boolean """ win_list = self.get_windows() for win in win_list: wnck_win = Wnck.Window.get(win.get_xid()) if (wnck_win is not None) and (not wnck_win.is_minimized()): return True return False def hide_icon(self): """ Hides the app's icon""" self.drawing_area.set_visible(False) def show_icon(self): """ Shows the app's icon""" self.drawing_area.set_visible(True) def is_visible(self): """ Method which returns whether or not the app's icon is visible Returns: boolean """ return self.drawing_area.get_visible() def get_desktop_from_custom_launcher(self, srch_dir): """ Search the custom launchers in a specified directory for one where the Exec field is found within self.cmd_line If a match is found found, self.desktop_file is set accordingly Note: All custom launchers .desktop filenames start with "mda_" Args: srch_dir : the directory to search Returns: True if a match was found, False otherwise """ # TODO: replace DesktopEntry with Gio.DesktopAppInfo... and then # if the search dir doesn't exist, don't do anything if os.path.isdir(srch_dir) is False: return False for the_file in os.listdir(srch_dir): if (the_file.startswith("mda_")) and \ (the_file.endswith(".desktop")): the_de = DesktopEntry.DesktopEntry(srch_dir + the_file) # remove command line args from the Exec field of the .desktop de_exec = the_de.getExec().split(None, 1)[0] if self.cmd_line.find(de_exec) != -1: self.desktop_file = srch_dir + the_file return True def set_all_windows_icon_geometry(self, x, y, width, height): """Set the location on screen where all of the app's windows will be minimised to. Args: x : The X position in root window coordinates y : The Y position in root window coordinates width: the width of the minimise location height: the height of the minimise location """ for win in self.get_windows(): window_control.set_minimise_target(win, x, y, width, height) return True def get_allocation(self): """ Returns the allocated position and size of the app's icon within the applet """ alloc = self.drawing_area.get_allocation() return alloc.x, alloc.y, alloc.width, alloc.height def set_drawing_area_size(self, size): """Set the size request of the app's drawing area. Args : size : the size in pixels we need, although extra can be applied if required by indicators """ self.drawing_area_size = size extra_s = ind_extra_s(self.indicator) if extra_s == 0: self.drawing_area.set_size_request(size, size) else: # we need to allocate extra space in the x or y dimension depending on applet orientation if (self.applet_orient == MatePanelApplet.AppletOrient.DOWN) or \ (self.applet_orient == MatePanelApplet.AppletOrient.UP): self.drawing_area.set_size_request(size + extra_s, size) else: self.drawing_area.set_size_request(size, size + extra_s) def queue_draw(self): """Queue the app's icon to be redrawn. """ self.drawing_area.queue_draw() def set_indicator(self, indicator): """Set the running indicator type to the value specified Args: indicator - the indicator type """ self.indicator = indicator def set_active_bg(self, active_bg): """Set the active background type to the value specified Args: active_bg - the background type """ self.active_bg = active_bg def set_multi_ind(self, multi_ind): """ Set whether to use an indicator for each open window Args: multi_ind - boolean """ self.multi_ind = multi_ind def set_attention_type(self, attention_type): """Set the attention type to the value specified Args: indicator - the indicator type """ self.attention_type = attention_type def is_running(self): """ Is the app running ? Returns: True if the app is running, False if not """ if self.bamf_app is None: return False return self.bamf_app.is_running() def has_desktop_file(self): """ Does the app have a .desktop file? Returns: True if there is a desktop file, False otherwise """ return self.desktop_file is not None def read_info_from_desktop_file(self): """Attempt to read from read the app's desktop file. Will try to read the icon name and app name from the desktop file Will also get the executeable path if we don't already have this Will read the details of any right click menu options the .desktop file defines Returns: True if successful, False otherwise """ if self.desktop_file: if os.path.isabs(self.desktop_file): self.desktop_ai = Gio.DesktopAppInfo.new_from_filename(self.desktop_file) else: self.desktop_ai = Gio.DesktopAppInfo.new(self.desktop_file) self.app_name = self.desktop_ai.get_locale_string("Name") self.icon_name = self.desktop_ai.get_string("Icon") # if the desktop file does not specify an icon name, use the app # name instead if (self.icon_name is None) or (self.icon_name == ""): self.icon_name = self.app_name.lower() # hack for the MATE application browser app, where the # .desktop file on Ubuntu does not specify an icon if self.icon_name == "application browser": self.icon_name = "computer" # get the command specified in the .desktop file used to launch the app self.cmd_line = self.desktop_ai.get_string("Exec") # get the list of addtional application actions (to be activated by right # clicking the app's dock icon) self.rc_actions = self.desktop_ai.list_actions() return True return False def app_has_custom_launcher(self): """ Determines whether the docked app has a custom launcher Examine the .desktop filename. If it starts with "~/.local/share/applications/mda_" the app has a custom launcher Returns : True if the app has a custom launcher, False otherwise """ cl_start = os.path.expanduser("~/.local/share/applications/mda_") return os.path.expanduser(self.desktop_file).beginswith(cl_start) def win_state_changed(self, wnck_win, changed_mask, new_state): """Handler for the wnck_window state-changed event If the app needs attention and we're not already flashing the icon start it flashing. If the app icon is not visible, make it visible If the app doesn't need attention and its icon is flashing, stop it flashing """ if ((new_state & Wnck.WindowState.DEMANDS_ATTENTION) != 0) or\ ((new_state & Wnck.WindowState.URGENT) != 0): if not self.needs_attention: self.needs_attention = True self.attention_blink_on = False # initial blink state = off timer = AttentionTimer(self) if not self.is_visible(): self.show_icon() else: if self.needs_attention: # we need to turn flashing off self.needs_attention = False self.queue_draw() # the timer will handle the rest .... # hiding the icon (if necessary) will be taken care of next # time the user changes workspace def get_num_windows(self, cur_ws=None): """ Get the number of normal and dialog windows the app has open. If cur_ws is specfied, then only windows on the specified workspace are counted Params: cur_ws - an int representing the workspace number: Returns: an int """ num_win = 0 if self.bamf_app is not None: for win in self.get_windows(): win_type = win.get_window_type() if win_type in [Bamf.WindowType.NORMAL, Bamf.WindowType.DIALOG] and win.is_user_visible(): if cur_ws is None: num_win += 1 else: xid = win.get_xid() wnck_win = Wnck.Window.get(xid) if (wnck_win is not None) and wnck_win.is_on_workspace(cur_ws): num_win += 1 return num_win def do_expose_event(self, drawing_area, event): """The main drawing event for the docked app. Does the following: draw the app icon if the mouse is over the app icon, highlight the icon if the is running draw the app running indicators(according to the applet orientation) if the app is the foreground app, highlight the background with a gradient fill if the app is pulsing, draw the icon with a variable level of transparency according to the pulse count if the app is flashing, draw the icon either fully opaque or completely transparent according to its flash state if the app is being dragged to a new position on the dock, draw a completely transparent background Args: drawing_area : the drawing area that related to the event. Will always be the same as self.drawing area event : in Gtk2 the event arguments, in Gtk3 a cairo context to draw on """ # there are lots of drawing operations to be done, so do them to an # offscreen surface and when all is finished copy this to the docked # app if self.applet_orient == MatePanelApplet.AppletOrient.DOWN or \ self.applet_orient == MatePanelApplet.AppletOrient.UP: oss_w = self.drawing_area_size + ind_extra_s(self.indicator) oss_h = self.drawing_area_size else: oss_w = self.drawing_area_size oss_h = self.drawing_area_size + ind_extra_s(self.indicator) offscreen_surface = cairo.Surface.create_similar(self.app_surface, cairo.CONTENT_COLOR_ALPHA, oss_w, oss_h) ctx = cairo.Context(offscreen_surface) if self.is_dragee is False: # convert the highlight values to their cairo equivalents red = self.highlight_color.r / 255 green = self.highlight_color.g / 255 blue = self.highlight_color.b / 255 dbgd = None if self.applet_win is not None: scale_factor = self.applet_win.get_scale_factor() else: scale_factor = 1 if self.active_bg == IconBgType.UNITY_FLAT: dbgd = UnityFlatBackgroundDrawer(ctx, self.drawing_area_size, self.applet_orient, red, green, blue, self.is_running(), scale_factor) elif self.active_bg == IconBgType.UNITY: dbgd = UnityBackgroundDrawer(ctx, self.drawing_area_size, self.applet_orient, red, green, blue, self.is_running(), scale_factor) elif self.is_active: if self.active_bg == IconBgType.GRADIENT: dbgd = DefaultBackgroundDrawer(ctx, self.drawing_area_size, self.applet_orient, red, green, blue) else: dbgd = AlphaFillBackgroundDrawer(ctx, self.drawing_area_size, self.applet_orient, red, green, blue, 0.5) if dbgd is not None: dbgd.draw() # draw the app icon if self.active_bg in [IconBgType.UNITY_FLAT, IconBgType.UNITY]: pb_size = (self.drawing_area_size) * 0.75 offset = self.drawing_area_size / 2 - pb_size / 2 ctx.set_source_surface(self.app_surface, offset, offset) else: ctx.set_source_surface(self.app_surface, 3, 3) if self.is_pulsing: # draw the icon semi-transparently according to how far through the # animation we are half_way = int(CONST_PULSE_STEPS / 2) if self.pulse_step <= half_way: alpha = 1.0 - (self.pulse_step / half_way) else: alpha = 0.0 + (self.pulse_step - half_way) / half_way ctx.paint_with_alpha(alpha) elif self.needs_attention and self.attention_type == dock_prefs.AttentionType.BLINK: if self.attention_blink_on: ctx.paint() # draw normally if in the flash on state elif self.is_dragee: ctx.paint_with_alpha(0.0) else: ctx.paint() ctx.save() if self.active_bg == IconBgType.UNITY_FLAT: if self.is_running(): dbgd.draw_shine() elif self.active_bg == IconBgType.UNITY: dbgd.draw_shine() ctx.restore() if (self.has_mouse is True) and ((self.is_dragee is False) and (self.scroll_dir == ScrollType.SCROLL_NONE)): # lighten the icon ctx.set_operator(cairo.OPERATOR_ADD) ctx.paint_with_alpha(0.2) ctx.set_operator(cairo.OPERATOR_OVER) elif (self.has_mouse is True) and (self.scroll_dir != ScrollType.SCROLL_NONE): # this app indicates the dock can scroll, so we darken it ctx.set_operator(cairo.OPERATOR_DEST_OUT) ctx.paint_with_alpha(0.5) ctx.set_operator(cairo.OPERATOR_OVER) # draw the app running indicators if (self.is_running()) and \ (self.indicator != IndicatorType.NONE) and \ (self.is_dragee is False): # work out how many indicators to draw - either a single one or # one for each open window up to a maximum of 4, and take into # account the fact that we might only be showing indicators from # the current workspace # get the number of indicators to show... if self.multi_ind is False and self.indicator != IndicatorType.SUBWAY: num_ind = 1 else: num_ind = self.get_num_windows(self.ind_ws) if num_ind > 4: num_ind = 4 ind = None if self.indicator == IndicatorType.LIGHT: ind = DefaultLightInd(ctx, self.drawing_area_size, self.applet_orient, num_ind) elif self.indicator == IndicatorType.DARK: ind = DefaultDarkInd(ctx, self.drawing_area_size, self.applet_orient, num_ind) elif self.indicator == IndicatorType.TBAR: ind = ThemeBarInd(ctx, self.drawing_area_size, self.applet_orient, self.applet) elif self.indicator == IndicatorType.TCIRC: ind = ThemeCircleInd(ctx, self.drawing_area_size, self.applet_orient, self.applet, num_ind) elif self.indicator == IndicatorType.TSQUARE: ind = ThemeSquareInd(ctx, self.drawing_area_size, self.applet_orient, self.applet, num_ind) elif self.indicator == IndicatorType.TTRI: ind = ThemeTriInd(ctx, self.drawing_area_size, self.applet_orient, self.applet, num_ind) elif self.indicator == IndicatorType.TDIA: ind = ThemeDiaInd(ctx, self.drawing_area_size, self.applet_orient, self.applet, num_ind) elif self.indicator == IndicatorType.SUBWAY: ind = SubwayInd(ctx, self.drawing_area_size, self.applet_orient, self.applet, num_ind, offscreen_surface, self.is_active) if ind is not None: ind.draw() # do we need a count? if self.show_count: self.draw_count(ctx) if self.show_progress: self.draw_progress(ctx) if self.needs_attention and self.attention_type == dock_prefs.AttentionType.SHOW_BADGE: self.draw_attention_badge(ctx) if not build_gtk2: # scrolling only available in GTK3 if self.has_mouse and (self.scroll_dir != ScrollType.SCROLL_NONE): if self.scroll_dir == ScrollType.SCROLL_UP: self.draw_scroll_up(ctx) elif self.scroll_dir == ScrollType.SCROLL_DOWN: self.draw_scroll_down(ctx) # now draw to the screen if build_gtk2: screen_ctx = self.drawing_area.window.cairo_create() screen_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) screen_ctx.clip() alloc = self.drawing_area.get_allocation() if (self.applet_orient == MatePanelApplet.AppletOrient.UP) or \ (self.applet_orient == MatePanelApplet.AppletOrient.DOWN): screen_ctx.set_source_surface(offscreen_surface, alloc.x, 0) else: screen_ctx.set_source_surface(offscreen_surface, 0, alloc.y) screen_ctx.paint() screen_ctx = None else: event.set_source_surface(offscreen_surface, 0, 0) event.paint() alloc = self.drawing_area.get_allocation() ctx = None def draw_count(self, ctx): """ Draw the app's counter value Args: ctx - the cairo context where the counter is to be drawn """ # drawing is done at a notional size 64x64 px, and then scaled # appropriately according to self.drawing_area_size draw_size = 64.0 # height of the counter = 2 pix border top and bottom + 16 pix # internal height height = 20 # work out the appropriate font size to use - has to fit within the # borders and provide some space above and below the count_val reqd_font_height = height - 8 # find a font size where the count can be shown with the required height ctx.select_font_face("", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) ctext = "%d" % self.count_val for fsize in range(24, 2, -1): ctx.set_font_size(fsize) extents = ctx.text_extents(ctext) if extents[3] < reqd_font_height: font_size = fsize break # work out an appropriate width for the counter inset = height / 2 radius = inset - 1 if int(extents[2] + extents[0]) > int(radius): width = extents[2] + extents[0] + radius else: width = height + inset ctx.save() # the background color of the count is the app's highlight colour # convert the highlight values to their cairo equivalents bred = self.highlight_color.r / 255 bgreen = self.highlight_color.g / 255 bblue = self.highlight_color.b / 255 # set an appropriate text and border color if bred + bgreen + bblue > 1.5: # mid-level grey tred = tgreen = tblue = 0.0 else: tred = tgreen = tblue = 1.0 # the count is placed in the upper right of the drawing area, and we need # to calculate it's position based on the notional DA_SIZE # adj = 2 left = draw_size - width + inset - adj # do the drawing - attribution for the drawing code: # https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp ctx.scale(self.drawing_area_size / draw_size, self.drawing_area_size / draw_size) ctx.move_to(left, height - 1 + adj) ctx.arc(left, inset + adj, radius, 0.5 * math.pi, 1.5 * math.pi) ctx.arc(draw_size - inset - adj, inset + adj, radius, 1.5 * math.pi, 0.5 * math.pi) ctx.line_to(left, height - 1 + adj) ctx.set_source_rgb(bred, bgreen, bblue) ctx.fill_preserve() ctx.set_source_rgb(tred, tgreen, tblue) ctx.set_line_width(2) ctx.stroke() # draw the text ctx.move_to(left - inset + width / 2 - (extents[0] + extents[2] / 2), (height / 2) + adj + extents[3] / 2) ctx.set_source_rgb(tred, tgreen, tblue) ctx.show_text(ctext) ctx.restore() def draw_progress(self, ctx): """ Draw a progress bar to show the app's progress value Args: ctx - the cairo context where the counter is to be drawn """ def rounded_rectangle(cr, x, y, w, h, r=20): """ Convenience function to draw a rounded rectangle # Attribution: # https://stackoverflow.com/questions/2384374/rounded-rectangle-in-pygtk # This is just one of the samples from # http://www.cairographics.org/cookbook/roundedrectangles/ # A****BQ # H C # * * # G D # F****E """ cr.move_to(x + r, y) # Move to A cr.line_to(x + w - r, y) # Straight line to B cr.curve_to(x + w, y, x + w, y, x + w, y + r) # Curve to C, Control points are both at Q cr.line_to(x + w, y + h - r) # Move to D cr.curve_to(x + w, y + h, x + w, y + h, x + w - r, y + h) # Curve to E cr.line_to(x + r, y + h) # Line to F cr.curve_to(x, y + h, x, y + h, x, y + h - r) # Curve to G cr.line_to(x, y + r) # Line to H cr.curve_to(x, y, x, y, x + r, y) # Curve to A # drawing is done on a to scale of 64x64 pixels and then scaled # down to the fit the app's drawing area draw_size = 64.0 # the foreground colour of the progress is the app's highlight colour # convert the highlight values to their cairo equivalents fred = self.highlight_color.r / 255 fgreen = self.highlight_color.g / 255 fblue = self.highlight_color.b / 255 # set an appropriate border color and also a background colour for # the progress bar, based on the highlight colour if fred + fgreen + fblue > 1.5: # mid-level grey brd_red = brd_green = brd_blue = 0.0 bk_red = bk_green = bk_blue = 1.0 else: brd_red = brd_green = brd_blue = 1.0 bk_red = bk_green = bk_blue = 0.0 height = 8 # total height of the progress bar line_width = 2 # border line width int_height = height - line_width * 2 # interior height left = 8.5 width = draw_size - left * 2 # width of the progress bar top = (draw_size / 8) * 5 + 0.5 ctx.save() ctx.scale(self.drawing_area_size / draw_size, self.drawing_area_size / draw_size) # fill the interior with the background colour ctx.set_line_width(1) ctx.set_source_rgb(bk_red, bk_green, bk_blue) rounded_rectangle(ctx, left, top, width, height, 7) ctx.stroke_preserve() ctx.fill() # fill part of the interior with a different colour, depending on # the progress value ctx.set_source_rgb(fred, fgreen, fblue) rounded_rectangle(ctx, left + line_width - 1, top, (width - (line_width - 1) * 2) * self.progress_val, height, 7) ctx.fill() # draw exterior of the progress bar ctx.set_source_rgb(brd_red, brd_green, brd_blue) ctx.set_line_width(2) rounded_rectangle(ctx, left, top, width, height, 7) ctx.stroke() ctx.restore() def draw_attention_badge(self, ctx): """ Draw a badge on the app icon to indicate the app requires attention Basically a copy and paste of draw_count... Args: ctx - the cairo context where the counter is to be drawn """ # drawing is done at a notional size 64x64 px, and then scaled # appropriately according to self.drawing_area_size draw_size = 64.0 # height of the exaclamation mark = 2 pix border top and bottom + 16 pix # internal height height = 20 # work out the appropriate font size to use - has to fit within the # borders and provide some space above and below the exclamation mark reqd_font_height = height - 8 # find a font size where the count can be shown with the required height ctx.select_font_face("", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) ctext = "!" for fsize in range(24, 2, -1): ctx.set_font_size(fsize) extents = ctx.text_extents(ctext) if extents[3] < reqd_font_height: font_size = fsize break # work out an appropriate width for the badge inset = height / 2 radius = inset - 1 if int(extents[2] + extents[0]) > int(radius): width = extents[2] + extents[0] + radius else: width = height # width = height + inset width = extents[2] + extents[0] + radius ctx.save() # the background color of the badge is the app's highlight colour # convert the highlight values to their cairo equivalents bred = self.highlight_color.r / 255 bgreen = self.highlight_color.g / 255 bblue = self.highlight_color.b / 255 # set an appropriate text and border color if bred + bgreen + bblue > 1.5: # mid-level grey tred = tgreen = tblue = 0.0 else: tred = tgreen = tblue = 1.0 # the badge is placed in the upper left of the drawing area adj = 2 left = inset # do the drawing - attribution for the drawing code: # https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp ctx.scale(self.drawing_area_size / draw_size, self.drawing_area_size / draw_size) ctx.move_to(left, height - 1 + adj) ctx.arc(left, inset + adj, radius, 0.5 * math.pi, 1.5 * math.pi) ctx.arc(left + width - inset - adj, inset + adj, radius, 1.5 * math.pi, 0.5 * math.pi) ctx.line_to(left, height - 1 + adj) ctx.set_source_rgb(bred, bgreen, bblue) ctx.fill_preserve() ctx.set_source_rgb(tred, tgreen, tblue) ctx.set_line_width(2) ctx.stroke() # draw the text ctx.move_to(left + inset - (extents[0] + extents[2] / 2) - radius, (height / 2) + adj + extents[3] / 2) ctx.set_source_rgb(tred, tgreen, tblue) ctx.show_text(ctext) ctx.restore() def draw_scroll_up(self, ctx): """ To indicate that the docked app can scroll up (or left on horizontal panels) draw an up (or left) arrow on the icon Params : context : the docked app's cairo context for us to draw on size : the size of the context, in pixels orient : the orientation of the dock applet """ if self.drawing_area_size > 48: icon_size = Gtk.IconSize.DND icon_pix = 24 else: icon_size = Gtk.IconSize.LARGE_TOOLBAR icon_pix = 16 if self.applet_orient in [MatePanelApplet.AppletOrient.UP, MatePanelApplet.AppletOrient.DOWN]: arrow_pb = self.drawing_area.render_icon(Gtk.STOCK_GO_BACK, icon_size, None) arrow_pb = arrow_pb.scale_simple(self.drawing_area_size / 2, self.drawing_area_size / 2, GdkPixbuf.InterpType.BILINEAR) Gdk.cairo_set_source_pixbuf(ctx, arrow_pb, 0, self.drawing_area_size / 4) else: arrow_pb = self.drawing_area.render_icon(Gtk.STOCK_GO_UP, icon_size, None) arrow_pb = arrow_pb.scale_simple(self.drawing_area_size / 2, self.drawing_area_size / 2, GdkPixbuf.InterpType.BILINEAR) Gdk.cairo_set_source_pixbuf(ctx, arrow_pb, self.drawing_area_size / 4, 0) ctx.paint() def draw_scroll_down(self, ctx): """ To indicate that the docked app can scroll up (or left on horizontal panels) draw an up (or left) arrow on the icon Params : context : the docked app's cairo context for us to draw on size : the size of the context, in pixels orient : the orientation of the dock applet """ if self.drawing_area_size > 48: icon_size = Gtk.IconSize.DND icon_pix = 24 else: icon_size = Gtk.IconSize.LARGE_TOOLBAR icon_pix = 16 if self.applet_orient in [MatePanelApplet.AppletOrient.UP, MatePanelApplet.AppletOrient.DOWN]: arrow_pb = self.drawing_area.render_icon(Gtk.STOCK_GO_FORWARD, icon_size, None) arrow_pb = arrow_pb.scale_simple(self.drawing_area_size / 2, self.drawing_area_size / 2, GdkPixbuf.InterpType.BILINEAR) Gdk.cairo_set_source_pixbuf(ctx, arrow_pb, self.drawing_area_size / 2, self.drawing_area_size / 4) else: arrow_pb = self.drawing_area.render_icon(Gtk.STOCK_GO_DOWN, icon_size, None) arrow_pb = arrow_pb.scale_simple(self.drawing_area_size / 2, self.drawing_area_size / 2, GdkPixbuf.InterpType.BILINEAR) Gdk.cairo_set_source_pixbuf(ctx, arrow_pb, self.drawing_area_size / 4, self.drawing_area_size / 2) ctx.paint() def set_pixbuf(self, pixbuf): """Set the app pixbuf and calculate its average colour. """ self.app_pb = pixbuf rht, ght, bht = self.highlight_color = get_backlight_color(pixbuf) self.highlight_color = ColorTup(r=rht, g=ght, b=bht) def set_surface(self, surface): """Set the app surface """ self.app_surface = surface def start_app(self): """Start the app or open a new window if it's already running Use Gio.DesktopAppinfo as it supports startup notfication """ # start the app try: run_it = self.desktop_ai.get_string("Exec") except: run_it = None if run_it is not None: # hack for Linux Mint: # Mint has several shortcuts for starting caja so that it can # be started in a specific directory e.g. home, /, etc # However, the main caja.desktop is responsible for starting the # user's desktop and this is the .desktop file the applet finds # first. # When the caja icon on the applet is clicked, caja is run as a # desktop window and no new file browser appears. # To get around this, we can simply check the command that is going # to be run and change it so that a caja window opens in the user's # home directory, which is the behaviour they'll probably be # expecting.... if run_it == "/usr/bin/startcaja": run_it = "caja" self.run_cmd_line(run_it) return if self.desktop_file is not None: gdai = Gio.DesktopAppInfo.new_from_filename(self.desktop_file) else: gdai = None disp = Gdk.Display.get_default() if build_gtk2: alc = Gdk.AppLaunchContext() else: alc = disp.get_app_launch_context() alc.set_desktop(-1) # use default screen & desktop alc.set_timestamp(Gtk.get_current_event_time()) alc.connect("launch-failed", self.launch_failed) # indicate we want startup notification if gdai is not None: self.startup_id = alc.get_startup_notify_id(gdai, []) gdai.launch_uris_as_manager([], alc, GLib.SpawnFlags.SEARCH_PATH, None, None, None, None) # make the app's icon pulse if not self.is_running(): throbber = PulseTimer(self) else: # if the app is already running, we want the icon to pulse at most once only # For apps which don't open a new window, the pulse timer will end up cancelling # the unneeded startup notification throbber = PulseTimer(self, True) def cancel_startup_notification(self): """ Cancel any startup notification """ if build_gtk2: Gdk.notify_startup_complete_with_id(self.startup_id) else: display = Gdk.Display.get_default() display.notify_startup_complete(self.startup_id) self.startup_id = None def launch_failed(self, app_launch_context, startup_id): """Handler for app launch failure events Cancel the startup notification Args: app_launch_context : the Gdk.AppLaunchContext that failed startup_id : the startup notification id """ self.cancel_startup_notification() display = Gdk.Display.get_default() display.notify_startup_complete(startup_id) def run_cmd_line(self, cmd_line): """Run a command line. Args: cmd_line - the command to run """ # TODO: this is old code and needs to be removed # the command line may contain escape sequences, so unescape them.... cmd_line = bytearray(cmd_line, "UTF-8") cmd_line = cmd_line.decode("unicode-escape") # if an environment variable is specified, extract its name an value # Note: the .desktop file specification at # https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html # does not mention this. Both Ubuntu # https://help.ubuntu.com/community/EnvironmentVariables#Launching_desktop_application_with_an_environment_variable # and Arch linux # https://wiki.archlinux.org/index.php/Desktop_entries#Modify_environment_variables # seem to indicate that only a single variable can be set and that # there are no spaces between the variable name, the '=' character and # variable's value ..... # so, if cmd_line begins with "env" it specifies an environment variable # to set, follwed by the app e.g. env LANG=he_IL.UTF-8 /usr/bin/pluma # if cmd_line.startswith("env"): cmd_parts = cmd_line.split(" ", 2) var_parts = cmd_parts[1].split("=") var_name = var_parts[0] var_value = var_parts[1] # now we need to get the app path and args and carry on... cmd_line = cmd_parts[2] else: var_name = None var_value = None # if any of the directories in cmd_line contain a " ", they need to be # escaped head, tail = os.path.split(cmd_line) if " " in head: head = head.replace(" ", "\\ ") cmd_line = head + "/" + tail app_info = Gio.AppInfo.create_from_commandline(cmd_line, None, Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION) alc = Gdk.AppLaunchContext() alc.set_desktop(-1) # use default screen & desktop alc.set_timestamp(Gtk.get_current_event_time()) # if the .desktop specfied an environment variable, set it if (var_name is not None) and (var_value is not None): alc.setenv(var_name, var_value) file_list = GLib.List() # app_info.launch(None, alc) self.startup_id = alc.get_startup_notify_id(app_info, []) throbber = PulseTimer(self) def run_rc_action(self, act_no): """ run the right click action specified by act_no Args: act_no - integer, the action number to run """ if len(self.rc_actions) >= act_no: if build_gtk2: alc = Gdk.AppLaunchContext() else: disp = Gdk.Display.get_default() alc = disp.get_app_launch_context() alc.set_desktop(-1) # use default screen & desktop alc.set_timestamp(Gtk.get_current_event_time()) alc.connect("launch-failed", self.launch_failed) # indicate we want startup notification self.startup_id = alc.get_startup_notify_id(self.desktop_ai, []) self.desktop_ai.launch_action(self.rc_actions[act_no - 1], alc) self.start_pulsing() def get_rc_action(self, act_no): """ return a specified right click action's details Args: act_no - integer, the specified action number Returns: bool - True if the action exists, False otherwise string - the name of the action (i.e. the text to appear in the right click menu) """ if len(self.rc_actions) >= act_no: return True, self.desktop_ai.get_action_name(self.rc_actions[act_no - 1]) else: return False, "" def start_pulsing(self): """ start the dock icon pulsing """ throbber = PulseTimer(self) def pulse_once(self): """ Make the dock icon pulse once""" throbber = PulseTimer(self, True) def set_dragee(self, is_dragee): """ Set the flag which indicates whether or not this app is being dragged to a new position on the dock Set the value of the self.is_dragee flag and redraw the app icon """ self.is_dragee = is_dragee self.queue_draw() def set_progress_visible(self, is_visible): """ Update the progress visibility and cause the app's icon to be redrawn Args: is_visible : whether the progress is to be displayed """ if is_visible != self.show_progress: self.show_progress = bool(is_visible) self.queue_draw() def set_progress_value(self, val): """ Update the progress value and cause the app's icon to be redrawn Args: val : the counter value """ # if the new progressvalue is the same as the old, then there's no need # to do anything... if val != self.progress_val: self.progress_val = val self.queue_draw() def set_counter_visible(self, is_visible): """ Update the counter visibility and cause the app's icon to be redrawn Args: is_visible : whether the counter is to be displayed """ # if the new value is the same as the old, then there's no need # to do anything... if is_visible != self.show_count: self.show_count = bool(is_visible) self.queue_draw() def set_counter_value(self, val): """ Update the counter value and cause the app's icon to be redrawn Args: val : the counter value """ # if the new counter value is the same as the old, then there's no need # to do anything... if val != self.count_val: self.count_val = val self.queue_draw() def set_scroll_dir(self, scroll_dir): """ Sets the app's scroll direction Param: scroll_dir - a docked_app_helpers.ScrollType """ self.scroll_dir = scroll_dir def main(): """Main function. Debugging code can go here """ pass if __name__ == "__main__": main() mate-dock-applet-21.10.0/src/docked_app_helpers.in000066400000000000000000001115111411344606400217370ustar00rootroot00000000000000#!/usr/bin/env python3 """ Helper classes for apps in the dock Provide a base class and descendants which will allow various types of indicators to be drawn onto a Cairo canvas provided by an app in the dock Provide a base class and descendants which will allow various types of backgrounds (e.g. gradient fill) to be drawn onto a Cairo canvas provided by an app in the dock """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("MatePanelApplet", "4.0") from gi.repository import Gtk from gi.repository import MatePanelApplet from gi.repository import GdkPixbuf from gi.repository import Gdk import cairo import math import os class IndicatorType: """Class to define the indicator types""" LIGHT = 0 # Light Circle DARK = 1 # Dark Circle NONE = 2 TBAR = 3 # Theme Bar TCIRC = 4 # Circle drawn in theme colour TSQUARE = 5 # Square drawn in theme colour TTRI = 6 # Triangle drawn in theme colour TDIA = 7 # Diamond drawn in theme colour SUBWAY = 8 # Metro type class IconBgType: """Class to define the icon background types""" GRADIENT = 0 ALPHAFILL = 1 UNITY = 2 UNITY_FLAT = 3 # static list to hold the rgb colour elements to use when drawing # and indicator and we can't get the theme highlight colour i.e. when # using gtk2 fallback_ind_col = [0.9, 0.9, 0.9] def get_theme_highlight_col(applet): """ get the current theme's highlight colour (Gtk3) or the fallback colour (Gtk2) Args: applet : the dock applet :return: a tuple containing the r,g,b values (0-1.0) of the colors """ if build_gtk2: return fallback_ind_col else: context = applet.get_style_context() sel_bg = context.lookup_color("theme_selected_bg_color") if sel_bg[0]: hcol = sel_bg[1] return [hcol.red, hcol.green, hcol.blue] else: # assume what is hopefully a decent looking highlight # colour - something a bit brighter (or maybe a lot darker) # than the background c_info = context.lookup_color("dark_bg_color") if c_info[0]: bgcol = c_info[1] return [(bgcol.red + 0.25) % 1, (bgcol.green + 0.25) % 1, (bgcol.blue + 0.25) % 1] else: # we don't even have a background colour, so.... return fallback_ind_col class IndicatorDrawer(object): """ Base class for drawing indicators Provide a base class which can be used for drawing various type of app indicators onto Cairo surfaces This class must not be instantiated and it will be an error to try and use this to draw indicators. Descendant classes will be implement their own drawing functions Attributes: _context : the cairo context to draw onto _size : the size (width and height - assumes is square...) of the docked app _orient : the orientation of the dock applet e.g. MatePanelApplet.AppletOrient.RIGHT _num_ind : the number of indicators to draw, may not be applicable to all indicators extra_s : (static) amount of extra size (in pixels) the indicator requires of the docked_app Descendant classes can implement their own properties as necessary if they need more control over the drawing process (e.g. to set a specific indicator colour) """ extra_s = 0 # default value for most indicators - those which require more must override this def __init__(self, context, size, orient, num_ind=0): """ Constructor Set attributes according to the constructor parameters """ super().__init__() self._context = context self._size = size self._orient = orient self._num_ind = num_ind def draw(self): """ Abstract method - descendants will implement this as required and return _surface when drawing is completed """ raise NotImplementedError("Must be implemented by Indicator subclasses") class DefaultInd(IndicatorDrawer): """ Base class for the two variants (light and dark) of the default indicator """ def __init__(self, context, size, orient, num_ind): """ Constructor - call the inherited constructor and do additional bits of setup """ super().__init__(context, size, orient, num_ind) # set the cairo colour values of the inner and outer areas of the indicator self._col1 = [0.0, 0.0, 0.0] self._col2 = [0.0, 0.0, 0.0] def draw(self): """ Draw up to 4 indicators """ if self._orient == MatePanelApplet.AppletOrient.RIGHT: ind_x = 2 ind_y = (self._size - 4) / (self._num_ind + 1) + 2 elif self._orient == MatePanelApplet.AppletOrient.LEFT: ind_x = self._size - 1 ind_y = (self._size - 4) / (self._num_ind + 1) + 2 elif self._orient == MatePanelApplet.AppletOrient.DOWN: ind_x = (self._size - 4) / (self._num_ind + 1) + 2 ind_y = 2 else: ind_x = (self._size - 4) / (self._num_ind + 1) + 2 ind_y = self._size - 1 this_ind = 1 while this_ind <= self._num_ind: rad_patt = cairo.RadialGradient(ind_x, ind_y, 2, ind_x, ind_y, 4) rad_patt.add_color_stop_rgba(0, self._col1[0], self._col1[1], self._col1[2], 1) rad_patt.add_color_stop_rgba(1, self._col2[0], self._col2[1], self._col2[2], 0) self._context.set_source(rad_patt) self._context.arc(ind_x, ind_y, 6, 0, 2 * math.pi) if self._num_ind > 1: if (self._orient == MatePanelApplet.AppletOrient.RIGHT) or \ (self._orient == MatePanelApplet.AppletOrient.LEFT): ind_y += (self._size - 6) / (self._num_ind + 1) else: ind_x += (self._size - 6) / (self._num_ind + 1) this_ind += 1 self._context.fill() class DefaultLightInd(DefaultInd): """ Class to draw the dock applet's default light indicator """ def __init__(self, context, size, orient, num_ind): """ Constructor - call the inherited constructor and do additional bits of setup """ super().__init__(context, size, orient, num_ind) # cairo colour values of the inner and outer areas of the indicator self._col1 = [0.9, 0.9, 0.9] self._col2 = [0.0, 0.0, 0.0] class DefaultDarkInd(DefaultInd): """ Class to draw the dock applet's default dark indicator """ def __init__(self, context, size, orient, num_ind): """ Constructor - call the inherited constructor and do additional bits of setup """ super().__init__(context, size, orient, num_ind) # cairo colour values of the inner and outer areas of the indicator self._col1 = [0.0, 0.0, 0.0] self._col2 = [0.9, 0.9, 0.9] class BarInd(IndicatorDrawer): """ Base class for the bar indicator, a filled rectangle Multiple indicators are not supported - the bar runs the full width (or height, depending on the applet orientation) of the context and merely indicates that an app is running, not how many windows it has open... """ def __init__(self, context, size, orient): """ """ super().__init__(context, size, orient) # cairo colour values of the bar, will be overridden by descendant classes self._barcol = [0.0, 0.0, 0.0] def draw(self): """ Draw the bar along the edge of the panel adjoining the screen """ line_size = 1 self._context.set_line_cap(cairo.LINE_CAP_SQUARE) self._context.set_line_width(line_size) self._context.set_source_rgb(self._barcol[0], self._barcol[1], self._barcol[2]) if self._orient == MatePanelApplet.AppletOrient.RIGHT: rect = [[0.5, 0.5], [2.5, 0.5], [2.5, self._size - 0.5], [0.5, self._size - 0.5]] elif self._orient == MatePanelApplet.AppletOrient.LEFT: rect = [[self._size - 2.5, 0.5], [self._size - 0.5, 0, 5], [self._size - 0.5, self._size - 0.5], [self._size - 2.5, self._size - 0.5]] elif self._orient == MatePanelApplet.AppletOrient.DOWN: rect = [[0.5, 0.5], [self._size - 0.5, 0.5], [self._size - 0.5, 2.5], [0.5, 2.5]] else: rect = [[0.5, self._size - 2.5], [self._size - 0.5, self._size - 2.5], [self._size - 0.5, self._size - 0.5], [0.5, self._size - 0.5]] self._context.set_line_width(1) self._context.move_to(rect[0][0], rect[0][1]) for point in rect: self._context.line_to(point[0], point[1]) self._context.line_to(rect[0][0], rect[0][1]) self._context.stroke_preserve() self._context.fill() class ThemeBarInd(BarInd): """ A bar indicator in which the bar is drawn in the current theme's highlight colour (Gtk3) or using a fallback colour (Gtk2) """ def __init__(self, context, size, orient, applet): """ Constructor - call the inherited constructor and do additional bits of setup Args (in addition to those specified in the base class) applet : the applet """ super().__init__(context, size, orient) # set the bar color self._barcol = get_theme_highlight_col(applet) class ThemeCircleInd(IndicatorDrawer): """ Draws round indicators (up to 4) with the current theme's highlight colour """ def __init__(self, context, size, orient, applet, num_ind=0): """ Constructor - call the inherited constructor and do additional bits of setup Args (in addition to those specified in the base class) applet : the applet """ super().__init__(context, size, orient, num_ind) # set the indicator color self._indcol = get_theme_highlight_col(applet) def draw(self): """ Draw up to 4 indicators """ if self._orient == MatePanelApplet.AppletOrient.RIGHT: ind_x = 2 ind_y = (self._size - 4) / (self._num_ind + 1) + 2 elif self._orient == MatePanelApplet.AppletOrient.LEFT: ind_x = self._size - 2 ind_y = (self._size - 4) / (self._num_ind + 1) + 2 elif self._orient == MatePanelApplet.AppletOrient.DOWN: ind_x = (self._size - 4) / (self._num_ind + 1) + 2 ind_y = 2 else: ind_x = (self._size - 4) / (self._num_ind + 1) + 2 ind_y = self._size - 2 this_ind = 1 while this_ind <= self._num_ind: self._context.set_source_rgb(self._indcol[0], self._indcol[1], self._indcol[2]) self._context.set_line_width(1) self._context.arc(ind_x, ind_y, 2, 0, 2 * math.pi) self._context.close_path() self._context.fill() if self._num_ind > 1: if (self._orient == MatePanelApplet.AppletOrient.RIGHT) or \ (self._orient == MatePanelApplet.AppletOrient.LEFT): ind_y += (self._size - 6) / (self._num_ind + 1) else: ind_x += (self._size - 6) / (self._num_ind + 1) this_ind += 1 class ThemeSquareInd(IndicatorDrawer): """ Draws square indicators (up to 4) with the current theme's highlight colour """ def __init__(self, context, size, orient, applet, num_ind=0): """ Constructor - call the inherited constructor and do additional bits of setup Args (in addition to those specified in the base class) applet : the applet """ super().__init__(context, size, orient, num_ind) # set the indicator color self._indcol = get_theme_highlight_col(applet) def draw(self): """ Draw up to 4 indicators """ line_size = 1 ind_size = 3 self._context.set_line_width(line_size) # work out the x&y coords of the top left of the first indicator if self._orient == MatePanelApplet.AppletOrient.RIGHT: ind_x = 1.5 ind_y = (self._size - ind_size) / (self._num_ind + 1) + 0.5 elif self._orient == MatePanelApplet.AppletOrient.LEFT: ind_x = self._size - ind_size - 0.5 ind_y = (self._size - ind_size) / (self._num_ind + 1) + 0.5 elif self._orient == MatePanelApplet.AppletOrient.DOWN: ind_x = (self._size - ind_size) / (self._num_ind + 1) + 0.5 ind_y = 1.5 else: ind_x = (self._size - ind_size) / (self._num_ind + 1) + 0.5 ind_y = self._size - ind_size - 0.5 self._context.set_line_width(line_size) this_ind = 1 while this_ind <= self._num_ind: self._context.set_source_rgb(self._indcol[0], self._indcol[1], self._indcol[2]) self._context.rectangle(ind_x, ind_y, ind_size, ind_size) self._context.stroke_preserve() self._context.fill() if self._num_ind > 1: if (self._orient == MatePanelApplet.AppletOrient.RIGHT) or \ (self._orient == MatePanelApplet.AppletOrient.LEFT): ind_y += (self._size - 6) / (self._num_ind + 1) else: ind_x += (self._size - 6) / (self._num_ind + 1) this_ind += 1 class ThemeDiaInd(IndicatorDrawer): """ Draws diamond indicators (up to 4) with the current theme's highlight colour """ def __init__(self, context, size, orient, applet, num_ind=0): """ Constructor - call the inherited constructor and do additional bits of setup Args (in addition to those specified in the base class) applet : the applet """ super().__init__(context, size, orient, num_ind) # set the indicator color self._indcol = get_theme_highlight_col(applet) def draw(self): """ Draw up to 4 indicators """ line_size = 1 ind_size = 4.0 # self._context.set_line_cap(cairo.LINE_CAP_ROUND) self._context.set_line_width(line_size) # work out the x&y coords of the top centre of the first indicator if self._orient == MatePanelApplet.AppletOrient.RIGHT: ind_x = (ind_size / 2) + 0.5 ind_y = (self._size - ind_size) / (self._num_ind + 1) + 0.5 elif self._orient == MatePanelApplet.AppletOrient.LEFT: ind_x = self._size - (ind_size / 2) - 0.5 ind_y = (self._size - ind_size) / (self._num_ind + 1) elif self._orient == MatePanelApplet.AppletOrient.DOWN: ind_x = (self._size - ind_size) / (self._num_ind + 1) + (ind_size / 2) + 0.5 ind_y = 1.5 else: ind_x = (self._size - ind_size) / (self._num_ind + 1) + (ind_size / 2) + 0.5 ind_y = self._size - ind_size - 0.5 this_ind = 1 while this_ind <= self._num_ind: self._context.set_source_rgb(self._indcol[0], self._indcol[1], self._indcol[2]) self._context.move_to(ind_x, ind_y) self._context.line_to(ind_x + (ind_size / 2), ind_y + (ind_size / 2)) self._context.line_to(ind_x, ind_y + ind_size) self._context.line_to(ind_x - (ind_size / 2), ind_y + (ind_size / 2)) self._context.line_to(ind_x, ind_y) self._context.stroke_preserve() self._context.fill() if self._num_ind > 1: if (self._orient == MatePanelApplet.AppletOrient.RIGHT) or \ (self._orient == MatePanelApplet.AppletOrient.LEFT): ind_y += (self._size - 6) / (self._num_ind + 1) else: ind_x += (self._size - 6) / (self._num_ind + 1) this_ind += 1 class ThemeTriInd(IndicatorDrawer): """ Draws triangle indicators (up to 4) with the current theme's highlight colour """ def __init__(self, context, size, orient, applet, num_ind=0): """ Constructor - call the inherited constructor and do additional bits of setup Args (in addition to those specified in the base class) applet : the applet """ super().__init__(context, size, orient, num_ind) # set the bar color self._indcol = get_theme_highlight_col(applet) def draw(self): """ Draw up to 4 indicators """ line_size = 1 ind_width = 4 ind_height = 3 self._context.set_line_width(line_size) self._context.set_source_rgb(self._indcol[0], self._indcol[1], self._indcol[2]) # for each panel orientation we need to make sure the triangle is drawn with its # point facing the icon... if self._orient == MatePanelApplet.AppletOrient.RIGHT: ind_x = 0.5 ind_y = (self._size - ind_width) / (self._num_ind + 1) + 0.5 point1 = [ind_x, ind_y] point2 = [ind_x + ind_height, ind_y + ind_width / 2] point3 = [ind_x, ind_y + ind_width] elif self._orient == MatePanelApplet.AppletOrient.LEFT: ind_x = self._size - 0.5 ind_y = (self._size - ind_width) / (self._num_ind + 1) + 0.5 point1 = [ind_x, ind_y] point2 = [ind_x - ind_height, ind_y + ind_height / 2] point3 = [ind_x, ind_y + ind_width] elif self._orient == MatePanelApplet.AppletOrient.DOWN: ind_x = (self._size - ind_width) / (self._num_ind + 1) + 0.5 ind_y = 0.5 point1 = [ind_x, ind_y] point2 = [ind_x + ind_width / 2, ind_y + ind_height] point3 = [ind_x + ind_width, ind_y] else: ind_x = (self._size - ind_width) / (self._num_ind + 1) + 0.5 ind_y = self._size - 0.5 point1 = [ind_x, ind_y] point2 = [ind_x + ind_width / 2, ind_y - ind_height] point3 = [ind_x + ind_width, ind_y] this_ind = 1 while this_ind <= self._num_ind: self._context.move_to(point1[0], point1[1]) self._context.line_to(point2[0], point2[1]) self._context.line_to(point3[0], point3[1]) self._context.line_to(point1[0], point1[1]) self._context.stroke_preserve() self._context.fill() if self._num_ind > 1: if (self._orient == MatePanelApplet.AppletOrient.RIGHT) or \ (self._orient == MatePanelApplet.AppletOrient.LEFT): point1[1] += (self._size - 6) / (self._num_ind + 1) point2[1] += (self._size - 6) / (self._num_ind + 1) point3[1] += (self._size - 6) / (self._num_ind + 1) else: point1[0] += (self._size - 6) / (self._num_ind + 1) point2[0] += (self._size - 6) / (self._num_ind + 1) point3[0] += (self._size - 6) / (self._num_ind + 1) this_ind += 1 class SubwayInd(IndicatorDrawer): """ Indicator which mimics the Metro look """ extra_s = 4 # this type of indicator requires extra space def __init__(self, context, size, orient, applet, num_ind, surface, active): """ Args (additional): surface : the cairo surface the indicators are being drawn on active : bool - whether or not the app is active """ super().__init__(context, size, orient, num_ind) # set the color of the bar indicator for the first indicator self._barcol = get_theme_highlight_col(applet) self._surface = surface self._active = active def draw(self): """ The first open window of an app is drawn as for ThemeBarInd. If more than one indicator is required, the way it is drawn depends on whether or not the app active. If so, the last three columns (or rows, depending on the applet orientation) of the cairo surface are copied and drawn to the right (or below) the app icon. If not active, the rightmost part of the bar is drawn in a darker colour """ b_o = 0.5 # orgin of the bar in cairo units b_w = 2.0 # width of the bar in cairo units # draw the bar line_size = 1 self._context.set_line_cap(cairo.LINE_CAP_SQUARE) self._context.set_line_width(line_size) self._context.set_source_rgb(self._barcol[0], self._barcol[1], self._barcol[2]) # define the four points to the bar - the order of the points is # top left, top right, bottom right, bottom left if self._orient == MatePanelApplet.AppletOrient.RIGHT: rect = [[b_o, b_o], [b_w, b_o], [b_w, self._size - b_o], [b_o, self._size - b_o]] elif self._orient == MatePanelApplet.AppletOrient.LEFT: rect = [[self._size - b_w, b_o], [self._size - b_o, b_o], [self._size - b_o, self._size - b_o], [self._size - b_w, self._size - b_o]] elif self._orient == MatePanelApplet.AppletOrient.DOWN: rect = [[b_o, b_o], [self._size - b_o, b_o], [self._size - b_o, b_w], [b_o, b_w]] else: rect = [[b_o, self._size - b_w], [self._size - b_o, self._size - b_w], [self._size - b_o, self._size - b_o], [b_o, self._size - b_o]] self._context.move_to(rect[0][0], rect[0][1]) for point in rect: self._context.line_to(point[0], point[1]) self._context.line_to(rect[0][0], rect[0][1]) self._context.stroke_preserve() self._context.fill() if self._num_ind > 1: if self._active: # copy the required area from the surface to other parts of the surface... if self._orient == MatePanelApplet.AppletOrient.DOWN or \ self._orient == MatePanelApplet.AppletOrient.UP: sx = self._size - 4 dx = self._size + 1 dy = 0 dw = 3 dh = self._size + 1 else: sx = 0 dx = 0 dy = self._size + 1 dw = self._size + 1 dh = 3 # copy the relevant part of the source surface to a new surface copy_surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, dw, dh) copy_ctx = cairo.Context(copy_surf) copy_ctx.set_source_surface(self._surface, -sx, 0) copy_ctx.rectangle(0, 0, dw, dh) copy_ctx.fill() # now draw onto the main surface if self._orient == MatePanelApplet.AppletOrient.DOWN or \ self._orient == MatePanelApplet.AppletOrient.UP: self._context.set_source_surface(copy_surf, dx, 0) else: self._context.set_source_surface(copy_surf, 0, dy) self._context.rectangle(dx, dy, dw, dh) self._context.fill() else: # darken part of the bar and draw a seperator between both parts darken_size = 5 self._context.set_operator(cairo.OPERATOR_CLEAR) self._context.set_source_rgba(0, 0, 0, 1) if self._orient == MatePanelApplet.AppletOrient.DOWN or \ self._orient == MatePanelApplet.AppletOrient.UP: self._context.move_to(rect[1][0] - darken_size, rect[1][1]) self._context.line_to(rect[1][0] - darken_size, rect[2][1]) else: self._context.move_to(rect[2][0], rect[2][1] - darken_size) self._context.line_to(rect[3][0], rect[2][1] - darken_size) self._context.stroke() self._context.set_operator(cairo.OPERATOR_OVER) # darken with semi transparent black rectangle self._context.set_source_rgba(0, 0, 0, 0.20) if self._orient == MatePanelApplet.AppletOrient.DOWN or \ self._orient == MatePanelApplet.AppletOrient.UP: self._context.move_to(rect[1][0] - darken_size - 1, rect[0][1]) self._context.line_to(rect[1][0], rect[1][1]) self._context.line_to(rect[2][0], rect[2][1]) self._context.line_to(rect[1][0] - darken_size - 1, rect[3][1]) self._context.line_to(rect[1][0] - darken_size - 1, rect[0][1]) else: self._context.move_to(rect[2][0], rect[2][1] - darken_size - 1) self._context.line_to(rect[2][0], rect[2][1]) self._context.line_to(rect[3][0], rect[2][1]) self._context.line_to(rect[3][0], rect[2][1] - darken_size - 1) self._context.line_to(rect[2][0], rect[2][1] - darken_size - 1) self._context.stroke_preserve() self._context.fill() def ind_extra_s(indtype): """ Convenience function for returning the extra space required by an indicator Args: IndicatorType : The type of indicator e.g. ThemeTriInd Returns : int """ if indtype == IndicatorType.LIGHT: return DefaultLightInd.extra_s elif indtype == IndicatorType.DARK: return DefaultDarkInd.extra_s elif indtype == IndicatorType.TBAR: return ThemeBarInd.extra_s elif indtype == IndicatorType.TCIRC: return ThemeCircleInd.extra_s elif indtype == IndicatorType.TSQUARE: return ThemeSquareInd.extra_s elif indtype == IndicatorType.TTRI: return ThemeTriInd.extra_s elif indtype == IndicatorType.TDIA: return ThemeDiaInd.extra_s elif indtype == IndicatorType.SUBWAY: return SubwayInd.extra_s else: return 0 ########################################################################################### class BackgroundDrawer(object): """ Base class for drawing the background of dock apps Provide a base class which can be used for drawing various type of backgrounds onto Cairo surfaces This class must not be instantiated and it will be an error to try and use this to draw backgrounds. Descendant classes will implement their own drawing functions Attributes: _context : the cairo context to draw onto _size : the size (width and height - assumes is square...) of the size _orient : the orientation of the dock applet e.g. MatePanelApplet.AppletOrient.RIGHT Descendant classes can implement their own properties as necessary if they need more control over the drawing process (e.g. to set a specific indicator colour) """ def __init__(self, context, size, orient): """ Constructor Set attributes according to the constructor parameters """ super().__init__() self._context = context self._size = size self._orient = orient def draw(self): """ Abstract method - descendants will implement this as required and return _surface when drawing is completed """ raise NotImplementedError("Must be implemented by ActiveBackgroundDrawer descendents") class DefaultBackgroundDrawer(BackgroundDrawer): """ Class to draw the default active background, a colour gradient based on the the average colour of the app's icon """ def __init__(self, context, size, orient, r, g, b): """ Constructor ... Args (in addition to those of the base class): r : the red component of the average colour g : the green component of the average colour b : the blue component of the average colour """ super().__init__(context, size, orient) self._red = r self._green = g self._blue = b def draw(self): """ Do the actual drawing, based on the panel orientation """ # draw a background gradient according to the applet orientation if self._orient == MatePanelApplet.AppletOrient.RIGHT: pattern = cairo.LinearGradient(0, 0, self._size, 0) pattern.add_color_stop_rgba(0.0, self._red, self._green, self._blue, 1) pattern.add_color_stop_rgba(1.0, self._red, self._green, self._blue, 0) elif self._orient == MatePanelApplet.AppletOrient.LEFT: pattern = cairo.LinearGradient(self._size, 0, 0, 0) pattern.add_color_stop_rgba(0.0, self._red, self._green, self._blue, 1) pattern.add_color_stop_rgba(1.0, self._red, self._green, self._blue, 0) elif self._orient == MatePanelApplet.AppletOrient.DOWN: pattern = cairo.LinearGradient(0, 0, 0, self._size) pattern.add_color_stop_rgba(0.0, self._red, self._green, self._blue, 1) pattern.add_color_stop_rgba(1.0, self._red, self._green, self._blue, 0) else: pattern = cairo.LinearGradient(0, self._size, 0, 0) pattern.add_color_stop_rgba(0.0, self._red, self._green, self._blue, 1) pattern.add_color_stop_rgba(1.0, self._red, self._green, self._blue, 0) self._context.rectangle(0, 0, self._size, self._size) self._context.set_source(pattern) self._context.fill() class AlphaFillBackgroundDrawer(BackgroundDrawer): """ Fills the background with a specified colour and opacity """ def __init__(self, context, size, orient, r, g, b, a): """ Constructor ... Args (in addition to those of the base class): r : the red component of the bg colour g : the green component of the bg colour b : the blue component of the bg colour a : the alpha component """ super().__init__(context, size, orient) self._red = r self._green = g self._blue = b self.alpha = a def draw(self): """ Do the actual drawing """ self._context.rectangle(0, 0, self._size, self._size) self._context.set_source_rgba(self._red, self._green, self._blue, self.alpha) self._context.fill() class UnityFlatBackgroundDrawer(BackgroundDrawer): """ A Unity type flat backgroud (i.e. without the 'button' look) Running apps have their background filled with a specified colour (which will be based on the icon) Contains an extra method to be called after the app's icon has been drawn which will add a highlight """ bg_surf = None shine_surf = None surf_size = 0 def __init__(self, context, size, orient, r, g, b, running, scale_factor): """ Constructor ... Args (in addition to those of the base class): r : the red component of the average colour g : the green component of the average colour b : the blue component of the average colour running : whether or not the app is running scale_factor : 1 = standard def, higher values means HiDpi """ super().__init__(context, size, orient) self._red = r self._green = g self._blue = b self._running = running self._scale_factor = scale_factor # load the .svgs used to draw the background and the shine_surf if # they haven't already been loaded, or they need to be reloaded # at a different size if (UnityFlatBackgroundDrawer.bg_surf is None) or (UnityFlatBackgroundDrawer.surf_size != size): self.load_bg() self.load_shine() UnityFlatBackgroundDrawer.surf_size = size def load_shine(self): """ Load the background shine and convert to a cairo surface """ d, f = os.path.split(os.path.abspath(__file__)) if self._scale_factor == 1: fn = "assets/unity_dock_shine_56.svg" else: fn = "assets/unity_dock_shine_168.svg" # load hipi version pb = GdkPixbuf.Pixbuf.new_from_file_at_size("%s/%s" % (d, fn), self._size * 0.875, self._size * 0.875) UnityFlatBackgroundDrawer.shine_surf = Gdk.cairo_surface_create_from_pixbuf(pb, 1, None) def load_bg(self): """ Load the background and convert to a cairo surface """ d, f = os.path.split(os.path.abspath(__file__)) if self._scale_factor == 1: fn = "assets/unity_dock_bg_56.svg" else: fn = "assets/unity_dock_bg_168.svg" # load hipi version pb = GdkPixbuf.Pixbuf.new_from_file_at_size("%s/%s" % (d, fn), self._size * 0.875, self._size * 0.875) UnityFlatBackgroundDrawer.bg_surf = Gdk.cairo_surface_create_from_pixbuf(pb, 1, None) def draw(self): """ Draw the background """ offset = self._size / 16 if self._running: self._context.set_source_rgb(self._red, self._green, self._blue) self._context.mask_surface(UnityFlatBackgroundDrawer.bg_surf, offset, offset) self._context.fill() def draw_shine(self): """ Draw the shine """ offset = self._size / 16 self._context.set_source_surface(UnityFlatBackgroundDrawer.shine_surf, 4, 4) self._context.paint() class UnityBackgroundDrawer(UnityFlatBackgroundDrawer): """ A Unity type backgroud (i.e. Unity Flat bur with the 'button' look) Running apps have their background filled with a specified colour (which will be based on the icon) """ edge_surf = None edge_size = 0 def __init__(self, context, size, orient, r, g, b, running, scale_factor): """ Constructor ... Args (in addition to those of the base class): r : the red component of the average colour g : the green component of the average colour b : the blue component of the average colour running : whether or not the app is running """ super().__init__(context, size, orient, r, g, b, running, scale_factor) if (UnityBackgroundDrawer.edge_surf is None) or (UnityBackgroundDrawer.edge_size != size): self.load_edge() UnityBackgroundDrawer.edge_size = size def load_edge(self): """ Load the background shine and convert to a cairo surface """ d, f = os.path.split(os.path.abspath(__file__)) if self._scale_factor == 1: fn = "assets/unity_dock_edge_56.svg" else: fn = "assets/unity_dock_edge_168.svg" # load hipi version pb = GdkPixbuf.Pixbuf.new_from_file_at_size("%s/%s" % (d, fn), self._size * 0.875, self._size * 0.875) UnityBackgroundDrawer.edge_surf = Gdk.cairo_surface_create_from_pixbuf(pb, 1, None) def draw(self): """ Draw the background """ offset = self._size / 16 if self._running: self._context.set_source_rgb(self._red, self._green, self._blue) self._context.mask_surface(UnityFlatBackgroundDrawer.bg_surf, offset, offset) self._context.fill() else: self._context.set_source_surface(UnityBackgroundDrawer.edge_surf, 4, 4) self._context.paint() mate-dock-applet-21.10.0/src/dom_color.in000066400000000000000000000017371411344606400201110ustar00rootroot00000000000000#!/usr/bin/env python3 """ Calculate the average color of an image Code adapted from from: https://github.com/ZeevG/python-dominant-image-colour """ import binascii import struct try: import Image import ImageDraw except ImportError: from PIL import Image, ImageDraw def get_dom_color(filename): image = Image.open(filename) image = image.resize((150, 150)) # optional, to reduce time colour_tuple = [None, None, None] for channel in range(3): # Get data for one channel at a time # in case of errors stop processing and return black as the # dominant colour try: pixels = image.getdata(band=channel) except ValueError: return "000000" values = [] for pixel in pixels: values.append(pixel) colour_tuple[channel] = int(sum(values) / len(values)) colour = binascii.hexlify(struct.pack('BBB', *colour_tuple)).decode('utf-8') return colour mate-dock-applet-21.10.0/src/log_it.in000066400000000000000000000011451411344606400174020ustar00rootroot00000000000000#!/usr/bin/env python3 import os.path import time def log_it(thing, newfile=False): """Provide a quick and dirty logging facility Args: thing : the string to be written to the log file newfile : boolean - if True the log file is created, if False it is appended to """ filename = os.path.expanduser("~/tmp/log") if os.path.isdir(os.path.expanduser("~/tmp")): if newfile: thefile = open(filename, 'w') else: thefile = open(filename, 'a') thefile.write(time.strftime("%d %b %X: " + thing + "\n")) thefile.close() mate-dock-applet-21.10.0/src/org.mate.panel.DockApplet.mate-panel-applet.in000066400000000000000000000003601411344606400262670ustar00rootroot00000000000000[Applet Factory] Id=DockAppletFactory InProcess=false Location=@LOCATION@ Name=Dock Applet Factory Description=An application dock for the MATE panel [DockApplet] Name=Dock Description=An application dock for the MATE panel Icon=desktop mate-dock-applet-21.10.0/src/org.mate.panel.applet.DockAppletFactory.service.in000066400000000000000000000001431411344606400272340ustar00rootroot00000000000000[D-BUS Service] Name=org.mate.panel.applet.DockAppletFactory Exec=/usr/bin/env python3 @LOCATION@ mate-dock-applet-21.10.0/src/org.mate.panel.applet.dock.gschema.xml000066400000000000000000000167251411344606400247540ustar00rootroot00000000000000 [] the apps which have been pinned to the dock A string array containing the names of the apps which have been pinned to the dock. 0 The type of indicator (e.g light, dark or none) The type of indicator (e.g. light or dark, or no indicator) which is displayed next to running apps. false Whether to display an indicator for each open window Whether to display an indicator for each open window (maximum 4) that an application has. true Whether to show unpinned apps from all workspaces Whether to show running unpinned apps from all workspaces false Whether to show indicators and window list items for the current workspace only Whether to show indicators and window list items only for apps which have windows open on the current workspace false Whether to focus the last active window of an app on first left click instead of showing a window list Whether to focus the last active window of an app on first left click. Otherwise a window list will be shown (default behaviour) true Whether or not to use the applet's window list, or Compiz thumbnail previews Sets whether or to switch between an app's open windows using either the applet's built in window list, of Compiz window previews false Whether to show an app's action list on the panel right click menu. Whether to an apps action list on the panel right click menu. If set to false, the applet's built in action list popup will be used true Whether this is the first time the applet has been run Whether this is the first time this particular instance of the applet has been run true Specifies what to do when a running app's dock icon is click If set to true, the app's last running window is made active again. If false all of the app's windows are restored/unminimizes and the last active window is made active again false Specifies whether MATE panels are to change colour according to the desktop wallpaper If set to true, the colour of the MATE panel will change whenever the desktop wallpaper is changed, and will be set to the dominant colour of the wallpaper image false When changing MATE panel colours, specfies whether or not all panels are to changed If set to false, the colour of all MATE panels are changed. If set to true only the color of the panel containing the dock will be changed will be set to the dominant colour of the wallpaper image 0 The type of active icon background (e.g gradient or solid fill) The type of icon background (e.g. gradient or solid fill) which is displayed under the active app. ["128","128","128"] The rgb elements of the fallback color of bar and other types of indicators A colour to be when drawing bar and other types of indicators and the highlight colour of the current theme cannot be determind. Mainly intended for use with Gtk2. 0 The amount of space between app icons in the dock The amount of space between app icons in the dock - valid values 0 - 8 0 Defines how a dock icon reacts when an app requires attention Defines how a dock icon reacts when an app requires attention e.g. blink 1000 The delay before a popup window appears The delay (in milliseconds) before an action list or window list appears when the mouse hovers over a docked app [] The pinned app configurations defined for each workspace Each item in the list is a csv string containing the config name, the name of the workspace that the config will automatically be selected for, followed by .desktop filenames representing the pinned apps true Whether pinned apps appear on all workspaces, or only on the workspace where they were pinned If true, pinned apps appear on all workspaces. If false, whenever a new workspace is made active the corresponding pinned app configuration will be loaded -1 Indicates whether or not the dock should be a fixed size Speicifies the maximum number of app icons the can contain. If this is number is exceeded the dock not expand will instead allow the user to scroll app icons. A value of -1 indicates the dock is not a fixed size and will expand and contract and apps are open and closed 0 Defines what to do when a running app's dock icon is clicked 0 = display the app's window list, 1 = show compiz window spread, 2 = minimize/restore all app's windows. Note: the use-win-list key is now deprecated. 0 Specifies the theme used by the dock (e.g. Unity, Subway, Default). Sets the indicator type and icon background used by the dock. If theme is set to 'Custom' these can be individually specified. mate-dock-applet-21.10.0/src/window_control.in000066400000000000000000000104531411344606400211760ustar00rootroot00000000000000#!/usr/bin/env python3 """ Window control library Provide function to minimise, restore, activate etc. Bamf.Windows """ # Copyright (C) 1997-2003 Free Software Foundation, Inc. # # 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. # # 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. # # 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 St, Fifth Floor, Boston, MA # 02110-1301, USA. # # Author: # Robin Thompson # do not change the value of this variable - it will be set during build # according to the value of the --with-gtk3 option used with .configure build_gtk2 = False import gi if build_gtk2: gi.require_version("Gtk", "2.0") gi.require_version("Wnck", "1.0") else: gi.require_version("Gtk", "3.0") gi.require_version("Wnck", "3.0") from gi.repository import Gtk, Wnck def activate_win(win, event_time=None): """ Activate the specified window Params: win : the Bamf.Window event_time : the time of the event which triggered this activation """ if event_time is None: event_time = Gtk.get_current_event_time() wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is not None: wnck_win.activate(event_time) def minimise_win(win): """ Minimise the specified window Params: win : the Bamf.Window """ wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is not None: wnck_win.minimize() def close_win(win, event_time=0): """ Close the specified window Params: win : the Bamf.Window event_time : the event time to passed to wnck_win.close """ wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is not None: wnck_win.close(event_time) # we need to know what adjustments to apply when calculating minimize positions, # when the dock has scrolling enabled and the variable below defines # a callback which the dock can set in order to provide this info # the callback should return four integers, the x and y adjustments to # be applied to the minimise position, and the max width and height of the # scrollable area, and a string - the panel orient adj_minimise_pos_cb = None def set_minimise_target(win, x, y, width, height): """ Set the on-screen rectangle that a specified Bamf.Window will visibly minimize to Params: win : the Bamf.Window x : the x coordinate of the top left corner y : the y coordinate of the top left corner width : the width of the rectangle height : the height of the ractangle """ wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is None: return if adj_minimise_pos_cb is not None: final_x, final_y = adj_minimise_pos_cb(x, y) else: final_x = x final_y = y win_type = wnck_win.get_window_type() if ((win_type == Wnck.WindowType.NORMAL) or (win_type == Wnck.WindowType.DIALOG)) and \ (wnck_win.is_skip_tasklist() is False): wnck_win.set_icon_geometry(final_x, final_y, width, height) def get_wm_class_group_name(win): """ Use wnck to get the wm_class name of a specified bamf.window Params: win : the Bamf.Window returns: string : the wm_class_name, or None """ wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is None: return else: return wnck_win.get_class_group_name() def get_icon_pb(win): """ Use Wnck to try and get a pixbuf of a specified window's icon Params: win : the Bamf.Window Returns: a Pixbuf of the app icon, or None if the window doesn't specify an icon """ wnck_win = Wnck.Window.get(win.get_xid()) if wnck_win is None: return None elif wnck_win.get_icon_is_fallback(): return None return wnck_win.get_icon()