pax_global_header 0000666 0000000 0000000 00000000064 14113446064 0014515 g ustar 00root root 0000000 0000000 52 comment=a097520d3f019a9f0464cd26252dc63215ab1a1e
mate-dock-applet-21.10.0/ 0000775 0000000 0000000 00000000000 14113446064 0015005 5 ustar 00root root 0000000 0000000 mate-dock-applet-21.10.0/.github/ 0000775 0000000 0000000 00000000000 14113446064 0016345 5 ustar 00root root 0000000 0000000 mate-dock-applet-21.10.0/.github/FUNDING.yml 0000664 0000000 0000000 00000000710 14113446064 0020160 0 ustar 00root root 0000000 0000000 # 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/.gitignore 0000664 0000000 0000000 00000000453 14113446064 0016777 0 ustar 00root root 0000000 0000000 .*.*~
*.*~
*~
*.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/AUTHORS 0000664 0000000 0000000 00000000017 14113446064 0016053 0 ustar 00root root 0000000 0000000 Robin Thompson
mate-dock-applet-21.10.0/COPYING 0000664 0000000 0000000 00000104513 14113446064 0016044 0 ustar 00root root 0000000 0000000 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/ChangeLog 0000664 0000000 0000000 00000050727 14113446064 0016572 0 ustar 00root root 0000000 0000000 V0.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/INSTALL 0000664 0000000 0000000 00000001316 14113446064 0016037 0 ustar 00root root 0000000 0000000
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.am 0000664 0000000 0000000 00000000052 14113446064 0017036 0 ustar 00root root 0000000 0000000 SUBDIRS = src po
ACLOCAL_AMFLAGS = -I m4
mate-dock-applet-21.10.0/NEWS 0000664 0000000 0000000 00000002514 14113446064 0015506 0 ustar 00root root 0000000 0000000 15 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/README 0000664 0000000 0000000 00000001326 14113446064 0015667 0 ustar 00root root 0000000 0000000 An 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.md 0000664 0000000 0000000 00000013155 14113446064 0016271 0 ustar 00root root 0000000 0000000 # An application dock applet for the MATE panel
### Mate dock applet V0.88 on Ubuntu MATE 18.10

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/)

GTK3 version of the applet running on Ubuntu MATE 16.10 Alpha 1

Running on Arch with a Unity style layout

Running on Ubuntu with a Windows 7 style layout

Running on a Raspberry Pi 2 with Ubuntu MATE

mate-dock-applet-21.10.0/configure.ac 0000664 0000000 0000000 00000002027 14113446064 0017274 0 ustar 00root root 0000000 0000000 AC_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/ 0000775 0000000 0000000 00000000000 14113446064 0015423 5 ustar 00root root 0000000 0000000 mate-dock-applet-21.10.0/po/LINGUAS 0000664 0000000 0000000 00000000006 14113446064 0016444 0 ustar 00root root 0000000 0000000 es
fr
mate-dock-applet-21.10.0/po/Makevars 0000664 0000000 0000000 00000004763 14113446064 0017131 0 ustar 00root root 0000000 0000000 # 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.in 0000664 0000000 0000000 00000000565 14113446064 0017206 0 ustar 00root root 0000000 0000000 # 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.po 0000664 0000000 0000000 00000052264 14113446064 0016403 0 ustar 00root root 0000000 0000000 # 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.po 0000664 0000000 0000000 00000052237 14113446064 0016403 0 ustar 00root root 0000000 0000000 # 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/ 0000775 0000000 0000000 00000000000 14113446064 0015574 5 ustar 00root root 0000000 0000000 mate-dock-applet-21.10.0/src/Makefile.am 0000664 0000000 0000000 00000014407 14113446064 0017636 0 ustar 00root root 0000000 0000000
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.xml 0000664 0000000 0000000 00000007177 14113446064 0020266 0 ustar 00root root 0000000 0000000
Ubuntu16.04vivaldi-previewvivaldi-betaUbuntu16.04Mainwindow.pyPlayOnLinuxarchMainwindow.pyplayonlinuxAntergos LinuxMainwindow.pyplayonlinuxUbuntu16.04dia-gnomeDia-gnomediaUbuntu16.04Tor BrowserTor Browsertor-browser-enUbuntu16.04BzrBzrbzr-explorerUbuntu16.04aptik-gtkAptik-gtkaptikUbuntu16.04gnome-softwareGnome-softwaregnome-software-local-fileUbuntu16.04gnome-disksGnome-disksorg.gnome.DiskUtilityarchsublime_textsublime-textAntergos Linuxsublime_textsublime-text
mate-dock-applet-21.10.0/src/assets/ 0000775 0000000 0000000 00000000000 14113446064 0017076 5 ustar 00root root 0000000 0000000 mate-dock-applet-21.10.0/src/assets/unity_dock_bg_168.svg 0000664 0000000 0000000 00000003572 14113446064 0023044 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/assets/unity_dock_bg_56.svg 0000664 0000000 0000000 00000003522 14113446064 0022753 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/assets/unity_dock_edge_168.svg 0000664 0000000 0000000 00000014303 14113446064 0023352 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/assets/unity_dock_edge_56.svg 0000664 0000000 0000000 00000014125 14113446064 0023270 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/assets/unity_dock_shine_168.svg 0000664 0000000 0000000 00000005633 14113446064 0023562 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/assets/unity_dock_shine_56.svg 0000664 0000000 0000000 00000005646 14113446064 0023502 0 ustar 00root root 0000000 0000000
mate-dock-applet-21.10.0/src/dock.in 0000664 0000000 0000000 00000556070 14113446064 0017061 0 ustar 00root root 0000000 0000000 #!/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.in 0000775 0000000 0000000 00000037555 14113446064 0020260 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000023044 14113446064 0021437 0 ustar 00root root 0000000 0000000 #!/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.in 0000775 0000000 0000000 00000066251 14113446064 0020426 0 ustar 00root root 0000000 0000000 #!/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.in 0000775 0000000 0000000 00000034740 14113446064 0021744 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000045074 14113446064 0022331 0 ustar 00root root 0000000 0000000 #!/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.in 0000775 0000000 0000000 00000016141 14113446064 0020065 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000121122 14113446064 0020266 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000156514 14113446064 0020257 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000050503 14113446064 0020757 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000043313 14113446064 0017730 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000174722 14113446064 0020232 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000111511 14113446064 0021737 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000001737 14113446064 0020111 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000001145 14113446064 0017402 0 ustar 00root root 0000000 0000000 #!/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.in 0000664 0000000 0000000 00000000360 14113446064 0026267 0 ustar 00root root 0000000 0000000 [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.in 0000664 0000000 0000000 00000000143 14113446064 0027234 0 ustar 00root root 0000000 0000000 [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.xml 0000664 0000000 0000000 00000016725 14113446064 0024754 0 ustar 00root root 0000000 0000000
[]the apps which have been pinned to the dockA string array containing the names of the apps which have been pinned to the dock.0The 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.falseWhether to display an indicator for each open windowWhether to display an indicator for each open window (maximum 4) that an application has.trueWhether to show unpinned apps from all workspacesWhether to show running unpinned apps from all workspaces falseWhether to show indicators and window list items for the current workspace onlyWhether to show indicators and window list items only for apps which have windows open on the current workspacefalseWhether to focus the last active window of an app on first left click instead of showing a window listWhether to focus the last active window of an app on first left click. Otherwise a window list will be shown (default behaviour)trueWhether or not to use the applet's window list, or Compiz thumbnail previewsSets whether or to switch between an app's open windows using either the applet's built in window list, of Compiz window previewsfalseWhether 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 usedtrueWhether this is the first time the applet has been runWhether this is the first time this particular instance of the applet has been runtrueSpecifies what to do when a running app's dock icon is clickIf 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 againfalseSpecifies whether MATE panels are to change colour according to the desktop wallpaperIf 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 imagefalseWhen changing MATE panel colours, specfies whether or not all panels are to changedIf 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 image0The 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 indicatorsA 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.0The amount of space between app icons in the dockThe amount of space between app icons in the dock - valid values 0 - 80Defines how a dock icon reacts when an app requires attentionDefines how a dock icon reacts when an app requires attention e.g. blink1000The delay before a popup window appearsThe 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 workspaceEach 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 appstrueWhether pinned apps appear on all workspaces, or only on the workspace where they were pinnedIf true, pinned apps appear on all workspaces. If false, whenever a new workspace is made active the corresponding pinned app configuration will be loaded-1Indicates whether or not the dock should be a fixed sizeSpeicifies 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 closed0Defines what to do when a running app's dock icon is clicked0 = 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.0Specifies 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.in 0000664 0000000 0000000 00000010453 14113446064 0021176 0 ustar 00root root 0000000 0000000 #!/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()