pax_global_header00006660000000000000000000000064150613302650014513gustar00rootroot0000000000000052 comment=fe6e492887c1ea542300ac2860aac6f3c08c59a3 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/000077500000000000000000000000001506133026500205235ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/.gitignore000066400000000000000000000002231506133026500225100ustar00rootroot00000000000000target/ build/ _build/ builddir/ build-aux/app build-aux/.flatpak-builder/ src/config.rs *.ui.in~ *.ui~ .flatpak/ vendor .vscode .flatpak-builder/ bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/.gitlab-ci.yml000066400000000000000000000046471506133026500231720ustar00rootroot00000000000000image: fedora:latest stages: - check - test - deploy include: - project: "GNOME/citemplates" file: "flatpak/flatpak_ci_initiative.yml" - project: "GNOME/citemplates" file: "templates/basic-release-with-dist.yml" inputs: dist-job-name: "flatpak" - project: "GNOME/citemplates" file: "templates/default-rules.yml" variables: BUNDLE: "bustle-nightly.flatpak" MANIFEST_PATH: "build-aux/org.freedesktop.Bustle.Devel.json" FLATPAK_MODULE: "bustle" APP_ID: "org.freedesktop.Bustle.Devel" RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo" flatpak: extends: ".flatpak@x86_64" publish-nightly: extends: ".publish_nightly" needs: ["flatpak"] # Configure and run rustfmt # Exits and builds fails if on bad format rustfmt: image: "rust:slim" stage: check script: - rustup component add rustfmt # Create blank versions of our configured files # so rustfmt does not yell about non-existent files or completely empty files - echo -e "" >> src/config.rs - rustc -Vv && cargo -Vv - cargo fmt --version - cargo fmt --all -- --color=always --check codespell: stage: check script: - dnf install --quiet -y codespell - codespell -L crate,trough -S "*.po,target,_build" -f .flatpak-command: &flatpak-command image: '${RUNTIME_IMAGE}' tags: - flatpak script: - flatpak-builder --user --disable-rofiles-fuse --keep-build-dirs --stop-at=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH} - flatpak build-finish --share=network flatpak_app - echo "cd .. && ${COMMAND}" | flatpak-builder --user --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH} cargo-clippy: <<: *flatpak-command variables: COMMAND: "cargo clippy --all-targets --workspace --all-features -- -D warnings" cargo-test: <<: *flatpak-command variables: COMMAND: "cargo test --workspace --all-features --all-targets" check-potfiles: stage: check script: - dnf install --quiet -y gettext python3 - curl --proto '=https' --tlsv1.2 -sSf -O https://gitlab.gnome.org/msandova/check-potfiles/-/raw/master/check_potfiles.py - python3 check_potfiles.py --directory src --directory data --keyword=gettext_f --keyword='ngettext_f:1,2' --exclude src/i18n.rs cargo-audit: stage: test interruptible: true allow_failure: true script: - dnf install --quiet -y cargo-deny cargo - cargo-deny check bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/CONTRIBUTING.md000066400000000000000000000072131506133026500227570ustar00rootroot00000000000000# Contributing > This document is based on Fractal's [CONTRIBUTING.md](https://gitlab.gnome.org/World/fractal/-/blob/7a35b0ac596b5a6c0704270bcb6ce4fa809d2947/CONTRIBUTING.md) file, which is licensed under the GNU General Public License v3.0. ## Newcomers [Bustle](https://gitlab.gnome.org/World/bustle) follows the [GNOME Newcomers workflow](https://welcome.gnome.org/app/Bustle/#getting-the-app-to-build). Check out these pages to learn how to contribute. Here are also a few links to help you get started with Rust and the GTK Rust bindings: - [Learn Rust](https://www.rust-lang.org/learn) - [GUI development with Rust and GTK 4](https://gtk-rs.org/gtk4-rs/stable/latest/book) - [gtk-rs website](https://gtk-rs.org/) Don't hesitate to join [our Matrix room](https://matrix.to/#/#bustle:gnome.org) to come talk to us and ask us any questions you might have. ## Build Instructions ### Setting up Your Development Environment Bustle is written in Rust, so you will need to have at least Rust 1.85 and Cargo available on your system. If you're building Bustle with Flatpak (via GNOME Builder or the command line), you will need to manually add the necessary remotes and install the required Freedesktop extensions: ```sh # Add Flathub and the gnome-nightly repo flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo flatpak remote-add --user --if-not-exists gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo # Install the gnome-nightly Sdk and Platform runtime flatpak install --user gnome-nightly org.gnome.Sdk//master org.gnome.Platform//master # Install the required rust-stable extension from Flathub flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//25.08 # Install the required llvm extension from Flathub flatpak install --user flathub org.freedesktop.Sdk.Extension.llvm20//25.08 ``` If you are building the flatpak manually you will also need flatpak-builder on your system, or the `org.flatpak.Builder` flatpak. ### Developing with Flatpak Using [GNOME Builder](https://apps.gnome.org/Builder/) or [Flatpak VSCode Extension](https://github.com/bilelmoussaoui/flatpak-vscode) is the recommended way of building and installing Bustle. ## Committing Please follow the [GNOME commit message guidelines](https://handbook.gnome.org/development/commit-messages.html). ## Creating a Merge Request Before submitting a merge request, make sure that [your fork is available publicly](https://gitlab.gnome.org/help/user/public_access.md), otherwise CI won't be able to run. Use the title of your commit as the title of your MR if there's only one. Otherwise, it should summarize all your commits. If your commits do several tasks that can be separated, open several merge requests. In the details, write a more detailed description of what it does. If your changes include a change in the UI or the UX, provide screenshots in both light and dark mode, and/or a screencast of the new behavior. Don't forget to mention the issue that this merge request solves or is related to, if applicable. GitLab recognizes the syntax `Closes #XXXX` or `Fixes #XXXX` that will close the corresponding issue accordingly when your change is merged. We expect to always work with a clean commit history. When you apply fixes or suggestions, [amend](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---amend) or [fixup](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupamendrewordltcommitgt) and [squash](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash) your previous commits that you can then [force push](https://git-scm.com/docs/git-push#Documentation/git-push.txt--f). bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/COPYING000066400000000000000000000636421506133026500215710ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/Cargo.lock000066400000000000000000001234051506133026500224350ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "async-broadcast" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-recursion" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bustle" version = "0.10.0" dependencies = [ "anyhow", "base64", "futures-util", "gettext-rs", "gtk4", "indexmap", "libadwaita", "pcap-file", "shell-escape", "tokio", "tracing", "tracing-subscriber", "zbus", ] [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "byteorder_slice" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b294e30387378958e8bf8f4242131b930ea615ff81e8cac2440cea0a6013190" dependencies = [ "byteorder", ] [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cairo-rs" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1158f326d7b755a9ae2b36c5b5391400e3431f3b77418cedb6d7130126628f10" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", ] [[package]] name = "cairo-sys-rs" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b963177900ec8e783927e5ed99e16c0ec1b723f1f125dff8992db28ef35c62c3" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "cc" version = "1.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "cfg-expr" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d458d63f0f0f482c8da9b7c8b76c21bd885a02056cc94c6404d861ca2b8206" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "concurrent-queue" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "derive-into-owned" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d94d81e3819a7b06a8638f448bc6339371ca9b6076a99d4a43eece3c4c923" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", ] [[package]] name = "enumflags2_derive" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", "windows-sys 0.60.2", ] [[package]] name = "event-listener" version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "field-offset" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", "rustc_version", ] [[package]] name = "find-msvc-tools" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7330cdbbc653df431331ae3d9d59e985a0fecaf33d74c7c1c5d13ab0245f6c" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", ] [[package]] name = "gdk-pixbuf-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25899cc931dc28cba912ebec793b730f53d2d419f90a562fcb29b53bd10aa82" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "gdk4" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a67b064d2f35e649232455c7724f56f977555d2608c43300eabc530eaa4e359" dependencies = [ "cairo-rs", "gdk-pixbuf", "gdk4-sys", "gio", "glib", "libc", "pango", ] [[package]] name = "gdk4-sys" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2edbda0d879eb85317bdb49a3da591ed70a804a10776e358ef416be38c6db2c5" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", "glib-sys", "gobject-sys", "libc", "pango-sys", "pkg-config", "system-deps", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", "wasi 0.14.3+wasi-0.2.4", ] [[package]] name = "gettext-rs" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44e92f7dc08430aca7ed55de161253a22276dfd69c5526e5c5e95d1f7cf338a" dependencies = [ "gettext-sys", "locale_config", ] [[package]] name = "gettext-sys" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb45773f5b8945f12aecd04558f545964f943dacda1b1155b3d738f5469ef661" dependencies = [ "cc", "temp-dir", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b5e3f390d01b79e30da451dd00e27cd1ac2de81658e3abf6c1fc3229b24c5f" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", "glib", "libc", "pin-project-lite", "smallvec", ] [[package]] name = "gio-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a03f2234671e5a588cfe1f59c2b22c103f5772ea351be9cc824a9ce0d06d99fd" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", "windows-sys 0.60.2", ] [[package]] name = "glib" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60bdc26493257b5794ba9301f7cbaf7ab0d69a570bfbefa4d7d360e781cb5205" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", "memchr", "smallvec", ] [[package]] name = "glib-macros" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e772291ebea14c28eb11bb75741f62f4a4894f25e60ce80100797b6b010ef0f9" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "glib-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc7c43cff6a7dc43821e45ebf172399437acd6716fa2186b6852d2b397bf622d" dependencies = [ "libc", "system-deps", ] [[package]] name = "gobject-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9a190eef2bce144a6aa8434e306974c6062c398e0a33a146d60238f9062d5c" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "graphene-rs" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d96914394464c04df8279c23976293afd53b2588e03c9d8d9662ef6528654a85" dependencies = [ "glib", "graphene-sys", "libc", ] [[package]] name = "graphene-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8205bb19b7a041cf059be3c94d6b23b3f2c6c96362c44311dcf184e4a9422a" dependencies = [ "glib-sys", "libc", "pkg-config", "system-deps", ] [[package]] name = "gsk4" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5dbe33ceed6fc20def67c03d36e532f5a4a569ae437ae015a7146094f31e10c" dependencies = [ "cairo-rs", "gdk4", "glib", "graphene-rs", "gsk4-sys", "libc", "pango", ] [[package]] name = "gsk4-sys" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d76011d55dd19fde16ffdedee08877ae6ec942818cfa7bc08a91259bc0b9fc9" dependencies = [ "cairo-sys-rs", "gdk4-sys", "glib-sys", "gobject-sys", "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "gtk4" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938d68ad43080ad5ee710c30d467c1bc022ee5947856f593855691d726305b3e" dependencies = [ "cairo-rs", "field-offset", "futures-channel", "gdk-pixbuf", "gdk4", "gio", "glib", "graphene-rs", "gsk4", "gtk4-macros", "gtk4-sys", "libc", "pango", ] [[package]] name = "gtk4-macros" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0912d2068695633002b92c5966edc108b2e4f54b58c509d1eeddd4cbceb7315c" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "gtk4-sys" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a923bdcf00e46723801162de24432cbce38a6810e0178a2d0b6dd4ecc26a1c74" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", "graphene-sys", "gsk4-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "indexmap" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "io-uring" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags", "cfg-if", "libc", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libadwaita" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4df6715d1257bd8c093295b77a276ed129d73543b10304fec5829ced5d5b7c41" dependencies = [ "gdk4", "gio", "glib", "gtk4", "libadwaita-sys", "libc", "pango", ] [[package]] name = "libadwaita-sys" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf8950090cc180250cdb1ff859a39748feeda7a53a9f28ead3a17a14cc37ae2" dependencies = [ "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", "gtk4-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "locale_config" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" dependencies = [ "lazy_static", "objc", "objc-foundation", "regex", "winapi", ] [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" dependencies = [ "libc", ] [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", "memoffset", ] [[package]] name = "nu-ansi-term" version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "objc" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", ] [[package]] name = "objc-foundation" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" dependencies = [ "block", "objc", "objc_id", ] [[package]] name = "objc_id" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" dependencies = [ "objc", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ordered-stream" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" dependencies = [ "futures-core", "pin-project-lite", ] [[package]] name = "pango" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab47feb3403aa564edaeb68620c5b9159f8814733a7dd45f0b1a27d19de362fe" dependencies = [ "gio", "glib", "libc", "pango-sys", ] [[package]] name = "pango-sys" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f855bccb447644e149fae79086e1f81514c30fe5e9b8bd257d9d3c941116c86" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "parking" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pcap-file" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc1f139757b058f9f37b76c48501799d12c9aa0aa4c0d4c980b062ee925d1b2" dependencies = [ "byteorder_slice", "derive-into-owned", "thiserror", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro-crate" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "regex" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rustc-demangle" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.60.2", ] [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "serde_repr" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "serde_spanned" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shell-escape" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "7.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "temp-dir" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys 0.60.2", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "thread_local" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", ] [[package]] name = "tokio" version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", "slab", "socket2", "tracing", "windows-sys 0.59.0", ] [[package]] name = "toml" version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "sharded-slab", "smallvec", "thread_local", "tracing-core", "tracing-log", ] [[package]] name = "uds_windows" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ "memoffset", "tempfile", "winapi", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ "wit-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.3", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "zbus" version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" dependencies = [ "async-broadcast", "async-recursion", "async-trait", "enumflags2", "event-listener", "futures-core", "futures-lite", "hex", "nix", "ordered-stream", "serde", "serde_repr", "tokio", "tracing", "uds_windows", "windows-sys 0.60.2", "winnow", "zbus_macros", "zbus_names", "zvariant", ] [[package]] name = "zbus_macros" version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.106", "zbus_names", "zvariant", "zvariant_utils", ] [[package]] name = "zbus_names" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", "winnow", "zvariant", ] [[package]] name = "zvariant" version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", "serde", "winnow", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.106", "zvariant_utils", ] [[package]] name = "zvariant_utils" version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", "syn 2.0.106", "winnow", ] bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/Cargo.toml000066400000000000000000000013451506133026500224560ustar00rootroot00000000000000[package] name = "bustle" version = "0.10.0" authors = ["The Freedesktop community"] edition = "2024" rust-version = "1.85" [profile.release] lto = true [dependencies] adw = { package = "libadwaita", version = "0.8", features = ["v1_8"] } anyhow = "1.0.69" futures-util = "0.3.31" gettext-rs = { version = "0.7", features = ["gettext-system"] } gtk = { version = "0.10", package = "gtk4", features = ["gnome_47"] } indexmap = "2.2.1" pcap-file = "2.0.0" shell-escape = "0.1.5" tokio = { version = "1.0", default-features = false, features = [ "rt-multi-thread", ] } tracing = "0.1.36" tracing-subscriber = "0.3.15" zbus = { version = "5.6", default-features = false, features = [ "tokio", ] } [dev-dependencies] base64 = "0.22.1" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/README.md000066400000000000000000000024151506133026500220040ustar00rootroot00000000000000Download on Flathub # Bustle

Visualize D-Bus activity

Bustle draws sequence diagrams of D-Bus activity, showing signal emissions, method calls and their corresponding returns, with timestamps for each individual event and the duration of each method call. This can help you check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based application isn't performing as well as you like. It also provides statistics like signal frequencies and average method call times. ## Screenshots ![screenshot](data/screenshots/bustle-diagram.png) ## Hack on Bustle To build the development version of Bustle and hack on the code see the [general guide](https://welcome.gnome.org/en/app/Bustle/) for building GNOME apps with Flatpak and GNOME Builder. ## Translations Helping to translate Bustle or add support to a new language is very welcome. You can find everything you need at: [l10n.gnome.org/module/bustle/](https://l10n.gnome.org/module/bustle/) ## Code Of Conduct This project follows the [GNOME Code of Conduct](https://conduct.gnome.org/). bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/build-aux/000077500000000000000000000000001506133026500224155ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/build-aux/dist-vendor.sh000066400000000000000000000020751506133026500252130ustar00rootroot00000000000000#!/bin/sh # Since Meson invokes this script as # "/bin/sh .../dist-vendor.sh DIST SOURCE_ROOT" we can't rely on bash features set -eu export DIST="$1" export SOURCE_ROOT="$2" cd "$SOURCE_ROOT" mkdir "$DIST"/.cargo # cargo-vendor-filterer can be found at https://github.com/coreos/cargo-vendor-filterer # It is also part of the Rust SDK extension. cargo vendor-filterer --platform=x86_64-unknown-linux-gnu --platform=aarch64-unknown-linux-gnu > "$DIST"/.cargo/config.toml set -- vendor/gettext-sys/gettext-*.tar.* TARBALL_PATH=$1 TARBALL_NAME=$(basename "$TARBALL_PATH") rm -f "$TARBALL_PATH" # remove the tarball from checksums cargo_checksum='vendor/gettext-sys/.cargo-checksum.json' tmp_f=$(mktemp --tmpdir='vendor/gettext-sys' -t) jq -c "del(.files[\"$TARBALL_NAME\"])" "$cargo_checksum" > "$tmp_f" mv -f "$tmp_f" "$cargo_checksum" # Don't combine the previous and this line with a pipe because we can't catch # errors with "set -o pipefail" sed -i 's/^directory = ".*"/directory = "vendor"/g' "$DIST/.cargo/config.toml" # Move vendor into dist tarball directory mv vendor "$DIST" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/build-aux/org.freedesktop.Bustle.Devel.json000066400000000000000000000031251506133026500307050ustar00rootroot00000000000000{ "id": "org.freedesktop.Bustle.Devel", "runtime": "org.gnome.Platform", "runtime-version": "master", "sdk": "org.gnome.Sdk", "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable", "org.freedesktop.Sdk.Extension.llvm20" ], "command": "bustle", "finish-args": [ "--device=dri", "--env=G_MESSAGES_DEBUG=none", "--env=RUST_BACKTRACE=1", "--env=RUST_LOG=bustle=debug", "--share=ipc", "--socket=fallback-x11", "--socket=session-bus", "--socket=wayland" ], "build-options": { "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin", "build-args": [ "--share=network" ], "env": { "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER": "clang", "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold", "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER": "clang", "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold" }, "test-args": [ "--socket=x11", "--share=network" ] }, "modules": [ { "name": "bustle", "buildsystem": "meson", "run-tests": true, "config-opts": [ "-Dprofile=development" ], "sources": [ { "type": "dir", "path": "../" } ] } ] } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/bustle.doap000066400000000000000000000056741506133026500227020ustar00rootroot00000000000000 Bustle Visualize D-Bus activity Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, method calls and their corresponding returns, with time stamps for each individual event and the duration of each method call. This can help you check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based application is not performing as well as you like. It also provides statistics like signal frequencies and average method call times. Rust Maximiliano Sandoval msandova msandova Bilal Elmoussaoui bilelmoussaoui bilelmoussaoui Dave Patrick Caberto SeaDve SeaDve Will Thompson wjt wjt bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/000077500000000000000000000000001506133026500214345ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/icons/000077500000000000000000000000001506133026500225475ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/icons/meson.build000066400000000000000000000004241506133026500247110ustar00rootroot00000000000000install_data( '@0@.svg'.format(application_id), install_dir: iconsdir / 'hicolor' / 'scalable' / 'apps' ) install_data( '@0@-symbolic.svg'.format(base_id), install_dir: iconsdir / 'hicolor' / 'symbolic' / 'apps', rename: '@0@-symbolic.svg'.format(application_id) ) org.freedesktop.Bustle-symbolic.svg000066400000000000000000000045261506133026500313750ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/icons bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/icons/org.freedesktop.Bustle.Devel.svg000066400000000000000000000464441506133026500307000ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/icons/org.freedesktop.Bustle.svg000066400000000000000000000214041506133026500276270ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/meson.build000066400000000000000000000041461506133026500236030ustar00rootroot00000000000000subdir('icons') subdir('resources') # Desktop file desktop_conf = configuration_data() desktop_conf.set('icon', application_id) desktop_file = i18n.merge_file( type: 'desktop', input: configure_file( input: '@0@.desktop.in.in'.format(base_id), output: '@BASENAME@', configuration: desktop_conf ), output: '@0@.desktop'.format(application_id), po_dir: podir, install: true, install_dir: datadir / 'applications' ) # Validate Desktop file if desktop_file_validate.found() test( 'validate-desktop', desktop_file_validate, args: [ desktop_file.full_path() ], depends: desktop_file, ) endif # Appdata appdata_conf = configuration_data() appdata_conf.set('app-id', application_id) appdata_conf.set('gettext-package', gettext_package) appdata_file = i18n.merge_file( input: configure_file( input: '@0@.metainfo.xml.in.in'.format(base_id), output: '@BASENAME@', configuration: appdata_conf ), output: '@0@.metainfo.xml'.format(application_id), po_dir: podir, install: true, install_dir: datadir / 'metainfo' ) # Validate Appdata if appstreamcli.found() test( 'validate-appdata', appstreamcli, args: [ 'validate', '--no-net', '--explain', appdata_file ], depends: appdata_file, ) endif # GSchema gschema_conf = configuration_data() gschema_conf.set('app-id', application_id) gschema_conf.set('gettext-package', gettext_package) configure_file( input: '@0@.gschema.xml.in'.format(base_id), output: '@0@.gschema.xml'.format(application_id), configuration: gschema_conf, install: true, install_dir: datadir / 'glib-2.0' / 'schemas' ) # Validate GSchema if glib_compile_schemas.found() test( 'validate-gschema', glib_compile_schemas, args: [ '--strict', '--dry-run', meson.current_build_dir() ], ) endif # D-Bus service service_conf = configuration_data() service_conf.set('app-id', application_id) service_conf.set('bindir', bindir) configure_file( input: '@0@.service.in'.format(base_id), output: '@0@.service'.format(application_id), configuration: service_conf, install_dir: datadir / 'dbus-1' / 'services' ) bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/org.freedesktop.Bustle.desktop.in.in000066400000000000000000000013201506133026500303730ustar00rootroot00000000000000[Desktop Entry] Name=Bustle Comment=Visualize D-Bus activity Type=Application Exec=bustle %F Terminal=false Categories=GTK;Development;Debugger;Profiling; # Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! Keywords=debug;profile;d-bus;dbus;sequence;monitor; # Translators: Do NOT translate or transliterate this text (this is an icon file name)! Icon=@icon@ DBusActivatable=true StartupNotify=true MimeType=application/vnd.tcpdump.pcap; Actions=new-window; [Desktop Action new-window] Exec=bustle --new-window Name=New Window # Translators: Do NOT translate or transliterate this text (this is an icon file name)! Icon=window-new bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/org.freedesktop.Bustle.gschema.xml.in000066400000000000000000000010271506133026500305270ustar00rootroot00000000000000 900 Window width 600 Window height false Window maximized state org.freedesktop.Bustle.metainfo.xml.in.in000066400000000000000000000170371506133026500312600ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data @app-id@ @app-id@.desktop CC-BY-SA-3.0 Bustle Visualize D-Bus activity

Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, method calls and their corresponding returns, with time stamps for each individual event and the duration of each method call. This can help you check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based application is not performing as well as you like. It also provides statistics like signal frequencies and average method call times.

LGPL-2.1-or-later https://gitlab.gnome.org/World/bustle/raw/main/data/screenshots/bustle-diagram.png Explore sequence diagrams of D-Bus activity https://gitlab.gnome.org/World/bustle/raw/main/data/screenshots/bustle-statistics.png See statistics summarizing the log https://gitlab.gnome.org/World/bustle/raw/main/data/screenshots/bustle-welcome.png Relax with this soothing greyscale welcome page https://apps.gnome.org/Bustle/ https://gitlab.gnome.org/World/bustle/issues https://welcome.gnome.org/app/Bustle/ https://l10n.gnome.org/module/bustle/ https://gitlab.gnome.org/World/bustle https://matrix.to/#/#bustle:gnome.org will_at_willthompson.co.uk bustle bustle.desktop application/vnd.tcpdump.pcap

Minor release.

  • Use newer features from libadwaita
  • Various bugfixes

Minor release.

  • Various bugfixes
  • Updated translations
  • Added "Copy as busctl" to copy commands
  • Add tag for secret service related messages
  • Fix open in new window

Minor release.

  • Various bugfixes
  • Updated translations

Minor release.

  • Use newer widgets from libadwaita 1.6
  • Use dbus-monitor for recording
  • Updated translations

Minor bugfix release.

  • metainfo: Add branding color

Translation updates:

  • Ukrainian (Yuri Chornoivan)

Minor bugfix release. Added some translations and missing links.

Bustle has been rewritten from scratch in Rust, in the process it was ported to GTK 4 and libadwaita.

  • The filtering and statistic views have been improved
  • Adaptive design
  • Improved performance

Bustle has a new icon, kindly provided by Tobias Bernard.

Closing a window without saving a recorded log no longer prompts for confirmation. Anecdotally, most users just want to record and read logs, not save them.

Bustle now uses GLib's implementation of the D-Bus wire protocol throughout. The only user-facing consequence is that message bodies are now pretty-printed in the GVariant text format.

Since Bustle no longer depends on any GPL libraries, the project license has been simplified to plain LGPL 2.1 or later.

As well as being able to filter out messages involving certain services, you can now also filter messages to only show certain services.

In the details for an error reply, the error name is now shown, and the error message is formatted more legibly.

The default file extension for log files is now ‘.pcap’, reflecting what they actually are.

Bustle now handles the application/vnd.tcpdump.pcap MIME type, which in practice means that your file manager will offer to open pcap files with Bustle.

You can now explore messages while they're being recorded. Filtering, statistics and exporting are still only available once you stop recording.

The raw sender and destination for each message is now shown in the details pane.

Bytestrings with embedded NULs which are otherwise ASCII are now shown as ASCII strings.

It's now possible to monitor the system bus (from the user interface and with the bustle-pcap command-line tool), with no need to reconfigure the system bus. It's also possible to monitor an arbitrary bus by address.

Bustle now requires that dbus-monitor (≥ 1.9.10) and pkexec are installed on your system.

keyboard pointing touch 360 The Bustle developers The Bustle developers #99c1f1 #613583
bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/org.freedesktop.Bustle.service.in000066400000000000000000000001121506133026500277530ustar00rootroot00000000000000[D-BUS Service] Name=@app-id@ Exec=@bindir@/bustle --gapplication-service bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/000077500000000000000000000000001506133026500234465ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/000077500000000000000000000000001506133026500245615ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/000077500000000000000000000000001506133026500263275ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/actions/000077500000000000000000000000001506133026500277675ustar00rootroot00000000000000expander-arrow-symbolic.svg000066400000000000000000000003521506133026500352060ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/actionsbustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status/000077500000000000000000000000001506133026500276525ustar00rootroot00000000000000dock-right-symbolic-rtl.svg000066400000000000000000000042661506133026500347750ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status dock-right-symbolic.svg000066400000000000000000000010501506133026500341620ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status funnel-symbolic.svg000066400000000000000000000035121506133026500334230ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status question-round-outline-symbolic.svg000066400000000000000000000024721506133026500366110ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status statistics-durations-symbolic.svg000066400000000000000000000023741506133026500363410ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status statistics-frequencies-symbolic.svg000066400000000000000000000024711506133026500366400ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status statistics-sizes-symbolic.svg000066400000000000000000000015051506133026500354610ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/icons/scalable/status bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/meson.build000066400000000000000000000003161506133026500256100ustar00rootroot00000000000000# Resources resources = gnome.compile_resources( 'resources', 'resources.gresource.xml', gresource_bundle: true, source_dir: meson.current_build_dir(), install: true, install_dir: pkgdatadir, ) bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/resources.gresource.xml000066400000000000000000000046531506133026500302070ustar00rootroot00000000000000 icons/scalable/actions/expander-arrow-symbolic.svg icons/scalable/status/dock-right-symbolic-rtl.svg icons/scalable/status/dock-right-symbolic.svg icons/scalable/status/funnel-symbolic.svg icons/scalable/status/statistics-frequencies-symbolic.svg icons/scalable/status/statistics-durations-symbolic.svg icons/scalable/status/statistics-sizes-symbolic.svg icons/scalable/status/question-round-outline-symbolic.svg ui/shortcuts.ui ui/address_dialog.ui ui/details_view.ui ui/diagram.ui ui/diagram_header.ui ui/diagram_row.ui ui/diagram_row_tag.ui ui/diagram_view.ui ui/durations_page.ui ui/filter_pane.ui ui/filter_pane_bus_name_row.ui ui/filter_pane_message_tag_row.ui ui/frequencies_page.ui ui/sizes_page.ui ui/statistics.ui ui/window.ui style.css bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/style.css000066400000000000000000000017071506133026500253250ustar00rootroot00000000000000/* ColorWidget */ colorwidget { border-radius: 50%; } /* DetailsView */ .arguments-view textview, .arguments-view textview text { background: none; } /* DiagramHeader */ .diagram-header-arrow { transition: -gtk-icon-transform 200ms ease-out; } .diagram-header-arrow:dir(ltr) { -gtk-icon-transform: rotate(0.5turn); } .diagram-header-arrow:dir(rtl) { -gtk-icon-transform: rotate(-0.5turn); } .diagram-header-arrow.expanded { -gtk-icon-transform: rotate(0turn); } /* FilterPane */ .filter-pane-row { padding: 6px 12px; } /* ProgressCell */ progress-cell progress { background: var(--accent-bg-color); border-radius: 3px; } progress-cell { min-height: 18px; min-width: 100px; } progress-cell trough { background: color-mix(in srgb, var(--border-opacity) 50%, transparent); border-radius: 3px; } progress-cell label { font-size: 0.9em; padding: 1px 3px; } progress-cell label.in-progress { color: var(--accent-fg-color); } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/000077500000000000000000000000001506133026500240635ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/address_dialog.ui000066400000000000000000000023211506133026500273640ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/details_view.ui000066400000000000000000000225601506133026500271060ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/diagram.ui000066400000000000000000000012211506133026500260220ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/diagram_header.ui000066400000000000000000000020121506133026500273310ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/diagram_row.ui000066400000000000000000000026721506133026500267240ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/diagram_row_tag.ui000066400000000000000000000011631506133026500275510ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/diagram_view.ui000066400000000000000000000021441506133026500270610ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/durations_page.ui000066400000000000000000000115071506133026500274320ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/filter_pane.ui000066400000000000000000000061551506133026500267210ustar00rootroot00000000000000
Select All Tags filter-pane.select-all-message-tags Unselect All Tags filter-pane.unselect-all-message-tags
Select All Names filter-pane.select-all-bus-names Unselect All Names filter-pane.unselect-all-bus-names
bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/filter_pane_bus_name_row.ui000066400000000000000000000032711506133026500314550ustar00rootroot00000000000000 filter_pane_message_tag_row.ui000066400000000000000000000023731506133026500320660ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/frequencies_page.ui000066400000000000000000000074431506133026500277370ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/shortcuts.ui000066400000000000000000000075161506133026500264710ustar00rootroot00000000000000 General Show Shortcuts app.shortcuts New Window app.new-window Close Window window.close Quit app.quit Recording Record Session Bus win.record-session-bus Record System Bus win.record-system-bus Record Address win.record-address Open Log File win.open-log Diagram Save as PCAP File win.save Save as DOT Graph File win.save-dot Filter Services win.filter-services Statistics win.statistics bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/sizes_page.ui000066400000000000000000000127561506133026500265660ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/statistics.ui000066400000000000000000000055751506133026500266300ustar00rootroot00000000000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/resources/ui/window.ui000066400000000000000000000373711506133026500257440ustar00rootroot00000000000000
New _Window app.new-window _Open win.open-log action-disabled Open Pair of Logs win.open-pair-logs action-disabled
Save As…
PCAP win.save DOT Graph win.save-dot
_Statistics win.statistics action-disabled
_Keyboard Shortcuts app.shortcuts _About Bustle win.about
Record Session Bus win.record-session-bus Record System Bus win.record-system-bus Record Address… win.record-address
bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/screenshots/000077500000000000000000000000001506133026500237745ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/screenshots/bustle-diagram.png000066400000000000000000002673741506133026500274250ustar00rootroot00000000000000PNG  IHDR:c IDATxy\gfaMpA4sMs ܷRRsjv]3ŬiqH7J3EQKQSrCRUMf43 30|̼y{gy U`@%6%""""@QLMv""""""ҏAJMl""""""a(ͪ~DDDDDDeʆ>ٲ*ƫB HDDDDDTU ^ie6"""""ʪRP+Ue&Wjau""""""2-,! 5T"""""" :W0iͺ \>Qeur 1`ZB+IDDDDDDEV(B-U_lKW^hi*z-sUf% ײem-|V.Zkr]p+{E իWoO<>E\=x+kOʦa`?|Cz]l67xk>rYoY/H:u%DDDDDDd3NNNpW ]{t]!:֫3LwYl͐}lUXMDDDDDd]CFFBՠy zNrBek)Ni[L$kzcYps{G2U'""""""C9r#4.tn]Oߜ`liTeU9?[߽4'Z$J#lHmk<&+F v.),,LoAѬ)뚀Ms1lf蹭| gpeoL"vČBܜU<-o(z Ū_b4$fwȆՉ쀢NQsNTc#cYy.6](,z՗uz+{Wٹܜ[&Ti+5ʻDDDDDdCsqqQ|ɠkڽ63빝5 o?oD"i1 tc9 իWGaŊhժPXX={ѣ{.\\\ЩS'L8?ƍXv-n޼Zj8""* lٲj|\l\$%% 5j@jj*A5lT h" $$些r^z ͛77L]tYٻܻwot &LŋkBCvv6iӦ!44-[Dhh(?pUtnC۵3 K9+mݺqqqhѢQeRo\BNa()ԶOƦ]}t]3k܂{yoS10WQR֭CddDQFX|e-<<{1`VQstt͛71{l,ZH߯r[4k6Zڵk3޽w(UVhԨϟ_lq^:.O>YnKǎ~<~9ʕ+{`Ȑ!6lΝ;T5b܆҆mg%} _EC}ԬYaaax饗} a|"]V} T_U%2LMKΆX,DlP!zíKK1KC C}'_iR|}* M_,t}Ma,ؘc۶m:ݼy'Nĺu-O6 <0vaƌVm?1yd<aaahժj/6mk׮!:::uBzz:?WWWth8p9 S0*Pr9Qn]E1&&Fu=RwZ㰳CΝ۔gg(HLjgrsUgΜ}<۷G֭U)'3|"]V'==[n@ P͋aM7WJ=޺J31]RV1?y+29=Z;0{M:x-K|7h֬j}FF&N;wgϞǔ巙B ,K/aѢE QKLLĤITxꏊ.]K.8q"LYf_ObԩH$F:th^t III9s:;5|ׯ;cyZj5jԩS0`Zh?h׮|||HII1xX۵5 MU6l7oӧOC 7h@#G"88oƄ ؿ?R)\h6~m'"ۥoEuZ… U ظq#iBrw@ Go RȫFi1emD"1j(<|P~^^^XjU۷ok}˸w<==U_V^󸺺bӦMظq#.\(b̘15j D"<==gWVMTƍٳ]6&N&Mcʕ<mڴ1c?ܹ3TęZ>&ߊ.2cZ=%a,xwM4yTa>{ C\\?nRLfLVn5CdoR5e]ک^ Y]j0ńgyi^ѽڹ`RW|}: O=C5cP58;X/>E݄\л>"Nbg=>ض{t: g:kğyH}R~͝TCb/p/WCnOh\]1 ӆ;lE9LǜiaرÇK|nsݺu Ǐ96l@ƍˬ>DDEǎigo[|"SV{ǖqYx)Rƍý{P~}DDD>3ܾ}u7|XX < P*W{Ux@n,[%s+bl\ T|/z[9R9:``O_|0WuL ;!po߆/-ZTb;7oeԚ;g|#F@G~7ӿ/Nn  hᄤB ۔XqakNO(+Яfm]p%IsJR ):nڴaΛN ^wyk߾=>c|WuN:c҄k 6m޽ Q.]1GIʶn(|[*HI 0<ˎ=3gwwٵq~vmtkX3VMb;57Pԋ.1(m;vhs}/J  /~?&N=_\mÇk,6jBDDDDdX{{{2Rr*bϷXp(3~cyءm#y½|yq'2tjP[hHk; T3h.ۉ~y+ ٓ`&"em|"lr@`:##ӧOGBBQ>}:""" >WW!M]袞⏺B(B.#GBOLČ!:Nޣm^vTmkIx,/xߒ=jk۹,l:Uln֬Yԭ[5kV&|[ıyw;)^NWrkN2Qs"e)bѢPCMjryv>n=(@Ǡj()!vW ߎF|z!z4VRNDDDDDX,68۷Ez#""0k,ܾ}hd]( kO>òGo"")>w FuArf!nf | /9``K'$>.Ë20:vRU>U2ǘ;^GA3~'=Mx{{#==qqqY̍P49Qydظq#&NXU!"+^zg}'$$(mi5Uo_ޤ-$$lo6eg~ +DsD_!Cյp~ N:@˗/͛Ɂ3z OOO"::FϞ=!HrlNE[і7dee!??ߢe:88E1oًWj봗6}˫wI&L8tQ[jFbZ"MgϞŝ;w1/&&Onnn:קڵ+<==|TVwnYH$j"6TB(EH b ݥ 4K[۔[͚5q}=)))P(D͚5T닧O">>@QJ1ٳgP(ԭ[ o]ُuQsѣ>>f=s[n8v888spssCvv6=gϞL5899ܹsΆL&Cpp0q}[Zj]6J~MDDTXkrumW u,#T;Nx{^P(i@o3(j,;;ǻk62GDDA;O_(~P[:et]lcߑaZ@.b,g\Vͪ}ucε5=&GoRNf"X]""ܹs׮]PtwNLFƱaDDDDDDT8윈,ᛈᛈJ>j(,,BBY@ @ ]U3|W^2ᛈ(÷H$*杖*¢ DDDDDD'4^*VUcU2|U'"""""W%wU@DDDDDDݿDDDDDDDVBRRكSRRw^$''q͈ӧOrJtŶ",, !!!ѣf̘Go̘1}E.]0qDܺu/fk[}-4ߤ)UMv=d2н{wԩSBrIII_PZ5*Qٳg6n܈lDFFѣG:t(nݺcǎA&aҥ̄=F ڵ 3f}*l)moet3]"Ѷm[9sǏGP~}̙3pttDv]U%"""*38p j׮Egȑ7nj911׮]imڴQmKKKÑ#G ɪ~Li[}-ߤ)Z1|  Qn]tGŹs琐D"t5kdmDDDTݻwSLѣ~7;88h,rh|2nܸh3ʆ@sV>l7߮U÷PZj_~طoO`pss+ddd ##cO:}I&(///"JӶcove𭃻;|M\z͚5c&""*q_ Ex5={oݻ}v4i:ud+>lsY,]qs=j֬^zf͚]"""2'3Q*߿}ݱhѢbCB!0p@ECT*sVyYXwb&"""" u놭[OJJ >l7ÜvݹsgŬp9ippp\]]M'::>\RϺud>} Xt)5jdE涭>lﷻ[QӮ...eX3ak@mUcY}qeū݋Wf\ Y};O_(~P[:e{u֙lvNDDDDDDde DDDDDDDVV%NNN\.7#Y)WFU2|שS@DDDTi$&&֭[5eLR5r rw^###BmIqеkW 4sEVVRRRH>$e0|QE@@<<<*Tٖgff&MP>={X, """s"*ѣG+ےp DDDDU{Lܽ{hݺ5ʕ+;v,v#F~p4hM8y$ 7|3gDpp0.;' ŦMsܹM6aXfM9Ԏl˗++rss1m4x"Zl 9r$BCC?#R)O_~r/Yc fo !%%F<ᅬ `Ϟ=x"r9v܉Ç#// 1x`8q;v-[+ 000gCjj*\|""""] Tj}cnji&ܻw*ZE߾}B#^T*_,-ؾ$ @PT'rjYc]zu͛ѸqcT/O>[.ƏTUV>IUO}姤N""srDEEa֭^:6lPlL͛77[oD6Mc6Szz:,XDԩSaaaQFyW܄;P=QQQضm7xƍ\.G믿|NcJr<޽{VZڴiH3ڴibǷl4h-о['}쌥K⣏>Bnn.$ 5~ -Z=;P^{Dׯ_Bno'''e΢NDPoF55Oqu@AAիz*:%K(6Ӵi;`߾}ҥ >R9wΡlڴ=7oĶm`ʕbkh֬^zX`rssl2( ,[Li>}:ҰyfhBoن5:/vvv:u*V\7nD"T*Ŏ;n:HR,Z 4(QիWǐ!C#G[&"2D.:t(͛$aܹze2*xתU Ν;sN̟?jµkgA&A9k@mUcY}qeū݋Wf\ʵkװrJ9p@,^AAA%)Bw) ?͛ +=Ӝ9sc["-_{Att4͛gl} A6K%Ø1cЫW//ߏ/j_ $&&֭ ;̙3ѯ_2Lze޾d ^/~j?e-CmB:]n6;7Ý;wаaCuM4ABBFٳ'q-cܸq8tܹ///,X>>>d/qD"t|AY_Z&:YfXf [oMc{nn.>szNrLL VZ\+={\*?#Fsؿ?B!rssqobXg-Oh""K)IVR2qD<<<0i$СC DD/0|!##}nnn:ϛ?>>>ضm̙H`ٲeo駟ܹsHHHP=W9YTEp/cزe ֭[={bԨQSjqơN:b lذ=ŋ_V¶m*̗V%Ǐ#//:Nl}'KL0׮]Xya޼y&Ѯ];ۂ|O^dd9lg뫪Oja6ݻ.;;[緾>>>.] **JՃ۹sgԯ_IIIG׮]U1Uu5ҥKC|w0a;;;H$U֭D~O>ғU6'Oɓ-/)DDdo3O\vmݻN®]m6\r~N'O"%%vrrB֭l2}""""6l(ڵk8w Q6k 7nTQlFUYвeK_~P,WWWK%.3''yyyѣ̙ vjc( 111P(uiڴ)q%E>@ѷvUODDT Bؽ{7Ν ???հCzӧ`ڵ(=##C5r_""b6@ W_}{bРAT q.x̚5 .f͚ӧٖ/鱏?ʕ+ѯ_?ont,'''cÆ ;v,L7n@(o߾7nrrrTﳲJ\'n޼i @~RBC~0h ߿ߢ֣GЧOR/J %KcZFrr2BBBU2.]?{F= %%`K'** xAyW(I A&Mp}7ǎCnn.rssqQLM6E߾}ʈlvX~}Bl޾T͚5S nڴ)֮]kZY۶m1~x76~xmV^{ mm7nsx'|=U|k֬)SЦMY֬YA憰-[1zhرիWWyfxxx5j5kfɒ!!!SNg +6yfIu;vӧo>|LbY?>6lA(''?cѢEhѢ爏',Yu*gޫW/Ï?oH$Bxx8>3\v aaa&jڴ),YHT.CDdT֬Y{رc&ߣG&* Ԯ]aXV+׿۷?,O͚5ѬY3=eo6bbb5|P(۷/aÆٳ+$bbb4N5e˖3X:u_|a*[n9"ׯׯ(z[o}2xiaR;tBCC? vVX'O3f̀?r9!88+fϞmŋXjQ~}$&&j +VΝ;9r$vZez.]ʕ+wwwΩjժ/ą  йsbć~O?׮]1rHaaa'$$?  1c 4iĤvԩmۦZ~ ._ }`֬Y퉉xWqmO>EXX.\]ve˖:g裏۷n.K˗~/mۦwg0gչud{̹s琐{b׮];vF='`ҤIh֬F&M` 3>?{g;v`„ 3gd2Im_F }Ю]D"Q=fC/\{k<)۷o`ǎ@ S~}ר߿?|||y^z:?\۷u~@ߐ|ڒ矣VZ0aFkȑ P;iӦ!(( ŋѲeK н{wԫWǏŋ\;w˃>W_}͚5+V׋/,o>kN5AM ̙}B"`޼yHMMEF全(CPg@₰0T*/C.c ̄Pu>su%""*O Ddu>rj(JXfj6c}=k{ý:Ϧ׭sfcZSѣ1aT.h>4//Tooojժ($qƪ}SRR{ew}#G_۱k.4j:ҭXY* /;w./^_]DureO_ݕH(6Qs"MҥKѨ[.tߤI\xQ+WTfR{QSέ_pA<sA\\ƣyƎZjauիWW=Xb IDATrDGG._ gggl L}ƍ0`x"6lPwww#;;^^A>9giM6Db 8;;kҿpBDFFbٲeG6m ggg,]}:BF={6>Cl޼cǎñb ߿K,1>BQQQXnݱpBm1vXUqVn:i߇H$X,͛ç~)S5jPt>u~/]ϡE:ϣa%<կU}()ի `Æ Xll2xxxzׯիW-R=/ĪU0uTԨQ!!!!BCCtR|'Xb#<< r-_^N.k]UTVΎ益,_{Att4g18y`; sٰo)BWrڏrjP[P{N:s=DdXC7(*߿B8rڷoMDD7٤'NH$B˖-ѯ_YYrr2?<:ujyWb8윈Jt۹lFUvΉᛈᛈᛈᛈᛈᛈᛈᛈݻȨPe[>Dpp0wQW"88;grr2BBB#22媷yLL ƍ]bРA;w.,P{ ]T*Epp'$$K,ANNίޮڢX|DDDT1|QE@@<<<*Tٖ]l^ڢ?>{ղ=d(%33ӦMP(|={B,Ţ+;bΜ9hԨۇbe[]r+ Q=zT-IjBdd$W_}"爉H$R-רQ[lP[l)kp QF+зo_Æ ٳg-VzÞo"*wEff&Zn r Ǝ޽EU1% Za:iy  WR3obz+-TJSJd^ To r dAs V19sޟ˙9 /}C  СCkTaHo؆jcGDDUX]-[nnnlٲ+W2d?ILLח˗LJvڱxb4M6KKKWWW Ĝ9s{.'N@V+ݻ7n"..ZMnnR*X-ZSVVV.ZTC_{󟒒 k=z4.\Pڵ#F֭[ ʊFw_Y`\rB&$$ホ[ P9B$oQgR3BBc&o!<6PHjG]<?~Js=͛7 ԔSbbbBPPW^UӇCAhh(`0vuibcc5jvvvDFF]2mٲ7|[[[-[ƅ ;v,ńpyѣp*jР>лwof͚ŨQطo%iptt$** KK˚Ϯ]㣏>*u۷/~~~dee'k֬`{TW7󟕕{gFa 8PCy|ԨQ'T۷%Ka{xxpUh4>|!C(mTM:LeDr!K:BR\e{fbRLaJ'ǏٙƍsA GGGLBrr2...L2/fΜIh4,ZHoohZ` Fƍ %##jˤ='d޼yL8'''222زe d4 7oo&MT)gIIRnݺ1d5k[[[̤˗/l租~f͚ѭ[7v@yر^zᡷƌ7 lڴhꦥ{(,,dƌtܹJ9CBB‚0^JIΝ_&Moc׮]ܺu<+޽{A̝;or9B%o'pZwRL w׉'ۯ {忛'|(JAjӛ]]Z]jTi[XX(ñϫtuYٴiSa3g-gϞ 012}wƍٻw/dÆ кukuݝ6m0a^j=/,,ToUA\ډϴwaҤIz_~?!33{֭[Wx~N6[!ğӤIaر |ܽ{1UԩSl߾]ݞ={3gP!FҥK1rH*!~] L<{VYZ'LJXvs=GDD?00ر(V\INN޽ N,5ޡ9KgO> '..˗?XGGGk..]ڵkINN666>K5ՅǏcff ~ꩧ?>DDDPVVFDDeڷo̙3y&9?S2bccҥKiР,_SNU)>z;׏իWsNe'N$==X R!Δ)S(**bƌ?>kkk|||سg{5ƍ:t3gk.FM.]‚6mĪU())a…<3PСgϦ gϞ 7Bu{4oJ".**wy j!Zf ,M6\|0⾡Q?BK\}MtkUzQ}w^k… mۖ`ݕY+x" .$**'O|r>3e#XhZw!&&FCs5a۷siBCCxhpuuզuwwgÆ QQQ3u.&ׯ_gƌUv>BBBuVx| [l!110u^ ?R]u#==6m˥K_qttѰaCy*w4  BVs9?~C _V6\;;;^BcX|9+M6|[Gfعs'*BK޽133PG.qGBleeENN\~˗/3o<Ο?O@@۷omAAӦMe˖_Cƍ`ݺu9rK.)T~泮dHƎС~)ѬZA1vXbѢE]f͚b ֯_ϤI EoꊝL4ӧ+9stRϧaÆʳUǏޞ۷ɚ5k dYl@ b_ƍ`۶mJFHg}=۶mcɒ%cU(Ut?\)**gϞm(Tm#EQ79}I*Eݸz*VRVBH绎EFFR\\̑#G +++.^X!]AA666XYY_| jN>]e' Zl @W ׏/(;e0` g:ubܸq72i$vAJJ 7nPͥERPPݻy&deeaggO<qrrRfֽ0baa=*֭[[nI˖-qtt$,,z FS%:t **,jozz::tI&&YIIIY9_fǎ>}T)ԩSk<6iBߓG}A]q]۷/ڵڵk8::_WHsYz-hڴ)SSS,--φTBulݺÇY|y4o-[CȠQF&㏙9s&m۶%))IYr[޹s۷oWg5kVo]w^ 4 ŘR4h/zgQ|.C{B!"<#[" !~Y@yرcdffҮ];\\\(,,|4i҄~f͚ѹsg6l@YY=а;wPTT !%%^;ҥK@y[&%%QVV=]t!--cǎþ޽Kjj*͚5ٙwr5N:O?)w=jK.;vT|Y6m(<<<طo?etѣGqeeB!B<^w-SPP9wk֬!88JڵkIKK{{{,XE˖-c޼yZ  1@hh(c4mڔ+ݏ4VZE^^M4a̙2Dg[g[τݺuh)--p033#<[nݚիWgeeLeJ]?"v =ҥr֕vЧ};}ڛӮ / /`pΝ}z OZ{6,0o߾Wﱆڣ6:Qt׭oݺ5hllltSL޾楰!44N;d/^ˋnݺqE֭2V||}z~4G!xoߒ>ݧ9/kY|8&LƍJ$|}}>|8AAAT(_׮]IHH`V)vm#GV)cu .O}q6m~z̙3L8#G̝`>)9 fϞ=J+W}v4  P;vLm믿mCЕcmN"//oooD|}}߿?\|{[nZ{222PDDD/..No,iiiʠA3gN戸|2iii899KII - WWW)++ĉjXb@ЪU+N_~YeBI[ԉQ=,9;y*7g@K']|ܹl۶ wwwt֍8^z%rss eL8teQy 6m[neqvvVӮ'}vNvǏ3tPϟϕ+W*?~<X؆PܦMΝ;ٸq#7oիzOaa!s!44Xiذ*--'11QYrΑW>|V#//ꫯVʡ&##ʐs@IjZyi4Ο?OPP=zP\p!ӦMGDEEaiiell,FΎH+. {f֬Y5}uVEGGV>`]Ϯ]㣏>C߾}#++p<==Yf eeeuҮB!"Ei׼gşmllnhms]եn{u屰e˖ߟxev~/8Yeh \[ 1{nn޼Inn.YYY=?NNN888TxP}|2EOOOZl#aaa^pii)g[3ǝ;wܜO?O?ͥKη59;w[NVVVV89sXh={0D_>}0aٹs'#Gh]6n޽{9x 6l &&F٧;~XXXN6m0aW^vB! ;BԻ:g%99Y%%%lٲ"<[g]֭Pvµk4k ggg޽˵k NVթS'~'222RuÃ}?ңG=zToXVh? CUw7n?#&L &&m۶ VXZZxbN8w}lj' N>>4mڔgyZ"88@4i5aaaW?bMUwڴiٳgqss;ə-oɉmmJxN:ppp?;;v,ƲvZ6lȳ>˸qPTذtRVXA@@-Z`С 333FM.]8q"6m"66!C7ooo*uM6QTT# .gqʝq!ZfBl׾|M9N]2oVE]عsXzuûkN!DJOO/#pH;?潀\FU~G;:*/j|W Ȱs!c͛Pݻw+cܹSgkxj5ׯ_B'o!Cs ڵk*v]ҖR} SNرe˖YB!ci 8qŮKr=z{1}t:uT㓒0555EDGGYyz< ڵ|MYB!c;B… ܾ}_| 0___;xyyC^#((°ʱ Ǝˋ2-[ ///زe +WdȐ! >~A/ߟ˗/W)gV_~%999K_{[nZ&77W6my⪭*))aѢEꊿ?eeeuR4quueРA̙3w<)))ʰѣGs%]1bnnnlݺʊFйw_Y`\rEic|||pssO? C!\u&5~($<&oaBcf{QT}:t(bmm? cW 66QFaggGdd$5*Ӗ-[x7eٲe\pcR\\Lxx8ϟ'((=zN^^ .Rξ}GVVxzzf Ƙ6mDEEaiiw>lϮ]㣏>: {f֬Y5}U8,{=4 ӧOgJ;F֖O>Boe߾},Y7cÃWF 2Di B;N:LeCr!M:BR\e{fbRLaJ'ǏٙƍsA GGGLBrr2...L2/fΜIh4,ZHoohZ` Fƍ %##jˤ='d޼yL8'''222زe d4 7oo&MT)'1c&!!M63zc<4k֌nݺѵkWW`[5o;vЫW/<<<[[[̤LPQXXȌ3ܹsz၅aaa\z:哢͝;/L4~[-Ǯ] ÇuyyyW(Ͻ{vlРs0{\!BI[< VÿuG3'N8t~W+<@$VZ璒 Ʈ.. Aeee5*S4];$?JgQiӦ &LիFc<(:kݻqFٰa111nq̙âEٳ' KPZZ>*U.g;0i$a[ZZү_?s=G֭P܎w xr!M:!11e˖ӻwonݺŊ+8s yyy8;;ҲeK233?>J-?~rrr'<<\quN_+^x֯_O~~>M6W^aZ\4  '..˗?XGGGk..]ڵkINN666>5ՅǏcff ~ꩧ?>DDDPVVFDDeڷo̙3y&9?S2bccҥKiР,_SNU)%%%lڴUVQRR… yg 2e EEE̘1Vƍ:t3gk.FM.]׏իWsNw;tٳ)((`ٳ7nTs߽{w7oJrr;FEE;(猝K!BT^o]]7J8w}־[z3.\m۶ŋ,\(N<ϔ#F`ѢEjՊwy叵 Cyׄ翷oӧaOa(:uj}wy;$$[njժ:'e 3[7!۠>Ԥ~B<|iMv~8{@)pO]gΫLwtU^Ym_kcm۶Fqbcc7ntرBg}K.믿ȁaÆ;Ui4 Zܹs3~xvMjj*̟?-[RZZҥK9z(׏ɓ'mCgn Ǝڵ++W} 2++ Xb8;;3k,޽KXX/_#G2zh4 oj2k,^z%Μ9CDDذl2x ;w.gΜĄٳghoQQ;y$мys>C _fȐ!@(;;;^BcX|9+M6UQ5zv܉J{һwo6Q6mǵB!;|ױӧ5z ;;a+++rrr077\|yqyؾ}{?p 6m-[dg7nLDD֭ȑ#\tIYG;P]T>С~)ѬZA1vXbѢE]f͚b ֯_ϤIsrrx7quuΎt&MӕNƜ9sXt)ӰaCecooۉd͚5ZZZJrr2P%|Xz5|qF<<<ضm>|pyyygaoo϶mXd ݏ+W~ٳ'cJ(EԇڶZ!ģӧNR)NqqCi%\tW3w#G VVV\xBlll"55/j5>}ZGB+w_|ߝ|2 0@SN,^7nqF&MĎ;HIIƍ:hSSS ؽ{77o$77,x'xW?#NNN,úF,,,W_eݺuzw-<==iٲ%SOh䗒BDEEZVMOOC4iD=Y9_fǎ>}T)ԩSk<6i줭B!|sssKvv|Ҝ={z +++6mFKKK!~oݺ5[nİ~z/_^G?w5۲eK8t4jԈ_|dvPޞ13gΤm۶$%%)Tn;wpj6 ~f*ɸq۷/GLJM3`bVZL`` M4ښz֬Y3BBB={ymڴٳ=OLÆ qrrR&kԨZ0n}mf=O]Qbff[og埕Ř1c2p5V\ə3g(,,dذaM{i?K֭y뭷xjgII Gh+WԩS}0`Ξ=֭[;w.k׮uڪ\Tԩ'O&''7oV{]B o@F 뎆jXV -љ3gpvvu@Ŷ-GGG FRRRuU*jNbi.׶m[˅ *tYt)={dҥits4hтh*﯍v憛W\ѣ*ѣܻwӧhGCqBGMo-Ӿ!}ڛ>ɓ'?~<^^^q%|˖-Ã#G̘1cx7 Wo\CCgϞo2/Cuquu%$$ooo|}}IHH 00Ç3anܸIJJחÇDAAAuڕ6oLnnnkז9rd2Vې7++iӦ鉻;ׯ7z~Μ9ĉ9r$~~~ SYQQ̟< `٣Yr%۷oG0`ecǔFXX60T]8::Vv)6NLLח˗yuqqqj+##ZMDD^^^ < ݟ? 49sh˗/Ѻh"puuߟ2N8Z&)) +V ZdUFqo !tEÂ;37oO{sf;bڵΝ˶mpww'88XٟNn݈㥗^"77P,X@LL ...ʄsKWPP?[lۛiӦuVLHHl߾ggge ::| ۷oNji׭?~8Ce\rB< mڴ)ܹ7yf^2gBCC%<< R{*ayxx~%Ç8p`< Q~jm~g222 9Nhkk ?VFѣGey 2m4|$@TT/P2j(숌$;;bлwof͚ŨQطo[n5_tt4jooo eڵ ???>#||| νa:???ӓ5kPVVV'*B<,2\ԙv}[y[FZ6U]: 쀊WW Zl @Wm׏/Bchu]֭oРkCykjjJAAw͛撕䄃@go 'Se˖8::SO=FsVVzz::tI&VuHII10ֶˡUXXȼy={v͛7'7srr͍;rAe>d4 7ooªݺuk׮Wfذa4nܘP222*,w/.kkklmm'332>ڵ_~|˴oh]czwØ1c&!!M635nW!Q̤x IDAT[^ZZZ j*^]ytUަ:T^e2[ý6*i(ٳg㏙9s&m۶%)){r|~ܹ۷[>P> *t*aT* Dbb"N^uk[}2{l>sܹsg͉O><<\tI|[[[ӹsg)$neee:̙3Eѳg Cӧ&L ==;w2rHeƍݻabbb}iӆ &p:mW!ɰs!Dnʞ}Ym^RR–-[(**2.c,uuhl .m'\yJfpvvݻ\vdU:u⧟~"##@y޻.Uw<<<طo?#=z{ѣG6me0tPen{7oޜq?2abbbضm111ʰkZ%/ĉ|w8q(ɓPRR@f,1,:loԨ*sΑ_%ɓ)++cŊFrիluֱcǎ +u˻r `Æ J^xh*B-wխu^-AAAvvvJg5;;u/Ҷm[<Zg^)۷ [o....xxx0~x"##ϳfͪQfffzۗ#GCӦMyg >۪U+ I&X[[y/TuM6={7776h۶Ѷ}ꫯԩSl߾> ñc@ll,k׮aÆ<쳌7J K.eŊТE ?ϔ)SXx13f 22fU90~)V^Mv;{n޺uk<==ٶmt]o]ڷoϡC033ct'i&bcc2dzR JJJشiEEE8::pByBn&ovJw4*ߵ/YnU\\ZE;wիWW;Bԭt˜?ann J{:>kt^e:/wt2Ϻ{~vwՀ ;BZjUy՗yѱcG$^}w]:7o>'BQ_-xhN8=vZZݻwgРAW}IJJԴG{uS\\o-_!B&o!Cqn߾͋/?VX hժݛ FԩSٹs'O=K,СC$??{v܉]\x/gfΜŋKLL$**WұcGppp0=!!UVY;N޽{899r 1vXLMMi֬n"..8㱲2N0qD RKJJw%//m۶pB~lҥK[F}WT1RPPqd<ѣG;f|(B!<-Lɟ;oR_ﷸғ?=GT*{9n޼I@@L:zO?O> : BCCߟ'**Ν;WwyN^^ .4]7Smk.裏D9瓝Mxx8DEEaiiYm;i46lヒ5ҥKUPY^{5\¥Kh4psszO6J _k˖-ڲl2.\رc)..&<<h !BQȝoQ'/dWUkdAɎ0>'xǏLƍ9x 䄣#SL!99~mL_~III 3gΤCh4-Z7vƍqttgk׮dddTi&4 7oo&MwϽ{4hܹs ӼysvA^[lܹsaoo@f֭]vȑ#նquu婧b„|uABDZg36mfBH !i&0&fnFӆv k.}nt:9lyyk|?IJJE/&ҵkW"""_h֬tޝ7oL /kѷo_*V̙3?_Fc2B!O |ǖ=v=PlM?*-ZÇ+C*Vnݺ߹z X|dPf /?&&F|'Xf ;v`ݬ^u)8}4t`Kl1ӍI֭ ֥N:j~yquuhŒ_-P^B!xZȰG믿ξ}}7odƌ 4={Gq57߿?cƌܹ}j5,v=sܟ kT^_޹sHOOuxzzbiiɼyغu+)၅O>1wIj9|0FӋٳ$%%Ѯ];:vHnnޒ_ZbСٳjժq-Qf͚Ell,Y$&ҳgO>̏?H=M__+Bw)ܽ{#Fc*WwB Ö-[x饗f0ao6mSN|x{{i&E:uhժUQCxbAēq!',,,'<>I&gFjոs^"nذ___y*O_;uDV*;rŋ;w.IIIEf3Vvq 9uۗ\vٳg3~xaE%mrr2P* 4(`l޼8>egU:uHLL,2伴Ǝ˾}Vj&B! 'e///4h+Wpqqa۶mziN8[o"2S]N6l޽{YnVbѢE"TuʹEZoeed*WL6m&ƒ-}e*>,2ٞB!xz̞=wڳ8@|Ijj*ǎ :sƍ4hصk?3UV՛ޥq=ڵ+$''`e\Sٳ'vkŦWf;o޼9gΜQqw9p)))o\xtB!B.]O>řB!a_=`ԩCTTcUV%66d*ݻw+NNNz[hBNݼys4ͰߧˍYefFW (_֛=iӦDGGIWxyye~|-^>OO']RYx1\%Y%Kԯ_h/_^ד]5|v証Z3sL7nlQYB!ijN|Rq8ڶ)qzmk}۷oB˖-9tIOONsU>|{=jTu֭W69qS/(ٳg͚!ONZXb*:u J!xzȓo*- }nݿcu`xW eȑ#DDDpmj֬ɓqqqAoVٽ{7SNW^!))HS.]ֻ׭Cv7o\xggg퍦7T7CmUlwIhǎo<z:t(׿ԩ^^^TTI9fiiIff&۷oddd899ХKGc礤^I߿,&L...5$틣#qqqܸq|.^ȯÇ8q"5ʊ3f`aa,+//KKKe;((}RJBBBHMMA9 /PF ZlI-J&!B&o!D+>F!''G.)£^8/c'&&olcyFךWTxyyk.x CxXYYQBcĉX `eeU9?S(|v+V sߵkӧOgΜ9mV9?77WmWڦM1!q.666 xo'_Fg}uU;Wf'K !_ uݾ}2\Z۷IIIe˖O*r !dع/)>>~KKK<<}gq=%C\\\cqqqj^jBc+ xv>| ۜu&LI&1۷osuTRls1sL7no90suЁ:M6CHHY򶱱ёʕ+%?!B<}-(7k׮I[CƎK&MJǙ3ghݺ5ݺu`XZZEuK*k 4wݛ/f˻VZXl !#΅ٳܺu6mo{ѹsgٷo| L0={GϞ=2e0yK;<<___wNLLR`|}}ѣׯgՋG'!!:uD`` /^,R{3gsmxK`` Ν#66Z7od֭j2226ldΞ=e @=ذaZjT\h{o(̚5~q%j5[n >>???z_|Ql C!O| I";X{ko'tv!T*/ׯ_gܸqXZZ2zh,,,2e /_VtЁ>}JHHo40x` ==DuZ~=?pttd={wyΝ S2e ̝;;w0{"bȑ1w\ҥK7Ymٙhpqq!::;;;tW^%99wryΜ9éSu:u3uTO?aսh#4 cǎk׮J :ё ug~'͛Grr2ݻw7Xݛ˗/s4 {W^XXX(yރGƍ1Sq,\!B<]dع0b9E߸C6 }ݝ*U{n0annn0j(fԨQ|2qD5jFaΜ9M o߾TRRSS9sLuҞWbEfΜ?OHMMeh4h4|dggSj":t($>>kײb ݹ}Ѻ5kF-Q-[TtBdd$?LJw^,,,pttYfT*ƍ+O _k>vRSS aoo_zlݺ,ƏOӦMg[[[BCC|25 &E1c?<ÇVFO薱eڵ+ݻ7or|||믿Cbƌ&B!E:-ؕl#{>dW5wbŊ$$$PbEPTڵӧ3gڶmKΝѽh1Nƨj֖ի3!!-[ЪU+lllQTTPh] VG!99&M`mm,CVbEڴi-7n`ذaʐ4SY=jpt{pz8֪UˬmB!F 믿fذa D޽ˈ#رcGd*T111lٲ^zp x7شi:u?ۛM6)(ԩCVʵm"CC͋;vUV1w\nʢEXX-[0|-[FRRvpp $$/<9:tkkkeXt #++p 7LXÆ 8q"ׯ_gz> m*)1̟?+++ƍǢE8zhzڒڵkYd ̞=_|u5jٌ?SNCϡ#XB^z%*U/ ԱcGؼyal:6:+.CJsdy)<ljQvиqct͚5z\bccYre|5 ݺuCVsIlll`HXXk&//s,--ر##F0C3:E,^ ҫW/Uw<11HpwwgԩܿP.^Hnn. bȐ!h4|Mj5wfԩ+?~p222ppp`TP,f̘DZ`ڴi%Z8;;;;;9rn߾M͚5I_.!B<JE.]ԩqqq|\xO?-[<=6cǒ=ڵ ==jժYv׮]jՊի;331cPvmVZEpp0TRpV\ɤIؿ?ϟWNcn7jO6jԈ/+Vdu;#3iii̙3e˖QF "##YjÇWޣ}6?ɉ .0|pƎtȦO[.wޥRJY8;;qF"""XtѺ呔)ݻL2(ٶmAAAY޽{w){ꍐs|W8;;w1o<1bDzر#| Ptŋ̝;Ν;+!4i҄?k׮fΦMHNNڵkʺԪU KKK233پ};ׯ_'##4P+2#8;;׏ڵkBhh(G)/99F)ݛhPZ.\QFTZU!@{rʦMcѣK<4iB!(.666xyyѠA\ ۶mKs z-мyQxv:uذa{eݺuZE=F YEOkV)ٓڵk3n8Cjj*+WM6zAA>3&N+2?eݻǭ[-[;A!5j{[W>|7AF!''kkkT*ݺu#!!?rMe,H=BXޑ-[.r8gqdvn&;v (L6MB+77^{Mٶy?^yŦ#n޼֭[QdddsOU{Q FV@dddQ5~-@09sУG $??hΜ9C`` t֍ӧs}RSSQ|7*K%''@Ν2dgϞUe @=%0 Ӗf2^Qh6楹vB!)f1Ӗ] :3׬CC&v'0Z3fLJ hٲ%[nW^!##f͚źuP&U\LY~=$""Dd̘1lذݻLHH7n]YN… ٸq#NNNJUKnCӧaaa\tI/M@@111zu,IƘ*XիW'$$͛7f[._ldee1}tBBBaܹTTd{t믿,W SרwܹSo޽tU;w]t)6Ӥr(x=IV+FéS2e gf̘1@Hhbȑ1w\ҥKrq=ʰaðc̙?;weFɧ~F@:u*槟~8tЁ>}G}FaرtUBA|8::pB+P 8|>c2֎(mKsBSdع05L-]:wpp0u'''@ckkKڵԩqqqʰ;7([g]u뭬-_Ҽ1V|---d\~ prr2x}ܨ[.{)׏ڵkBhh(G)5RJ'… 4jԈUꐜl0b̙L65kp%ѣ7f|IIIh4[~GU/jԨA˖-iѢСC8p ]+VNժU􄆆ҽ{wիLj#8x\ΦMh׮zH\\7n ??_fԨQlݺ,ƏOӦM'88޽{ckkKhh(/_QF@;|r^}U6lh^XnGi6橩vB!1SB9SkCѵm(@꣫>mcSx×uhj (\|O8g}ĉquu%11 r(|}ݻǭ[-[=P0իqpp(pJ[n$$$>_z7//iӦ1p@7m֭[Gxx8`;Czx|Bov4%[f͚5رݻwzj֭[g^vb̙3m ^ m XO;tυ ؼy3 d C$q1P !Ȱs!D+nš5kFRRsssY~=&ә>1κnM[(yCAE ]2囒B5pww\rDRM4/T5ݻ7?;?p{Tl h4|'xxxЧOenk֬ɰayYn}֭SDj9|0@xG!99\7nW&&&T*/2{ʕlڴiӧOdЦMΞ=KRRڵcǎ撞n^WسgkJb…ڵm۶1#F ??H2Sq\2*'OrRǼ4N!0E| !\qkȔ)S͝jzz:+WM6Mgclugggn2nw=uimmm0_///ߏիW_4.s=GPP&LP(bcSċ%U5W'NG'9stt$((H7puu5VZ~ ..GqFɓwyuòe˨T͚5cذaT*?>7ZjѧOZjŨQ?~<ڒڵkŅٳg/kC^SkeeEZj̜9DW_}5C yѣGA&6jԈiӦ_3k,i޼yԩS~wߑD֭ ֫aÆFa,.}v8p`c^k'OƅBRYJ̗:*ۺiT:i?[]-''RE$C !B!-B!B1| !B!eL:B!B!DηB!BQƤ-B!B1| !B!eL:B!B!DηB!BQƤ-B!B1| !B!eL:B!B!DηB!BQƤ-B!B1| !B!eL:B!B!DηlذZMzzz}FI.]0{Yqqqj^ZsSSSQƚ^icҭ[7juOR9t &NH^ܹ3cƌziv̟?Te>uԺt}!..B!ijIW@8|0 4L "55?{e3gҸqc|}}͒6&pykz-5jY1;w`ooùvWf֬YDGG+i ?:tK.u{mllpttrŦ5B!ߋttڵ>s [[neRVYILLlicҩS'~wkZnZ6[9鉧})N:ӠAw^nu4VZXDi}}B"΅ٳܺu6mo{ѹsgٷo!| L0={GϞ=2epaݼ9wj6nh0ԩ\xxѣ_|^ ˜9sѣw^vv6 b37ouVj5Ʃ]v,^~ѯ_?a߅mx9s@֭ӧO$''@Ν2dgϞ5ڦl1v9rV 6d{Ξ=Ν;۷{(ųu֭&裏 ^_!B<;-&+*{8lY̑-9RTáCPTK\~qaiiѣ`ʔ)\|YI_СCCjj*!!!H||Ѽ9gggiڴiN:Ŕ)Sdܹܹsٳgݯ[ѣqRsvɖ-[9r$~)~~~zü #==sGBtt4vvvIѰzj{= Sޓ֍II3uTO?Ć HKK㣏>B0vXv7p.^h06x!>_~%ܻw5ڳgGYUnjS !"΅Y&!"se~/sGqߔ:-;_¡CpwwJ*޽,&L...5$<<<x5j=L8Fh3gT ͚5Eoڵh4[~GZ*III<|P)ʊ3f=f͚lڴvѻwox_'O35jԠe˖h0_>>IIIŤ$tH\\7n ??//5LXimm/ɓ'׿Edd$[_kOnn..Ç׫zѷo_TBHH>}u2O++b.Ce7o޼B!ijE:ⱝݿM~(9Ywp}o9J>|X>x(x PbE`*-ɳ-mzkkky=˗/Djޯ:-_ ֬YÎ;ؽ{7Wfݺu1777N>M||Kl1+ILtg׮]L>9sжm[:w @VVyyy(&cv MRhܸ1}%!!s/=Z@S Su(l!B|?,XIh߾=7o$22ǏsݙQQQԩSVZkEVUC͋~w-[ƦMVZJlOơCV֯_0'??pK5lؐ'ru/_p%󱲲bܸq,ZGݯ[~tt4.VVV&v{aĉlٲ!CмyszФI.\Qf:uDqruu/̞=ڵk?RLtбcGؼyԺQFL6Lf͚Ell,׮]3ڦt1rDDDW_aooOxx8NNNFSm۶le(_)|}Bl)E^q om4*ڏVK)ڢٳ仰s1{l9r-⫯R09sw^TT*UbСZrc=(W^eȑlڴ0F|`n޼ɒ%K̒… Y~= 5ᅨT ~ʣ=)(ޅ WM\>$Ʀu5|lvߺ{/ɰRruu5yuݻTT ǰLƏO`` -Z --9sl2jԨAdd$VbĈESǧGq%vIvv6m۶U^C0GO-&O[{Qqرb'|'''(~bMtW?yH"""a?{wU?pÀ(l^ 4#4/*jʴ]25dTR\5f.Jbn[l ?: <{339w^^^pRrrr=/Ř1ctmii,֡C}|)))ѱc^tqUzuצ{n_Nff&iii\x777eE_h4ѣ%IBB׮]Sgff* C=z#Gdȑ:dž:'S!&o#077ח&Mp\\\-ĠAJ/ .eW-ɉ(8 Xf -z+μ6i555-:))ٳg3a\]]ݻܺu7ojCR˾}xשV5kۻBypo^B!U;iܸr|TT}5H,n"//Ν;BBB2ڐ8?cJNNvڸsm\)͛7ĉJM%hР3fӓgrQ^-۷o,N!B!K|WR`` 999\zӧO|rj5*+WrY qvvf̙XZZ0|ONdd$ 6TÌ? 1={Hf„ FyNٮvn`qra뇝>,&&&ԯ_Z㱶֖iӦmj* #<*W6l7nܠ7n(Lk e q-))),YsΑ /@ppp51^333RvM8R>{W,--!CaqVƷ~˦M8{,u'B!RjL<)7V2cBTTPPuFX%Kзo_eCyKEƍeirG]ҵK2[믿s߶/ŋӱcG8}4;w^`ɒ% [5$UC\5Rc2\<۰60tuϭ[uUAyҒmےORUh ӛ7oj*uFhh(ojG~C-BǐL;.wnA:u4i...ZkA~~>\xpvvv+vȑ#Ҿ}{^*֒59qk׮L2Eo~zꅏNR{xxT8rܴiSMFJJ N:ł ޞSzuҢE 9^Ǔ֭[2dH>G.]ЦMPT|G޻Q_;uʽ~wիWk۷/K.~ښ CDD6l߿+Vpe5kFpp0*7x#G}vybř~Wmۆ& !"d- ©e{s3%.)״3v ݱw|QG%;;`-[3vBV+u3332e }l޼r=_0h4U_~Z4R;w.ξZh Tekʷhтj57o~nܸ?...Oe˖J_6/L2u뒑o:uꄭ-SNe޼y4lؐl,,,E^ >9 | !ݻԬYSyhs2*{T(ۗZ<^a֬YâE5˺_~J311̬4($fϞ̈́ puu%66wr-n޼mע+'Fy zʕ+IOO,]y_+rn}7o۷outڕ;vн{w%q(fkk Uxe()náCHOOgյBq !¸d5!yzzrQ{ϯ6j=<Եށ>>XYY1g9x :֭ݻ]VBa3}tFVcaa2%8==իW];z5kZKvZM9͉$++ kkk&LJ*5/[S~}vfϞ7?*h"##c̙T:%ra뇝>,&&&ԯ_Z㱶֖iӦannZUVΙΥK{.M6eΜ9:% -)ӗ~>u);1GGGviР=z 66ٳg3c }]+6nH݉ޞyرc[.zRծ];HKKsΕ!BBͲe˴N~x|󘚚9u1hޞ+.RjHjHo!xL\~]Y5|ݴkG$999|LN گB!L;B>[n/cڐO.]Xv-:u !B٣@t"??GGG '##:u0i$\\\h4[?0yd^~e∈ ??WWW.^ȺuNrr2͍Kj/66rssqwwgXYY~Q-ZD~~>vvv+צ ͛Ǒ#G055} >TNn޼IPP'N$11'N0x`vʔ)SW^p)T*&Mã\> 1i$ڷoO׮]Xt)u_~ݺuvݖtN>9Æ c$''3Wμ6iĸH!B*&oa0'(-G S֭ 8B(oj lYlڵ Z /^{ǘ1cPTdff2e>3ټy3111Z=z4+aaa899hƧ~ʕ+]6Y~i>h BBBJܹ:jѢ.\ ** RaܸqҢE ZhALL jƍ7G7n~'BBBزe qaqtޝ]v)l2X*>]=|prrr ^zYCZX`Wfĉdee̫B!ēF 6w(ضSxh.Rm'qv{ IDAT;l#PԪG^#((ׯ+?ΐ!Cݻ7C Uz=v uaJN=MrssUzzzrQ{jԨ666Z ..Nioٳ=z7}Gz~]ܺu<:wLHH 4hРT90[[[ߍHѰ~;+}qt֍}qڵkwevݕ/B!O] L0;;rwkWN~h޼9wa,XYf0~x/[n?/Zٳyػw/SNe۶m+0vVm;%KзoJ ̄?~-[*k׮MHH}g:wpp 88ZT}WD͚5 #<Ξ=5͛D֭;ڗOu?k^:*B1yDjoRb{o%ۨJWwo;wĵr9\]]QуmjmwyBCCYb,Z>Lo駟ҨQ#wη~y @ojıaÆ) DիW7nnٲeꫯӧOl2,X`4 :u~(?~<ԠY!*^QF8'jHy (ߚ?E%~_S5%}]TmJ;ߕt2EEE:u72tPi֬Yv\p777^~eBBB}'Ie2\!V9y$:'ᑐ2pbbb8q"g&-- Jޞ >"""8p/CTTJJ# mϗ_~ի5jT̟&*J߿?cƌ1@DB!ēl! E|94i҄+W®]JIJJbРA미s0h ~=8/SRAKwUN:Ii$!B񨄆ramۦܨiР멾`HOm }Xr%3gϟOTT}eϞ=J{L:={2j(FMӦM8ƳgϢV4hjT C֭QqiM*Xl~Rj0j(~J]4oͱcǤ4cb۶m 8{(_6EEElٲ:dԩdN7.ɓmۖ[n۷o>=^Cطo>>> AXx1J5?Qeٓ7nvѾ}Jl2lll]j'߿48 4`<ݻ(Jݺu+aaaS~M뇗f2Zd֯_OLL۷/?ͳ>[zo6̌~'N[!w'[nlܸS"CDȂkY,  R,Y+W`aaAHHnnntڕ={@Ϟ=ILLdҥܸq pe6mTϵkײfv4hP㉌8;;3sLllltn?qK,ƍԫWWZj矓իWyvss#++ Z믿N~~>˗/رc,X@o~uFΝIHH ;;`Kh0o<֭{ӽx߻wotϏN:Hnn.D9wƍUcoNJJ DEE1vXjժEvv6ƍ# ;ҭ[7իǷ~˂ 7oҿV^ͰaXxq\U)_͐!Ct/kvnw᫯'/flܸgy@oN!PPP̌TnܸATTÇWd„ DEEn:֯_ϝ;w8ʗ_~I=ضmc}g$]ӧOÇ3gG?w}3gp%ZnsKٙW_}Uo_*xVZE=cߟe˖Ѹqc#* kk s89bUV|rQF 1O?~z|M,--uk.xx޽2vttV9uW?I&K//믿2j(޽˝;wEѐĦMvfff+q֩SW0++ SSSG^ ?3 ̌˗/~zn߾l۶#G`ee_|Az ԔXΝ;G9s ?s:u"<<<8u[lΎD>\.JEZI!'o!D8qDŻ˵0`:))mۖRm}|rklݺU 6Z-))W^yEo_poѕh~m>SZjERR/;wիlOɻG:޽7n˗ 66VYa\W˗ӬY3,~ݹsgtu祗^bر <OOO;<۬Viժo&)))+Ǘ?]G^033+W"!!_qvvƆSn]&N7|g};'NS5ēJ*W_}Ezd֭ԭ[;;;ެ:uuboo=;.B#΅UVZbY׮]c˖-[@QqXؽ{2`oieW2rU[YY~^-?Tu{{{N8ƍ7o\pdjԨt֭K͚59~8QQQ̙3K.)5nFCHH;w$44_~?JU*$''+Kz Bpd-AAAر0k,OL.^#FuV ŋAܽ{ŋ+0Xikȑ}ZykԨG}[[[[ӥKz-FEÆ ѣG85 ϟqz)viiiL:U$88ul2~wooo:ĪUxiӦ ||ԪU sss%G}o߾:uEq.]J6m#%%Ei{Y pww/}ԯ_O>BpηwA~G&OCrׯ_'00͛7?pEEE 6>Ftx?BTԟ!yR[!W׮]L~ʹipvvvڴlْ-Zpiƌtԉgywy8\\\JDW,;w&<w}777RSSٴiFo~B!2-oDr U,UATq {.5j\i߰aCRN.ٷ.%`ժUʝq@Y7:;غXn| ?k׮eÆ >777bbbxW\<}%s/j :Dzz:>,l_/ң%pn}뢢"[2BGC_lmmY|I!dؿ?}ʶ7nӿu|kהG^#((ׯ+?ΐ!Cݻ7C Uz=v 1uUx?t^|EZn)seǎcee<[&&&̝;{'"|||bΜ9sAun/y={0uu9hӦ ۷TɯVZ1d~Gn $$$PPPP<͜9={`iݺu若[ndzw^vzecu/Bhػw/gڴic)B! +!;;Ç7PfRWN~ظq#_5?|yǏ믿ζmС~!pOZĉٱco?< Ohj~?vV3K,)%x4;FjՔ< 3f 77 PTTĂ t? ~:VUV:{{{͛cǎeѢE:<+޾]Ə?Ȅ ӳTÆ y,\?#FǸq8sLŋ 44zˉXڵkiiiڗQW !Fa߾}[L64hIXl٣O!ƚw~7)JmT%+~]cf͝;wUZJ9wjzA۶m;vvvsBbeeP}l߾JEnn.| m۶ZjIS$''ӯ_?#G%xj-dm`cƌ!..[[[ڴi@zz:j*Ɔ SV\Iڵ`͚5 >¹"$$> ggg6oܹs8O_>=K.ߓK/رc 29ٽ{{Xj˗5kfͪtwJ/) O\5$W2D6pbbb8q"g&-- Jޞ >"""8p/ýՁvЁgyT*۷ʕ+)))Wneǝ۴iCRRyyy9sggglmm+| \vPƍGBBmEsRW^I&NFۉO>QV~Z999ye3bcccȄB!6rI&\rvUMRR _])3h ~=8/T򵓓QQQ8p 6f-Zd+GwUK[nGΝ !!!(eBL_0ʱ֭ȑ#kmmm===9{,G=[~Jɑ#GHNN~#%%r/B0"J ߟ8ϟ?IIIܺu+W>}rJfΜ%&&&̟?(˞={ y{L:={2j(FMӦM8ƳgϢV4hj &R֭QIJJiӦi} eY&aaa,_FUHjj5o6ǎ"|¸g+_brh4t =zbY=z/=33:-VC9~8>>>8p;pB^t^zuqN:%_|]O !DՑi畴tRϟs:{wzϫR׎[hBN7NC1k*]nk0й~7iDYt~_xV^~XbEv-/:Ukɒ%5֭[?P*eɒ%WYkqREFF?3lsVZUM6g=sssG^ONf۷ov999,^>Q):t ::?f͚ݻKKB`ηx(5&% 7{{gddL˖-dIOOIxnݺЪ*cҥ k׮ջp\СCʶ<~G<͛<ӕmJMM7ޠ}(;vȋ/_H=͏%...J,iȐ!Cbaa355%''ݻwsu233IKKKKKSuLrrr?PW p\aIJ:jzMZ2e +7nLڵiٲ%-ZJKlٲ%ݻwgŊĹ011ᩘ/BT| !l}tFÝ;w֓-,,|zeU=66VּJח}믗#Oɒx&&&QzR}woff/?{zW˖/,{]%_͒%K o)/))ٳg3a\]]-7Ѕ{wwϟ|請ܠA+]g{QXXXLNOOgx{{Wzq}p QCKZ)՗9deeammԚ/9|~aggdz>LE1>>Q:>-ZĮ](((Ã?kkk+JsaܸqVM>}عs';̛7ƎKݺuիZϏp^:?< /T8.!BLׯ&%Viy]qůLs׫BͲe˴N~i4:uTjѴ1~xXAi QQ/^0sՐ}uH[ѲpV%O!&΅B!CiUC\5$UC !B!d-B!B B!B#B!Bad2B!B!LB!B![!B!02| !B!F&o!B!d-B!B B!B#B!Bad2ػ/E%LL $5ܮJVVZbBP("Rh$(T_.%JfE P@Yƹ })3l@HH?3,X\y___1j;J6lی=k׮GZ@g~Gܛ.BO&|jSt7.䯡osK7nu_CNBѣGQToߞW닱1'N222_~% '== !11QgӦMޞ(^xr:uJCHHO8k׮)}몳k.m7oÆ HW\\Lpp0ل2e¢>ppqq)}xzzŽ;ݻwƨB_~5KVcάYRB<8|W[7n;w䩧*uVZ 6M6m6ڷo_ɼӦMcРAlݺnݺ `2bĈ?x`Ν˳>K>}?LLLy&cƌQ>(uwecǎ% f͚˗/3uT֯_odeelٲjioѢEDGGĬY—󭩪ښHC*xjYbϟgۛWzi>b"yfffnM(xWY*xlƾ5iWU&QbO6mѣHMMUV9;;s9ҥ 9͛7]]ZMϞ=quuɓ1vXHMM֖`6lHQQ ,q/ +:| uu?c <<ݏ/k.x7UI\\pR2227oͫr{@B'yg\I"|W>}:_|XZZS۷ƍL2HNj/wp%W_uFӦMDRK{W/\@hh( 'N //SNaooUرc\rNʱcǔ_+cǎѲeKei^z͛:%8q"111$$$+O~R5nܘCr^U'OfXZZVSdB!BmfffӼys.]۷o/UĉϴlRY槟~ȑ#:C47n͛ٷo7ndڵ,^}/dQ ]񛘘9}4}[zΝ;zP)r[XXXa{B!?͊+uڹ#[3$5C\3ęr廚sGڵk4oޜ;ݻW͚5ޞ+W믿"//=zȱc Lq===ٽ{7_9\ IDAT6mp9ܻΝ;UM6>|T~.\PB!2JEٸq#sgU:vBXwMrr2 .ۛ'N_j*x ^uVZŜ9s077Ȉ yf B||>~GѢE̙3o*UM={[n͉'hѢrEZ_z˗SOʊ+3f ~aD% y=ZCa"V~ĪNZW_ܹs|G.շo_vUn͛7֭O6X_Օ}ꫯh"u/^HIHHzdkƔ)SxboH rIBQsdy-]T籅 <ָqcd0WRgeήVm۶USiFoŸ3Wy1Ӂ1nxE7o\yZEfe^ **\www񉚵d>C\\\v(Ud 8dye_^^{Yy~^z CqԩrJ~ I$W(; +n: Dwww?ljƍ4hЀ3fZfĈg>tBrr2ܽ{GGGΟ?_jF[] 55#GҥKm}w 宭N:u5L4ӧsrkOquu?DR1cƌr΢"1̘1eҥKiР Z~bvWWWN<cǎ%..Tlmm VieW fժU111ۗLf͚Ņ (((`x{{VٳZJO?u:zWY <Ç~nݺѣ>|r, D%66KuΜ9/))(222hժT*̰a8pof\v8jժ2_~!2245j]ve̘1\~9spanʾ}ۤIYl9999%pOOχB<6d#E龘GEb:'c:?Q<>|8'O~vDͺu̜9-[зo_ϟ]vҥKn޼IPPsaƍtQyȝ>͛n:嶎mh"[Xv%kݽ{wY]u4ך߸q#cƌ)۷:u*>>>m///YnAAA'++ ooo>݃+>}b߾}Cg:4i7oW^ķ~K˖-[>崲155رc}v_>AAAİ~z6l@FF :u}w~lڴz ;;;""".u|ҤI888r쫯ՕCR\\ SAthh(9997֖ ;C~~>\z___8q"FFFA~̙3jG>}8}~ ʊ'rڵʕBO#WE1n^n:v-ZP߯_?ښZjѽ{wZn=[VmiOݯo]u^ueW_}N:)j|||۷rն177Ag޼y\xQZ^3777… ceeش3re>ђ=<<qOMF믿[nSN嘱1o&..WrM233;;;w(NjjjP\RK:0`֭KPPX[[+Ǜ5k ڵm۶Kl޼9^^z{w_VavA^^+gڵ={6>NNNZ2m4ppp`„ $''ӣGٷoYYYзo_O?QXX-ZĄ3gV{ BS[apeGW+Mˮ'[TTt멗mKҺ?11Qgߺt5Rpwwg 4Hۊ$&&&ԪUTֻ711ݝsi飷|eϹeKsݯΝ;dBBBعsgc'N/GGG:Tn `bbRa}?}з.3\`ռ Jt}m͕Kn(iڵ{[XXk׮Ѿ}{7nM6j !q'΅צM9LMJJgULrr k]ۯk-w]u50fy}eנחZMRRG‚ ʅ<==ٽ{7_[llι*T} &G)вeKܹåKJ>46nȲeضmX[[ӭ[7qeiiǕ)e7b"##puu‚瓒IIIR...Fll,XXX(׏o~cǎF||<}YbB!ʷlll O?QF̞=[gy[[[ N:)SYf ;w%GDDPTTDڵ=z4;vԹd-:u-333QS֚'L4իW3fz-"""/ЛJEBB˖-~̙3##R}Zn͉'pqqQH9sFgi;:Tokk3wܙΝ˯6l矯:Q<>s/^)((ٙ3gR^=nݺUT~?>SN%""ԃެ7x-[LNX`Oӿe|e4mڔ`VXAxx8=ʔN:ѠA+Z}㫯"**w}uU:!Ps*jWq#%*-ۚeTJK^2??!y'OdZ{?j5ZݏiӦUAI QYϟz>!yfffnM(xWY*xlƾ5UtoW$v.x$]zURLUw RSSi׮CB!n2\HJLLؘ;?pwwWO!M !B"tkfHkL;B!B!DB!Ba`2B!B! LB!B![!B!00| !B!&o!B!d-B!B B!BB!Ba`2B!B! LB!B![!B!00| !B!&o!B!d-B!B 5f͸mж/]޽;WBB\|uquu%>>*ː6~: ,k׮9s!G'BqLvB'GJJ ͛7ڠmLF/C={6ZbȐ!Ҟ!]1VdÆ \O!Pd-1)))m9sN:ѳgOe(ظ3D;Ɗ3b,-- ~B! 5"--ׯӹsg~"##IKKQFеkW>???ϟ_$ȠUVӤIYl999:7mu~ qrrbɒ%;`ll YYYKBBz4tPxӧO|he̘1eBBB矉fjQT*̰a8pof\v8jժ?...:;j(Tt!B2[Tq't! }[]qK76wBRtxȎ=J}\z___8q"FFF/yW߿?aee:۞6mDEE /kԩS/44BBBtĉ\vM[W]vm6Əϼy6lFFFJb&44)S@TTIVn:ƌ;w\Ntŋ9wj޽{{n'MT.F]_"::7|[[[.\HZZͯB!B|jq't![/_}1t ~ԞGҲeK֭˞={eڴi899 HNNcǎ9 &wQPP-Z@V3w\m׭[i۶-oPlذ;vGzHNNֺ~PĄ3gӠAn݊+hN<ɬYƆvѶm[Mbb"zse򔗗/'%4sҩS'4nWWW8@vv6?<8::,_/ѣ9pj}vqq1P龎_!BDžL;III߿_̙ٗ3>|8L2+WTXڵk|G 8I&q9p'ta% :̝Ѕf]|//#ٳdggөS'\\\066&,,X"""P-cǎƎ;u]XXX0|RRRؿ?))):kg}Va[iii$''憇СFb޽|wXZZrq;FAAA4g  r9')))ر޽{WXlο*W!q!*uƍcΝHo!yfffnM(xWY*xlƾ5iWU&QbO6mѣ+JVJsvvܹsj={ɓ'133cرő-4lؐ",XÇ166Ãq=YkWtAƌxxx(Sc.]J 4hׯ~cȑݛ :Ddd$lْO>ssJȑ#,^wR~}\FLL *\vI׮]1557UӧIMMeذaJ!&j6ydͭԭ[>KKKnܸQ۷4i 6dڵOݺu g͚5L>r96o aFM}1BQWW}a{߾},_\gy///+ߙ̝;UVaccCdd$k׮eܸqmNN\{{{lBXX}IQqEvE^^/"*9S~B!Ľ:R8Kse']"""HLLd|dff-oiiINNN}oz﫹r[n4mKKKT*\t w… \Џ:]񻹹q 8uXYYU|;ƕ+W aԩ;vLVT;v-[*KCK4o)ĉ!!!?\yq:tܔ}>}-_SO=EΝ(Yj}hB!x<rϷ=3 w5IOO? 9µk**ڱcGrssٽ{7?{_ѣG9vXfu0/'wüRvަMΜ9Ñ#G{߹sJqiӆÇ /… vB!B'\"n߾˗9y$+V JŪU8s EEE3gkٲ% .d,[&M(ݯ3gΰl2rrrW~~~T;gڹgOߺukN8rEZ_zbbb/ %"""j׮ѣK-[U[[[N:899U>aX'N 88JŌ3hݺu OWռkٳZc+sαpB6lH@@@}eʔ)߼y7x(ZhQ#?C)?amm /i=:ۗrطo_ŋ?'ҫWji_'44W^yEꫯ2`&O\!CUtR.\x_7nRJή`m۶t6mqt`*MǸ߼ysk׿Ś5k^xAYMSes DZd ~!...;*Yd C Q5(ؖ-[Ə?HӦMug8ꑚʴihݺ5ƍƍ\zUxϞ=VZ1dȐjliӦϹsoM-022 cffݢOu-BT =ݗtRM^@{߸qTڵkwmKvv6~*%%OOχV5[Ϟ=HJJZ˔ 8"޽{qvCޣ2yd3:}_~n݀{WөS'\]]xꫯ͇xm!*d-XE ]XpӁh A :~8ܸq 0c PՌ1WWW'|B.]HNN&22wM 55#GҥKm}w 宭N:u5L4ӧsrkOquu?Tחtiaƌxxx(E.]J 4hׯ/s677gϞrI;v,qqqbkkKppr\WN+Z7VR111ۗLf͚Ņ (((` IDATx{{VٳZJO?u:zWQFCW_U?e%66 ԩST*t邿?YYY |8Daa!NNN,Y L@@ ))(222hժT*jGEJI[{%HLLRB$#Q-jO<*ӁK=\eӁ1|7'Mnߟ3ge[Ӯ];bccҥ 7o$((9sqF:v 00R}芡O>nطo=zYllι&M͛իҲeK}9,gggLMM9vl߾*ׯOPP111_ 6Ctt4C%""Bo}?}($447x+VP\\̤I{_DEEaaa5]vO>᭷ؼyޗ_~+B֯ 6*oӦM[Avv6vb۶m?y1lذrO:?...S&ڽ՞i[!xPr[T6zOcǎѢE e}~Eff&ԪUݻ+roݺR}mKs}w~Z~]}+kͿtI_VC߾}U͏9J<ŋ>z1F^^.\+++Ŧ͕+ݺu+uÃ?J4j(ku놻;uQsmz*7o$33;;;ͱ{ʏꤦV;{:t(|7|Wlwwwlllh׮m۶`kkKBB׮] #Gd„ vQ }\Ů[.AAAӠAn݊+Grr2j 6cWv۴iS|J/***5\[{f͚WB o!]]Vcܹ⋼ꫥ7iҤ¾*FEiP\\LNX~=;wdϞ=[7*_2~ռ tj3f%ܽ{W:'B< 2\apmڴȑ#ݤ$}YZ:;;u=*=ZZ]W}k3gy`e_5GVѣGaÆr/OOOvÇy/s }9Fx„ |GʠDjj*666lْ;wpҥ úU%瑔/֭cӦMDDDR׿%ǏW՗ 333ٻwV@ZZɸAAAAiޮXXX0|RRRؿ?)))U>\\\ظq#˖-c۶mbmm<\4WB!+B!00O?ٳg,okk28;;5kйsJSOi]߽cǎ:Z]W[fffZך'L4իW3f̘rkˏJ"!!e˖Q~}̙Q\ܾ}[z[ĉ(WϜ9|شs*ՙSm;wLΝqww 6W:y7ߐ!!!<ϟԩSo߾,_͛jM/\r{rJLMMM6j[[[`"##駟Tŋپ};8;;3sLիǭ[*}+!~jϊvHcɻJ˶fF풗&.y?PsB!yfffnM(xWY*xlƾ5UtoR߻$΅W*SBҢnܸAjj*ڵ{ءcO!M !I|ӱcG^?VBBʴrQ1 !ɴs!B@dn͐< s͐iB!B!v2B!B! LB!B![!B!00| !B!&o!B!d-B!B B!BB!Ba`2B!B! LB!B![!B!00| !B!&o!B!d-B!B B!Bl޼WWW K?~<ݻw'""JHHՕ˗/Wnzz:W{\e2߆T6ׯ_gtڕ3g<B!@HIIyX[[퀀iԨQe(gϦUV 2Z3D;Ɗlذ+W[# !B 5&%%=z3gЩS'zi СCW[{wuX|F%5֯B!DuFqu:w /Bdd$iii4jvJzz:fĉдiSػw/K.֭[oߞDbbb+նgϞٳ˵DTTj 4isbb"˖-#''SSR禭Oaa!NNN,YT})3l@HH?3,X\y___1j;J6lی=k׮GZ@g~Gܛ.BO&|jSt7.䯡osK7nu_CNBѣGQToߞW닱1'N222_~% '== !11QgӦMޞ(^xr:uJCHHO8k׮)}몳k.m7oÆ HW\\Lpp0ل2e¢)))߿5>4qssÃRK~uЁQFw^;,--9~8ǎRy3g񄇇cffKӓvA޽+,_6F]_+Yj;v0|pf͚ܖ" nݺŸqعs'O=Tcjbذalڴm۶Ѿ}{ɼklݺUy-_ƍӡC;Ut7S+R|ɒ%~5ѣ*S6mJpp0S\\Lxx%{9z*W.Gٶ+ښ `bb//׹_(}]LLLu݋۶mۛ6mڔcرnݚE &ԩS9uTȗ_~ɍ7 aÆr/_~233'+_6F]_+jݻw3bf͚Ņ xg1c˗/ !cPs*jWq#%*-ۚeTJK^2??@Υ4 o߾tUkgBTTT-_:u(O}ϻ2jc@f ˗:u*ׯ7H@XlYh"IJJb֬Y?ESKT|{yyammMdd!BB+[#Gxb޽Krn^^^XXXedd0o<͛WO\3$φ2ʓDf$&&2}t 233+]w 2Dqsssknhڴ)T*<<ps/%"/HiF B[6Hg:C 8R)PA`|!"/R- #A"LaL1BT "*o^~l6{_r_g&s={sZ9rD=zPǎCr… 5m4jۖO>eggǺI 76oެR͛7/0vKս{w۷ѐp=*++7=1ЦM+;;[233SYY˗/7ɉ5`?w]ovޭ 6hݺuzûx{tۣE)??_eee:zFZꦛnR^^^`")7#GXW=n'LUVwn&-^X&N)S$RffU\\_裏BLSYYG}TEEE?ESSr6lXkmgiԩZpmBokܹs:t=Fk 6LK.u=ɓ=zJKKm>Z5]w- !aaZb_|1:t۷^>|jՀézhi?Gǎ{cGU꟝t-X;S ,fjJR~~Z|LA%*aY|~H&ʕ+c+Whڵo7U[[&^6mxghŋ+詧JD5>tP^Ӊ'si۶m*@ʣMnJA^~פϞ=* 80syꫯԽСCIt5lųn#F]'3?ZWN򚋮]IҾ}tȑFu۷:̘1CC Ybn=C_Fu猌 1B֧~6m藿mۦ*effjs6 u^"Y&͛UXX?Pt͝;W'NP]]Ə"~1"ZSO=}:#v߽PUWW?Ə2]pA=ƏW\\;w/Ԗ-[eo7nnݪ%Khݺuk.^ZN߮3g駟i&|>=ܹ6nܨk׮iԨQz4uԐ̓G@AAT2V/S뱣d]ǎVeMޒ4~x=M>… 9symڴI*..?~-[讻ҹs4g-X@6lPnnn`;S*;;[ׯתUletRҺuk.((ɓ'%Z6lĉŋ6m&MGQNN֯_9sm/REEE__W={vg00jԨU޽[wcykݜٸɓ'뭷߯ٳgkΜ9zէO#nmnZ媫ӻᆱcwIs͛7ЩS4i|M=*))q=^$6nܨ &(++K%%% ᖤɓ'KzիW}}iii>}iӦ9rD3gԠAxb?^ .uIUyy.\>Lǎӑ#G_kС hoDM9r*//mXիuuYm۶UAAAh=wy'kYe^]k׮۝t̘1ck6L4i OFFzs='OIj޽w}yĉѣ:vXޮnv,Im9Qc!Cs>~|jJ/^Զmti;wNgΜQVV222%I*((Q阪RT\kSqq~̙ju9;t颁j.8p@5JW֬Y8 ߯7xCjkkաChٲe:xP{ݻx" $ybtn)ƺ>ו+W3[ד|n=`?IDATݾsNk;KZ^WڱczFKǼ$QZZڶmNݧ)??_eee:zFZ>{._h/gE*//O˗/… 7WYYE'T^o߾FAWvϭ=E֭[ ˗mב5_vN&O={W^ lر$iڵׯ_uaM2EڵӮ]Ԯ];TT5& ﯃ڵKzk*''G]O.Yww1nkKĉխ[7͟??ͺ[~ڵKG}7٠-0rHرC׽+IupkM:5& UUUҥK.?w nǸ}i}tE ҆ rJŋչs簇u|ͪ ر&M}Zd:dzui婠@|:!CU7b,_3 ]tٳK }[ҳ>X>33S3gTqqӕW_OBZcX߽D>Oڵc=\Zݻw:NjӦV\ϫC|֬Y˲Mְ,|atJEPӁ!۶m FΞ=* 80UI|Hf@RڹsnݪVZ)77WcƌItRVii)gdvlعGv Ӎ9>haعOv~M3"wzs:J!<\oC6dnu z3:<<ܡI'Y÷8}nLݏjHRӧIJ{X%u;kRIdA"m8%]}"<' 1cr5ɽ;TΗM=fv`J7oމ-Ѯ(YMM͖yPan=v͈I!wS{N9lx^xa}UzЇCѝ$dٰH2|7uI5Ν;I :jjj̝;w\nnvI5Ks[n{Mjb|~Oe;ZYe9|{9hѢ}>Oedd\MKKk'źp<:t4ks|Αx<:uꝗ^z|p={PkF9KA<أ$\(8p Wk5Wh{O3XTa ~Of؅më\NXYZ`ixT6R/G7º']"|9Zn7u>.լ!mWXzS.bsoke(ՍѝcN9jNXKOy;g27o3/)f=:l> O7]>C J&IZ,;]?^Ϡvˀ Iwlr558 v9 !ehbI?\Z"&znۯl;KAg לB}8) 7;n=~?vʄmN="|nۼ-Xv^okN^o)9{ ip㽵C s,ZF # )!= RpczVGh"JxSwzKʆ:[i6z 55v[#T-&Q0ڽv" vnp+,\S[JmW6~r¶lc- H/ g4mpBSP=۹UxBH-a"3f1O-%>,6%|[߭,P{7%x;msb÷nnޗ o`Cﶽe.eu4+,RZP±ݾpCu4w$*Ydy_4t4z Ew9ܰN&[Jydj'H6zC sބIPI}b1\ 2+oޭ\$Nd '۱:pzrl!"\6۔p4546^mӔT[J-?[MdՔsؘIE D#6IS bp!T*h5h݆T P-3T !ԒC&Cg,`RBf<9DK&tEXtCreation Timesb 25 nov 2023 12:38:31׮ IDATxy\T0þ(.k]T4ZP+[VYM۴4ť_}ܮY(ྥ ,?pN3 5997""""""jxeUiSo"oay"""""""b0dPys׋j^m tp.Wk2?5< lN(b&lW$""""""2D^ST~"l%[TԙD]W7"<65LϵyZW*ɣcׅGDDDDDD5 K)l:@aksgj@P:AADDDDDD04 s 6Duշ^5:^Kk} NjŻ:@t\nvDDDDDDD BZoL:W [p05P[DDDDDDdy R07*R%e:tDDDDDDDaj6J)15(- ahL1JDDDDDD80놶% LhajP mA[>DDDDDDTwikEaLSu.8a+ JhN+@aNCleXr<C-)4kN;ZoPyf:} SZSKCDDDDDDdL}"mt [`/(+ a(DDDDDDDj++alpB4v:01 *Xi Zpnco$GDDDDDDd}'L m-aNPP@BWp’bQcTr-uQͷVڒA }m4ZS蛭""""""GԠڂ  cƌ9a [LWBWPNK:g;-NO>jmAk} @{ }z "0a%L Hng$""""""2f A-$ PT'8a&l90alPBu)A ;-U_nC5%CA@1 cfP͋1j50!̓Y8]-(mF:eB_`Bu9Tתۈ4^++_iY-a:sYDm&C[A߳1A Ƴ2;>$"""""LU2g*.=|VTP5UZ.T-&4ihӕ^x%8ٳO޽_vtt|T$yD" """""") B/++СC4hP22T)A  X NAP n،uߔnںk2nv%Xe˞={" cOT2,#!!a֠A2ӲB[mUUm.VkheCۘb'\߿Eƍ1|̖#T'Tךdj*g;0-˵i SiTT(**Zۤ3!""""""CoBpBu:(e*c[MJ`OdP977wDDDDDDd-...sssgpuXzR+li`GC^j--uPc~׮],u"""""""xzz۵kW$gԬj HhQ(Y[1>j:]]7QA/%\+**vH$@NHA*Z;+T$ lQ=#Jo?s|ٍC`ںs(Qe39lńBj4I^ @cǎ J%d2塨AzL*yyyd#$pǎQcKz@{DDufZBʛRPPͭiADD[^^ D"Aƍ ""G ) 2n5ahLV63;0fd2)CE...k2DD԰0(IRX;DDTúz,*q ں^t`” ܯUd(..:NP(..#\\\,P L빀ơc552/(,,4i2 NNN9:2c[?.zml50a c$!""y_5ك&MK.Fͭ-""zfo_XX\swYø)Am2` k& #YWH~9IDDd Xm۶a۶maooo쑍|2<ƍ˃H$;5kۣo߾h۶JDd5 J(ܖ ںjWV jX;0a,H,sR;BBB``ŊpM̞=N'rrrVI"˱k.Y׮]ӚoѣGzj4oF“O> ;;jhe>Lfr } aoooA1N ӕj3`65iA>?|-yHP1tl/*b1?nՎb 8w4wZt.\@ͫώ;O=l˖-Xd wyG~|MIx'esХ R...*^l޼εKyGp ^zm۶B޽:tC~~>k׮Xr}cÆ طo233ꊖ-[bСMM""SX|hGGGH$pH>WuO ʳBck]j$QWZL--Ȧ=s_,>}Ax__;''' ׯɓ1h xg㏫$}Ydffb۶m2d5kfH$Bff&Ν;3g 113f̨3R7|8p+V@˖-T;ek< ɍ7T(bccʉ2 ___DFF>urJa[mr9&N'O ;wFVVN>W_}^M"ߔSZZYYfu7t T|n ;;;]ٰm۶ aÆa֬Y8r&LQFaΝ[o<Ƅ  HMM'|Çcz_/;;'O;{9!''Ǧ*7YYYիڲɓ'7DFF>CU 0u={ ;`kΝæMQF!22RL ZҹE~C-#G`ꫯ裏 v???ܹsX~=ڴi={";;}2ܽ{% 'ԩS³re? 묩}prr… "::-ZКuxWT6޸q#D"t["R_~%&M&MH$µkٳgWkT7JDd}Ylٲ 2 XjP|2>={ R߿7o燵kB"O?nt~ C͛7~-.]ӧOw-[~Am S T.]߆Db{O>ӧOGv,~L/O۷o#66'NQЧO<8pbcc1&4ݹs;5kKB"`ǎXp!6mTwwܹsٳ'222 J裏b̘16ykZv q222|JKKqbӆ⫯!55֭Æ 0p@L4 j;uN:к꥗^B.]E%OP@y}о-@Cak-d`lJ%ٳGBPd?pL=z*}5)0aJ7~yyy4d2_0xmڴпDDD`B [5pպukl޼YvBwS&U RSS/… χBPƸvr9ÅrrrBVtUX+ŋ!жm[t6lѣGsN$$$]sf͚uWbʕ C喈{{{"K֙m&xak "zM}C/gBG:uںM }ȕ$#˵.7t m4+č?bƍ8p݋c͚55;wݻQTTdԵ*7?Ou? J3|]UIs,L88q"BBB0|pSZZ ɿRyy9D"4Gaɒ%xL:mڴAVVd2>ԩFD#44}UK3l0 0oΝ;u裏0dĉԥFgEE<=="oza>ꃀιG+ #ၔdff}3gZ%3g37nĚ5kf,_\ˆ/_.LYnP59C6Hc"vgF ∉BDDN2qqq&U;@TQQ˗~=u@rdff̙3(//СC1vXk E=#Y@AA,OGGGD<< *4߫:]jdLkuh w!Cٳ8{,d2SO!::Z iQǘ`f͚ *Qpss;@)ܪUljOS QdV...zj_SSǗP.3u %8YEM1Auǘ "JKKn9VcL3?t,3ezW"""""""rrr=w...}``HX,0(fEER0H$D"=&I"@*Z;d Jwb.ήQDDD @V{ODDT} LU39o$ "": """ pwwgpH$pwwv6~"""XƍeeesH$pttdK """ b`ȂYi%"""2r00ADDDDDDDVY DDDDDDDd5 L00ADDDD7nX; Tx>C """""""a` """""""&j """""""a` """""""&j$΀{.uLڧL&GGGVVVxyyΎ1%"""=Z=#93Q) B. bXgZh]5Q!i LHJJH$CвeKtɤ")) 6;9998y$x """"S;v Ub,E IDAT 6"ǰw&2\.Gjj*._ LH$YռoSSueFow^QBرc޽-%ݨ8<]֭[iӦd/uLu3D RRR{iMWVVbC" ??P({.җj˥R) u(//B P\\ pppZmg}7Dd(,,H$ڀ"\zaaah۶\MPC]C.]K.m۪̯ﳲ#>˃B3뾕"99DGtt픁C %N}^}V(HJJ#ٳgK Mk??? ??YYY SK+Wux#""NNNh֬ynnn8p "᯿Bf0p@ %%ǽ{phFFjRFӧѧO ?(՚Æ CΓU>c>;;;c Dff&jb ZnСCL)??pmܻwbmڴ;p-!77 Z+Zn7ouaaa8yd͜0޽g ׯ_GVV0kCyyymg4(Ǒ>7n\eNNNHJJ7o նm[br%t3>c>h ͛7QZZu"S00|||pjժze]vhڴiƃP틧UPPWWWǩPXXWWWadd""""[wׯ_~EmW:T|'xwAzz:N< 777A+]+ Jfn}իzDž!Kb`B.]ZGnڴ)qil2 h޼9ܐhB1qy:u ~~~Ά;P^^D'OiӦqFM*}P廑BsEʍ#$ n޼ OOOW$ t邓'O"!!GTTE}ii)]FBgTaaau JǜLT kE*=kSyV}8}gceJP޽{񁇇!^^^D \.G^^иqc(..'푗&M (..F@@\\\TR[nH$H$hҤ !ЩS' 00PeRw#___8::nnn|||B8;;ݽϹ1tH b4m45jH bt^^^f>>>~Z>>7nܸJB3f| \Y6Pkl`N{4vP>%_+)#& Qp X;Tx>O$Ex@@,ZP R(C=Ti܀e700ADDDDDDDVY DDDDDDDd5 L00ADDDDDDDVY DDDDDDDd5 L00ADDDDDDDV#v!##r """97nܰv}]l; AAA͹qBBB Au!G 3CDDDDDDD6A """""ur90|a\($a-ij/4VDda L=9p G[/bY\Zgq._Kw9{Ls-䊈&NBVVa ܩ(`TPBHnPsy! "7r9͛#&&׮]c#<<7oެbcc믿/" 희SSS3gX#.\ᅬc>}:rssMNC-?H`Ν:Ϟ=&MZq}˘&_q)Xj%o[;&|_t qqq7o222 OKKCrru}gr9&N+W"//;w;N> Tj>>siii5G'''"44NNNF322{Fii)mۆqơFg)I۹צΝ;jxyya;;;aر0c >P_|L,[ r &O1b6l`T@*b̙0`V^#-- 3f0) >Hooo,_{1w\\rJڋ/bVȥu7T),YDTo]vxGA۶mC7k֬Addt9s5UIIIɓ'e˖A$JJJhUڵkMO?EFPXXQF8x  TC朻榶ȑ# i>Ca/:t耨(ܻw޽{g4j`DGG B 0bDGGu$ {=&"((&L{gr888l F!߿?ۇ/UVr\ "66.]FVk1qcʗ1]6(010AD5N.cڵؾ};rssѩS'L2*.+ؾ};Ν;x2 ˖-î]P(d,^jǘ={6~;вeKlܸQ+**tRݻeeeׯ&L0^&aصkd2zC9r˗/ǵk#`x\Bk8p 7ۇ{SN5FGGc߾}HMMEf0uTk}$u]yO>DHvZX`֭[5߆Qll,틣Gʕ+Ƈ~?[n۷>ԩSP5<ضmΜ9]/Q~/ZɁƌzJH~رcHMME@@LΝ;`VT)ۖB9;3]II ,Y$k׮8q"1}t#F@,&]H 48pcǪqpp}L:z),]ׯ_lݺXz5r9֬Y;w";;͛7G\\z꥖Ç#//pss+g}֨M4yNnzjpuuE1gxxxt_tٴiyLs=LOOGFJ cǎ8t h"c1wrZcҨ2oJJJpI2Ӹqc#裏 uVkV̡m0ktgl U^^ 6`ڴi E@@Pa0899 ?O0afAsEEEغu+&N///7rlڴIg mTTTw@˖-^P(GqqZ sG}Ν;&L'Se֫W/… \"22ҨsRe_^Ov *`̘1Fpp0gu_1|p_HIIlذCZ{вeK`hڴpTϧC¨Qp=a }*\OjY닯 [۷n:⋘>}:߿˗/޽;ϟ3fo߾HMM͛7%=믿n߼y3-[7|pi=\.GJJ lDpV?)S ;; ZlcܹBWƍ1g*V5tts2ׯC*K.x1uT,X@̱yfM4n݈믿7oaFhh( ܽ{7o寴k֬+R4-2!oʔ)HNNƊ+p9@ŋ1vXf-|ƍktglؕjTff&R)ڴi#,svvƣ>*4;VN&m4Xqr9ƌ#J(**2j͛7͛7Gxx8>}`ذaѣZmۆ?ẇ2;vT5^I_dtIIIxDŽIkh@^^^B~/pss /5k`ڴiؾ}ZUyyyعs'Ξ=ܼy(}*^OͲMݱa=z;wDBBЭo߾*MӬܷYNhƍ}T^s@Ř0aฎҥ>-- r=$ʯJ _|QIPPK`~@({gCK26m ߿?"""0zh.//֭[1g|I<soߎy1{lmۆpvvFƍcaرOq4i3g())1*33,aʕjUyyy 3&[6}u?sƞѣG'|cǎū &< 1zh\f´iӄ)Sҩؿ;ܹskaĉx饗P\\)SW1ղeKDDD 33 >c̝;3fL/>#̙3"Hl9իW/bX|9z8<_*1{l=znBFF6l؀D6m™3gD?04nRR>|hժJJJYfLIS'9::"55cƌA~0sLDDDmaؐX3e˿.,urQ|ѭ[7|W 9sݻر#VX{{*ј 8'N̙3eӖoC6|5j6ef͚wŊ+tRDDD7:l<?Ɯ{HHFǏ#%%b۷Lj#0`I&عs'֭[ kN1|p\|IIIغu+틶mۢsol2\|h۶-x Y_~0ÇѡC{USh߾=97N0|{N_QQ///ڵ EEEäIO?'|RLrssCF_֭[ppp@۶mdIYYO^ Ä ?uT!@t<fZv?SrL2Em_cƌqNS'aƌFjP27Tw2h_,u?OTo֋T+WMc!~,yZHP1\FܻwO<>.&ELL fcطo/*T;X/22زeb17n@HHA5H=pju.'X"[dO$Ex@@,ZP<*4+T^=ZqMƮDTg"000+; IDATx z<ʼn( 6ED٬ׯ#11q/^,&ٳgI2XFŽ4ڤrED ǘ "UPPoӧOG&M{j222peYipXm k7U1ޫFDǘ """"}ǘ """""""E +GFFr!"""j(nܸa,P =x/4 M LY; DDDD6M?㺏꣆!""""""" [L:gH>86MڜjDDDDDsjN9Bz"푇EX׮sGDcDDDDDa3(vΞ8\" """,deeY;WU_1 Jj>/DԠ+Qt]\ǎý{.]`ҤI@ll,bbb0l0M>yy9r9ʟ-0믿իɁ7t1c 88ب㕖B*Em*_~{!..]v5T\\޽{bc]-|Wӧŏpvvݻ-~^cm\;KpVZK.A&gϞ8q"rXl Cm}ii)/^( DDD`jɘ4***b ޽yyy [opt<_={999h޼9лwo \%iEEEo_ǓO>)JXp!<bСVu8s ⴮ӧ+HRlٲ;w!M4Ѻ1ۘ_"% "c[o~ C6mfonlT1P(駟b„ 8z(<<<Э[78;;cǎF?G~u?N;#k׮tNPXiϞ=14Yڸv J1sL 0W󑖖3f;~8F 6 77W뾾 dffbٲeǕ+W0ydhZb1sL_:u„ p!Mnn.^y8::?W D˱w^?sŕ+W4O?ٳgcѢE8|s];:w=쐖c~3PTTc1gDJl1ADT\t 7oƍڴic圑;vYfGXƍ}5j\~'NѢE C;;;]T`׮]HLLL&3)c>:%H$|BK L0B 6`ĈƠA'77{Ş={ШQ# ˗/#,,̨4$''7txK. o͚5ǔ)S5kfD5#FŋѪU+ܿvꊰ0=7nD^gggWG#GN4~:t@TTݻooo59%Rb`l\.ڵk}vSN2e Tv'xW}v;wٳ'd2-[]vAP ** Xx1PXǎCjj*y\WJ,)؅*!K&(ci<%K%L| WbɇL>Y&Erzo!y>=9}yCJƆsq ̘?>ܽ{KKK[ԩ? رcx[[[Kҝϋ/{n哻|2ի%ʠ*:ܶm7o 6sy|+gϲ~zn߾|@߾}:urq]Ƈ~ܹsiݺʾ}xڵÃǏZd2AAA]٢@Nl&33N:1k,5j'5 Ο?_jݫӥK~7rssy1mۖUV?666X[[coo.N"'''''&M$lmmm?t,VZEtt4jblݺǎ Xs .^H.]Lzz:1113zhR⊗K&‘#G>͚5cĉ|Gs9022o߾L2mmrcY8|0ߗR~cbbشi75kܜ+V```Pfyˣ^FFFd2d2Px'[>ݦ,wܡvR@zh׮gΜB0122RڔIFFB+cQL/sIxp.\:+O7op[YY~fڳgC AKK0=BOOOeTF/ANL፱qFN8;v@[[ɓ'+Y`666Ӿ}{֮]˹sXjׯ'==2SQt6mM6̝;WGELOOgݺu̜9}aff+III}v rJRSSٶm?)))e~qnӦ ͛7믿f֭?pNZjT_eW?Ο?pA_-3223g:t닛aaak777233m۶%Kصk N:A.55 Zlyg,,,&669s ɤ\|esMiʃ*kL)sè… ^z~zKs1j֬INJ˗ر#cٲeDGG<G^FOO5j)}E&}vK(߇&ݻwիL> ʫ߻w2gRSS6lVS4.]iӦ*MA144ɓ'R;i")ʄ)ͤI8|0/f޽L6SJkdddfƆgϞ2T3]%֖3g2j(ڶm @ZZ|BXcccxiudڤ믿J%ҥK,_EylUK rrrرc ,y昘Hs^0 <''',,,ŋ鉅xzzeI6m022bر]jٲe _| 4OOO_.SsΥm۶%ʠ*:,O[Q}?x\z쉁͚5FC}tcccݩSGٲe ϧUVrKHH `ii)|\~Xt邟^^^p5O8 &_>}dȑܸqQF)&P?̺ud2|T/|%4-~[l'OO>XYYW_9 BU+VaÆ|*SWWooo F:u3f 4B0"--ݻwņ :t(4kL-bmm-uӇ8;VS9TL777sIxyܹ3O<ߟ@Ky!oSo^YbE @y\cccsqTMW/DDŽ oAikk_.//O߬]U/\+ϲU;vPO MMM찱ٙǏy!gUWEˬ[]] 8x sҲܻTX2R ,իWIIIĄ>}ЧOwڵĴ * Ν;w8{,VVVw#jȧլYx DũSJth;jԨQfʄ)J~^6R#55'ҰaC.\HӦMOeϗ{jjj)^ua݄r);Ɖ' PΜ9*LLL)\($"""[lfbӦM,_\z_މ#פI*=xKBK[ƍ<~X=ȑ#ԩSsjjj3n8]FtttƩLONw5I7)jjj e2W\yekܸqxQ5gy6lH͚5T|(z{* l}wܙNPxl޽ iӬY L_U21tPrrrXpZ 999 aիԩS&MH?/Eg9ihhH׬YL&.do߾-֭[e:Jy|B۷osm:u.ȧ^y&ܿ\ ֟166w,_gϞď?$AXX7U^F8I"}{DDŽ o===YbW\add'|Rn>˗ÇYfBmۦtWhjjڵk9<ܿ+WZqyRSSs_АsE޽[BrwFF --ϟtAC||<.\ɓ'lܸ,*ڵkӻwo|||HLL$++hi8iie(^/ӦM㣏>?ɉ'2sL 2 +6nȲeX`V-E[_ nݺեe˖dgg3f|faaAs...,X~I!|!O\ӦMiԨ))) ||X555iβa y1֭+1裏>SNիo?9\WWW@q$2DZYfXYYѣ0ڌ?J-֭[9֯_/6l'*@tt4-bŴhтl)Vƞ={x"#** 777&L MR6L6עE ̘7o|Hvذa@EpB0aXXXТE hKK͚5'~pM455|[[[LMM:=˗/gڵK6m>}Jwsss122ȑ#<{ 0{l)>Zx12 Ǐϔ)S;w”Ţ˖-ŋlڴ,,,pwwWXY_0? $((ٳg)'NdذaRkf͚5MV >K/e(DzwU[t-mZKy]4zxkw킂*ẸG߿?JR5puuɉWwV%ݻooo.\qnݺ۷JagJwp oq ;1/ XqU Ԯ]/^w^֭ˈ#o;[^3>\}jz nkL  >'O1!   S9*^GA}XY^1q~}|055,  qw8o?q w#   1!  "qܧrۃt AAgXnӤ'tu[ .}   p6/S@]\ >   N(SB "{EtL (''ϟ+鉥%|'8;;sNc|8O~z:lllhٲe-2WǏ_UEj9s 999 K~^v_7oSyέd̚57NUqqqUkTtRlmmyfuguuun޼ɤIصk^^^<{3g*{1GF,]۷3|pT ST^^? IDATK,o߾lڴ ???n޼~\p///N8 Xp!@g2dǏ OO7ϟgԨQرeSi*j U!A룯ϠAd˖-7xnnn)ʓC̚5I&3j֬Y͹S-\p=zHҺ۵[Z Z;wpŗ~\ի\pKKK֭[ԨQsvRWWgkcccڴi=nݺ`ii?THK0EijjsN})L>Ο300P߿?;w$>>F=VVV 20s]TPP;5j(eSi*j U!:&Axkd2nJDD?}xxxШQ#pѣҭ[7YnG;;;bbbq%S^'Oyf?ΰaØ;wBO2n8_T:t@zz:YYYYWWW-1FSSWVG^+555VeSeڭhTA7nĉc||pqYZYrpp ::ZKӧGKK SSSسgˋr˱rJRSSٶm?)))J )(( %%Rin:fΜɾ}033Օ$|}}پ};Jq;*[6{{{'::Za2**SveFRӧ>|x|TTt6mM6RgOqeeoy޽˜9sHMMeذaڢ1:::?۷+L._Lǎw˖-#::Z0Iݹz*ӧOB:lܸ]]],,,ӧV3e޼yٳ eΜ9pBéW͚5#>>^岗E&ťKX|9-BKK (bfƆg)2aq%6m+N69spttСCcggRƍ?k6mp i+Wp Px_՞Ti !:&Ax+c,X@111.7Xl?KPP&Mze#v'9UڭhBe RRRSX`OWWmے '[R4^~~BYLPBCCQSSQF|%d/KqZZZݛ(:uӧի,==Çs%233INN.I d2Ə/ٳgc̜9?)Sзo_ʦY6m E577'++ P\PltޝSNѱcGN8072U*eW‚ (1CrgFՕUʶߊlN}⫯.]cΝ;Ç9y45b֬YJTgkk Px͛d2>i=}5jԠcǎI籴TؖGڵN:k׮ пvX4i 4`„ 痙VZ;USNڲzjfqpܹl>wf̘ӧHTeW> vvvԫWDʖh*N:޽PN:űc8q!!!4555ё)S(LP1,&;;@x555i "?]v-1aÆ(Wƌ7k׮]6m7iDZ[E0 ̙3R*蹮NPPqqqի g̘1d2~wrssrBXzu֕:n߾-=&֭[%OJJ"33@Zxx cnn.)ٳg)((P8" O_W^N:4iD֦I&J|ikkKwSѣǏWar6lU066V9/KΝزe QQQ=zm۶Pd2Ν;Gnn.lٲdijPHTv+ڠ 1![AOOGGGVX+Wx1~~~IeX|9<|5k(`۶mJ_>/^ݻPO>I&h"i}_6uuuǒ%K::x)?3O::ExbZhAvvUW8p4?N\\rJJJٙ޽{ӻwouVѣIII!88l[S9Axk̙3~ٳgGnXvm7=<XFAΝYz+O<<<ؼys_{n͛'2m46l@PPVVV[!^i7n5jϏ{addĘ1chݺuuU &AXX_|EiVVE炪e+_~:uDDŽ25jܾ}???O#F,YvmGm,eQ9Gٳg4hЀٳgceeEbb"F\z >cF%=ˋ%K,[La'|={LRb(sEa/_.=7666m0} 2B"ׯ_'::plllhժs̡~>|m۶ajjJ֭)((@MM ___Fr W}}}j׮͚5kHMME[[VZ06oL@@ZJ)/^LBƏϔ)S_UdffrIxM4:GOO_FFF_52w\:88Hq`EVA**閶]ꥼ.FH}Jbb"lxAAxX45#r;'dЩ~vA*C1!;/33-[Iqrrbذa/-  큳O,o)}ZaE=o    殿1~ckL    FT$dAAx$&&VwWL㷟8.7eDŽiugAA#1~c(AAAA AAAdT;ݿO3sA^1!  >JAIOP^gͦ+غs'»L{  wЪ\S@]VSAx AA7ؽ{w^ugCxPSBڤz"{ELAbd29rCCCV\IӦM_˾srr"<<ƍW)-WWWMdd$/WWW=?'&&G2|9B@@+qqqaUښ={s8)SAGG͛ӼyWډ,tBLLOz0Gח'B50a/^(3]e<~ѣGSF .]>|x[»+## .ʼn'0a .$55( dر$&&Vsn_<,YB߾}ٴi~~~ܼy//2㨫sM&MĮ]ٳg̜9S!܆ aɒ%l߾΃^u1! ܹs֭[TwVzPN:t@ Xr%ݻwVZhii.ݻwgĈ8qK__Aɖ-[7n+gy/^Lڵy)cǎ%!!ӧOcoo֭[%OF__]ݲشi3f̐F1,YGGG"##qttt,--}/d@Bܹx5j&j_%<<szijjsNi)L>RG3|鵱1mڴΎGQn]bbb1b;v`ƌ 4O????TٳҳgO R(OLpp0۶mcDFFҭ[7|||={0qDlll3f W\Q1zh .3LZZ͚5^;88oI/zr=CKu[ZR}}}>S}X[[gÆ deecoo6mW^lܸQz).++N۷o0^KK޽{U8(Օ͛73alllpqqʕ+ڵ#GbccìY8U=sj׮ TsG)iFFFd2d2iBCvUjqЫW/QֲR IDATؿ?trR %445555jė_~ÁW9U8ݽEW ٦M"k׮hj~sppx)kv}: :Ǐ3rH KxiT*G>ښ.]uDZc|*G${VV42PeĈ՝7Ҋ+hذ4E< }%49ԛS#^---ZhE222xBݤҠARP&|=y'\&Mtwz k߾}p,B9sP4441ƍڵkDGGckkKRRDFFJqٲe̚5M6|JQAANLA WzO&q7o^fƍ4lؐ5k_EK+*:ww}ǖ-[RߨQ#ٺuk&K}!{UH#>>^h(lbbBn WNH+z(ND:]z\+ח%K0qD8HXrrr|rXLM&M~ {d2N*s2qѣǏWy$TGm6E.s6lU066TR*GɓeOcBAx+Ȋ+r ?###>r}g,_x>|Ț5kxxxm6󤩩+k׮s}Im(osP!| &MbѢE$%%P~}.^ݻwV~utt… @aرc  ::b 455:&փq\\\ #,,4;Çqvv~)eS= OfNRRݛ޽{.qss~ ƌS~}Yh/Edggq6x9w$%%c) -Žyσ8r{UxjPU6~S9AƜ9s={6yyyt֍kV8ÃUVEL&#%%wƍG5޽{1fi۠AhԨRgffrIxM4ǧQ_}'|g2b:wի+_}}}Yb>]vlذ--2Q~!EӦMi߾=w.1bϞ=j׮~~~ӧň#011Ad)*{|z\KSsMx{{sEjժ#pwLFzXnԪU;qFi&Ç@=zDΝٸqiPQKKK|||ؼy3jժ6bB$T/===~rܼy/^L&C3eʔmP__ڵkfRSS֦UVbaaN $((ٳg)'Ndذa/%ߢ ^d-mZKy]4zxkw킂*EAx\ثBR1o`cc /#8x]ΎAڵk7n]gMkoܹ3 ,кh޾FJRR>>>q E:>#66"\]]+[z5SLgϞ̛7ooo݋i+**볳ٿ?aÆ3p@Ο?3V`~|l߾]k} ּ/ILLy:{ܜYfi;::ҡCˍ7xnjJcvb#F`ʔ):tsi} Q1!R_|}aʔ)ZK/` 53^PŠAxW ӊPǏGϞ=2dHw0|pVZņ Xf {k׮,^ݲe 'NӓѣGsYN^#GDRqJdffҦM{///N}:޽ cN̙3quuϏEBB3f0|CCC'667777ʊmjݺ|q~9ܹsٽ{w/[deeѸqclmm5tꖑkjyyy}:JR뢽w޼ZyOzܹ3zp///y ^=Z޲eK233NKaa!wӓ 4jW/Fòe˘:u*۶m???RSS eƍLޅ qssPRIaa!+ .dΜ9XZZV9MU;i$ܹsٺu+'O߯thA&B+..fӦM̞=mZル+u+Cm߼y۷3m4pttw0-[Tׯ_={ҠAڴiCbVTRXXȜ9s]}:uё5jTac4i҄dL´i(**o1j[ƔCe IIIDEE1j(uwulƨ 5cdd$[o9v]t!,,`<==IJJ"--}ĉ0a~.\@ݱGsaT*Qm .{\fTVÆ 5֘4 2oH,,,4iYYYжm[>Sfɒ%8;;sѾ}{4uY=I @ڌJ0ƍHMw^zń HKK#;;ۤ4UӱcGƌw}GHH祗^t{1!tJKK&g\H~IJOOGPh3 川Rd񘙙w~ټyQf(V` <^+Ɲ;wrݫ Ɩ:unnn:'m3ZO_L>VXXȎ;OOO2ʋ!&&3337o[oŰaôK)#C糲&ZFvfO?͠AP*|wNHH0[}^^ޘ8ߏR\|]~wWጌ """x{{{ 888FOiР!!!8p_JHHCQF=@i> 5jJ3ǔ6#jWdd$6661C^y^~e_ή]Xd ݻJi`233ټy3X_UVi 3,KڠBq B돿 RJ%JtӦM:/p /))DGGs)[O4]t!99͛7,SN+..ŋ>֖7|r)ƔCyL:񕏻:ed|VGMVZannAzz:NNN׏~UP矯0c0ÇZĪU(,,$$$Ew N:|78::GnnVyfddЬY3Ғviu999inV{eHQ{mƱcXjy|TȸqHJJѣڂ1injj*vb޽0,XiXz5 .48Ԥ >d(B<䜝133_~,S*={mVEՔzU:ѝ*OXv-G޼ysBBBX~}&KKKR|غu6+2111899UFiacc_^+r(ODc;%edLU^M5[[[^u㏵kmժu?QFlRRŝߏΙp%.]DΝ5-[p!Qs槟~"!!ڷoOhh( hWWW6m5LITra8pcCT_ѲeJӤVrST͈ڱo>6l@ddd&t|XYYY,cˣ* OV:T>1!9[[[YhgϞ%;;0֭| .$11,>34lذ1YXXGDD ( ]y b3?//;wɭ[8u666Э[7&MĜ9sHMMiӦ>}+WPTTdT֭[D~'rssYr%0]XRRBQQ$&&W_}EPP֗R)..&//Cq);vdǎ\rt,XpP9z~˫n:?_U/ɓ'Or|||8q"SN<S+#C !//cDzrJ,Xٳ5j׮fbżzK֭[Ӽys9}Wr0afMu!>O8y$^^^;깁RSS>|8}O>rw˗ ĉdddʦM8ri@ ]v0sL~:qqqlݺI|xP!xL>??RvJDD[[Yt)4h@s`nnR$==]k7nքqU=zf+C6h ˟ϡC͛lْŋ `ر_kq1F;  `ѢEdeeƊ+cccҒ֭[ӱcG6o\a-[47lWWW4wc|3bt+SFΧ>Wy]eT^==x"<; 2DL6mʞ={ذaδoJÇ=z۷Qc_d:tWWW&O̒%Kox*ԩSO J"**7nʕ+f7|*J4i²eHKK~L8CI|xݭVkYu/ƼL>{?Z U**Bܸq_~cǎɗ*LJv(-)CJJޡMdӽ KTӇYw PeUeK2){Ueh_?Jr!0ZRR-ZN !BQccB!D9ryƸB;ser0BG1!R] 6mOBqگ[B+2DŽB!d@OOB!B!ɡ(J B!1)))?>{Ɏ ggA!#??ѣ#B!B#yDŽB!Bc\2ڸͬ"B!B.yz*S !1!B!BZ#B!B!51!x$)J,YB~ҥKlxxxVmcǎJs֭jC#Fnݺs1 IDAT}x7R~b u/k5ujdee7=z`РA̙3<ʼ[xxx{WӫW/>LT*ٶm ɓ:G߾}:u*nӚvQ3OZZ={СC5rL3P^}U Ν;k!cL;-O_5 Q2DŽ⑴c~g6oތ9 4jܚ5kHKKcwmpo-!!ߔ3;} e޼yl2&L@ll,wԉ^kRdذa:;gT*5rLSRRʕ+ٱc888THJAA{cѣ?1>HZhn ݻiӦ 111nh۷ݻ7\P(X|9qqq( zǏg\tƍӿ eWPPqx饗f͚58pC2c w8p$x f̘Au/???||||8999cggȑ#yM:.333"""tZ*Ӈ#GmԾl!CZNTT,^X6Li~ފeff_wڶmu7dY|VN3VNդ +WrA/^̦MwޡDfxzzj*:v@DD'N`ҥ,_Jp={6FbРA$$$hkK,!## 6t?GFFrqYbEEEZ˟f݄ӷo ǤP(9s&1~xHHH`ƌFohh(憿?F-[ԩSٶm...Jhh(7n$--(|٧)]gii3aaalٲLJJq꒕Eƍ5†ꓮxWW׌j<''h&O̶mС3f̠:жm[~m֯__anmSwU/|KҔ1b}1:uosqU,XѣGM>6c\z{{{իe˖z(+##!^^^<3S6Qz._4nXk#RT/Sme,i*cBq(..fӦM̞=mDpp07o2=x`|||puunݺܾ}Xpuuٙ b(7o}vM;qe˖Jc3디гgO4h@6m*IR 9sT|?#:uꄣ#4jԈ}U~Ѿ}{5jǏ_UgZ*oЦM;v,?8FYB ))(M955cꡚ1eC8880fnܸAjj*VVV]7| DPPϟ,M=Ɩ]֭[iӦ 7nd̚5 7'O'|>1İh"gÖ-[L:6ceeeQ~ 6lHNNQٳ'M6… {Fmm*\ EIԩ Ct0VN˒6(Br!锖jMgccó>Krrf[| B+_+ojLNN{_%??*?"&&h޼9oÆ  CRj1P2/kͱfkk=zƌúut+`y6uEJ%ݺu[3`>S͓Iv-ΝJ"((HoYsu޽ny!SPPs򊋋پ};-ZADD4lx&Rёׯk-aZک.cBq(..FTP(YYYB^ZZRDT/!_6mڤ˰%%%zc3ܜhN:ŷ~ <<\K.$''yfLS3|ԩ.*ۉpo1RTTĘ1c۷/&MYfL0BQx{{3uT1ե5cꡚe+6 틧'Ç:;&LgMzV:qm̰1:5O>$ׯ_믿pGTU3lTH^^ZeAf 揋QFtEkU(,,$$$Ds1P( N:|7:$1|\ڷoυ \'$$ЩSZTv[Y;5A&2 !ΘiV*={mVEՔzq:WswwO>aڵ9rDkg aƏꍥ|feei&UKLLM6z㫎S_~SZ;w999L<֭[ckkvQqg3ull}5}Ԫ[ԩSƍk]TWnRݪU+)..LR4*ƤQ333cԫW;wr1MƥK4?+ēk%!!s`,WWW6m5KTraƊoT訚;w.?3? $$$о}{BCCIHH0S2sqsttO>,\7o_|omv_ji&B7lmmfѢE={lppp(B[[[^{5.\Hbb"YYY|gZiٰaCcϏP(\vgPlsN233uNƦ㿺uƤI3g4mڔӧOs*ߺu뒘O?Dnn.+WPr>IOOGRA_~S딮spp\8y1b>MꚱLoj #!! ._̪U8wLۺSޕ`f̘y}iӆݻȑ#Yx1VVV?4e9::2yd>S󱱱ᩧѣGx(7䣏>bѢEix-3H=ʵkXh5]ᅮjJ=///>N<G֬3gvvv˰2ʶAc-v*m&C9ӧRZZJ׮]0+ҥK A 0VTI17kkk¸z*=Z3!c4hŬ:tHn޼I˖-51;@ ^{5;ƈ#pww򵳳c,Z,Xb.'/!!!\t 1ojU~'OfŊDGGӽ{ OH01 էE_]~L/s@s֭O?iٲ%ܘO}孯Η;I}~.\ycС|...Ƥ)kСݻӧOFpp0̛7d4h@PP ,נAy?.:t0p1cԼWذaT*q\R&++X^yy+j_tt̝;Es1NuACVnSisZoVf_s˦1/O^Ͽ_oCJu "xݸq_~cǎ*LJ?0Ջ{m۶U)_JJH9?cB!B!z$rT* 'B!xĤv.ss274x$;&k;!B"ss(FfwB!B! B!Bm> q˴k7zv8BGtL!B!eEL&d(B!B!jtL!B!HDŽ⡦T*Yd LJK.ݳ}AZZZǎ;*ϭ[}Fu>dJ5((<<<ڵ+?#>>?_=zܹs|u>*|_㨊BV\ѣ wɓFYS֭[ꫯ+0|'77Ë/?C-))!22A/2vX~joW<:/ӧ#GRR*l۶h>cʋϏ}2uT233+ӧ5_Zi SHDŽ⡶c~g6o̪Upqqjܚ5k?]݇'O=]Gm[oϾ}5kqqqDFF~V*s'hРذ{nԩcϟO޽xՎ]RɴiXr%999tԉs)JKK23ŧ~Jzz:˖-#44 .0}t4ٌ9kkkϟƍ6lnwŊ3o<6nHǎ ڮx40d8?AAAM܏eӦMdggLo>BCC8q"֭ښ ±u:txyyiI;/˗/Ӿ}{7n\ۡ??< nה)c?/Q1333N.]DrrV%}ȑ#:;&tٲe C RLک0 BԊ+WrA/^̦MwޡDfxzzj*:v@DD'N`ҥ,_JǨϞ=QF1h ѻ%K KzzWC###9~8X"xcx{{{n۷ocR(̜9WWW~m?~<^^^$$$0c 744bccqssߟ|_NN˖-cԩl۶ HMM%447FTTVP~Sꔮ򳴴ٙ0l &%%8~#G0w\v͸qdž صk㫯W^|)S+_6S~ɓٶm:t`ƌGYSOѴiS9s&[lՕ^zq1ORݻwoM9߿zѹsgzαn:~7{9^ʂ 8ziy֭Kbb">>>|ZsT58;;ӿJ%7nt;>}b {{{rsslii)fE^^XZZ2m4<==>^9sfYDDVyT**))Iפ TRRRT*$_ZU]tQ*RI/U*JS{*9yWN̞=[7βT#GjgL蒜\:BU_q*$$D5dլYT*՝2wwwդIT'OT8޽*>>m+J՚5k4eSh֛ݪ~N~X;ώQu4MӴ{Q͈WzTqqqzú+٭RT;vP :B={hv?3մiӴI;9 \>ϵ\6Z?׾nϵ]e2juN%憮k Bs锖jMhccó>Krrf[| B+i7~vJJ Jk~rM֧PVZy={jՃ}ѬYZeooONNN庆R\\Yhri:cBqT*Q(Z^pT*Q*&wHT~lڴIkKJJf(9ќ:uoӧAxx&M.]HNNfz'4|ԩ e;>M=f]3f }eҤI4k֌ &P(L֖7|r)y(((\$W6 `j+K] Zj9899ѯ_?WcBݞyP&\~@I>\iGCQߤ133{tooo}]~Jĉyiݺ NTa#Z_V˖-5󑔗ʮ]ػw& 6mWf…&m;; }ǁJ8::Gnnw :shԨ\<`Z7G1!眝1332T*9{,m۶4_-*)?8ȑ#&uLUɯ'|ڵk9rX͛+mW?nXʗoVVfbPDwCu/uJl;w&OL֭ZޛQF܇JsU-]6bL_i3vC<[[[^u㏵AqqVVZQN5jD˖-5/uŝa΍p%.]DΝ5/XCi'55U_=d/MII gϞJCe^W+nݢGtE3g:+1RRSSILL䥗^\4уhKNNqwf>䓚;CLٮ}RĉڵkIKK3QJӦMT*9|Q9ڬSQ1!lmmfѢE={lppp[nzk,\DϴaÆ*daa$$$P(vBzCʟΝ;֭[:u  O֭&MbΜ9дiSN>͕+W(**2|ՓOrJ 5_HL-G} ޞt )]@AA_5/Xed`(VsX\\Lqq1yyy:tSNѯ_?hذ!}aŤPXXѣG+ )_i#S~;vdǎ\rt,X`Ęɓ'Or|||8q"SNwttdȐ!1vXV\ɂ ={f[ڵLZ\\BRu4oޜtΟ?7䣏>bѢE1jԨ ZY'N+¤I5jƩs,Y???:#*gLb˖->}Wr0aĬF"66X233ٿ?{a4ewvpqqa̙$&&ruغu+vaqaߟ#GΪUj sssƌCdd$Gڵk,Z ]~w8pmK;BZ1}t>s>CJKKڵ+o dҥРAQ*hRLƍښ0^G֌7|l Һ՗???C͛7iٲ%/رc /^رc1www,_;;; E͍+V`iiYr}9BBBtb0:&O̊+{G+X JXXC g„ Jv4U+_} |@pp0#F ///M1]u^z'sE,,,x'xw2dVM6eϞ=lذgggڷoJ̌Çsy= (1/yf:yf xg8~8NNNnW֗e˖A:uxg\|ݝqƱuVC޽qvvֺBW~`T۷9s WWWW*ŋYf jՊKj~1----"::?|8q"C5zUaqqpp`}cƌZ4 6 JETT7nݝ+WjV] 66W^y;;;v*LuWf4b֗SeӔ1S>F6TTǫp,B؍7x9vQ???|||̺(-+˼Te^yOe߫,]˪d(⁖D-KJHB!Nr!(ɤ΍79[G!B9?cB!B!z$rT* 'B!xĤv.ss2x#1\!!Bw񇟜C0z4cB!Bq_ !B!o$B!BoaxC !B!BB!x@\zWvB!Dz$!UT* '..{{{,YB֭ɾ޽;۷oEږ>>> j)+ ytڵߦ]v5A||<;v?$33{{b6nݺEii)ZO{뭷HJJ?{ݻ w^*C1ÝԬY3<<l0{ܹs[ݲTT̝;N8A pwwƆݻwSN5|zŋu_z֭3%a(^zѳgOnݺΝ;yw)..6ju֥m۶m۶JGb2Vii)ͣ^0.^HppV|}}ٴi5}ĉYnL0۷oWăw=zT5j(^}377ŋL4`n޼ԩS3ԾLݮƒ;&/_}4nܸCvvv4jԈN:ѬY3,Y /@k97|w}GΝY`999ؾΜ9O?D.7Y? jl5mܹ4lؐƌCrr2= 0ܜ߃(u/uL@@|XYYRشi 8Ш2fp#jʔ)y޽{KG-D|]C͙5k潣#:to߾ܸq{Lg>C a,B<ДJ%ׯg׮]dggӱcGi޼9pv#Gk.~7BCCڵ+ e˖Jo߾YaDHH[n`ݴiӆJ]RRBtt4ݛm׆+ /_N\\ B%APǏ|r.]Dƍ߿?~+((`ܸqKX[[f8СC1cQ;p@8@RRO<3f̠}:ϗϤ O:̤Yf?F~=9r$111=z^{`111l۶7nY5;w &p1zzzr .\ f̙3l߾+WG}DFϏ޽{?tԉ 6)RItt4̟?EEEDEEqQܹ3ӦMyZCi|}}S Z-,,XzVĺuW7oެۺuسg׮]UVL8=z̀xg/Xt)111I^0`666>|b|||4iQ1ꫯMrrQ1;.8p}MdCAP<.#oTTWa.* (ƛx+xG% *l4zbb@71Q|xE ǒ1bX#r\AfDz^鮪vu0]U-#022"$$˽}6wEԔc&Gs]x:uݻw7DNNXZZ]_O C9AxEDDp̌sRZZL닣#.^ƍٺu+ddd݆/9$ݫדݻꫯTk}hh(.\ 88pTі?//oooFűcٹ>l2lmmd̙ҥKߠ ϟOtt4{fk\L\l̡Ch߾=Ğ={`<ί\X`W^,]Ru9E^^ݻwkeؿ?888py*_3f /_իO\zOOji Ą?ܼy O?錳㱱!77HΝ;W̕(Īi^JΝk}n޿͛Ӹqct:t ;;[mCu|Pa)\Naa!W^eڵXSSSi i_+ڠ`1!+(|}}ڵ+m۶UyLHHP=z4ҨQ#>}Jtt4~~~bcc_bZ'OQNfmmܹsйkMWRZZK.^$ MV;#5^^^hB xj2~xtB۶m>}:[&99YczC[ꌹ۷|rz聵5nnn뵝͛GϞ=@9d5j\Q^zaeeŴi!==]m S _+OFL&Sp-Ο?O߾}ٰa8::BFFθ0{lf͚Um{(رc 6L%݃8x ]taϞ=*oI޳gc΁زe sz _o޼ɡC011A&=sSMsNLRkej*~S;tYf-cȹ.nܸÇqppPɓ'qpp`֬YdddhŐeH C9AxeeffRVVFݕ@ݻw|*j:S}ղҐ̜9###bb7mgddhMWN:!ɘ0a bf?r׮]ɓʱ[8[ xjǹz*dddP\\W^]>|ȠAtƜIaa3}|OQ6Ǽ}~+M6+˨JsJAq'ȃzȑ#|cL&S)'//OCmjݺ5Çѣ:t .hѢjޥ"߿?&&_}\]]?۷ږB͕W^/թ|C }?1>kL5n:Zn] +yjړ;*3r /hݟ 4Ç3l0>|Hll,ׯ''ji i_  5,//WgffL9v(~qYf/--Ƅqe=72`e}r5KMA֭H_STTĴipvvfΜ9jՊYf)o%%%2m414 CTu~SFUS :uؘ7oI۶mqqqťZDŽ"{O9tHuz>uT[V^MӦM7ná*ObddTŜPt Ύ-[Ү];\\\ҥ1vL5w^Ν;޽{kT5yyy<~XʢUV5^ՎR}u唗Ǚ3gBwFFFX[[3c RRRHHHPہ`hҷ\}cTm~] lll022R-˹~:]v՘]vՖ֭[Ӹqck^>_ΎO?۷2FM6Y]v NmT~dVUxƍ`:wnR%K۶mׯΘ۶m\.ƍj277_UYfȅqUTKm甂cǎO>D%MGbvԉ pMZhA/EG"[ۜ%;vT~!?~rϪHLLTq$I@yme?`*DkJz4ĉ'gƍ*vglRe\.￯1)66{{{54NR,K[5%`$:&AxeYXX0j(֭[y6lʊkvZشiJvmpL&&&xzzBRR}8|0#33իWLi(]>֥sj-X?\v wwwfϞ]q`mm͘1ccDDDzj|}}eu @9Im %KkK.ۓɔ)SO ̌3gݻwS2=BߘkcM4&uXb+W[nQTTdy[U1ӦM#44`f͚Eii)t .\?'Nm۶iR_/]%K4SqƍOILL$55:vܹs3fJݵlْǏ{nlllٳ'$addĄ u H=lmm]VӫW/.\8AszjMAߘkcM|˖_ݹ}A4i$]L&ʕ+رcd_ruL&;:*..ڣ۸p|'OD&`3&eeeݻiӦȈ#X~=Grr2-͍!CǣG4`={Vk̘1gggyJRBCC9r$ b\tGx=duh/IMVAy Kc۶mZ깔}a.]ľ}}Jbbu;vx V+∋cټ;qqq5jD׮]ڵ+5ҙ~ժU899v_~vΝ;%g+檾+6nH~~>{&??}P`Ȑ!|lذTզ$>}\cccRSS3g| > '''T^/^8}ۓ&ڭhdR ‹p]zɛoY26mΝ;qppV+WhܸL,,,oذ! 4I&uыall̮]jk׮?ҷMx9~Z6x`:v숣#L8BsE_&&&|Wecc .333akk\ccc/_|ommM^pvv&''7xD&Nȟ'-Zٳgqo#JJJ8z(۷oP^8}ۓ&ڭh$Aرwww̢ERԩS,XGGG(//'44#F̘1cPmwͶm8y$#00Z&L@dddu~ ޱcެY={pQɓp 81cƎ섨l׮]L4گ͍~Aƍ 0@ӓ۷3{l|8'//???N< ɓׯ_LLL5sNOr۷3~x:u*ϟW2ӧ+mܸLF||23b\]] Wl߾S*:ڵk$IWj(:%Նdffyf-[F NNNXZZl+%%E>??<:v쨶 OOOmƬYpttÃׯ70i$Yx1y L^NN{53u=݊6(R~ dƍSBWzOOO|F1ydv @vv6qqq:t}\Ν;iӦ [neҥ/mKSSSlllذa5ԩSoӡC#ĉoO?ѢE rcvMYY~)!!!,]Tc<޽{x{{ӴiSƍӧOEݻw̘18pW;O?qU͛ǖ-[TquƟgXp!_C6mDFFo3p@1kϪ IOOqX[[.W^s*r[x.K.S\\̭[ cŊ*dΜ9,XbѣG3lٲݻwӪU+dĉ͛^XjOUnE'qDŽ B=WRRBTTtڕm*Ǥ&$$(Ӎ=wwwlmmiԨO>%::???lmmOv M_$IQXXȊ+Ԧ\yGAvv6+ RÇ2p@,--ҥ ={˄ ׿͛7bԨQ.Wm7ozʊiӦCzz^5z,ƏO.]h۶-ӧOu$''۷ogkkk077W=y򄘘/^̝;8<;wPVVƟ'>#.]Jpp0θ0{lf͚U-&&&l۶ ښaÆ{K.ٳ@-{18eΝ ˗Ƭm?ںu+7###cS;w2eʔj/3'OYȨ6f>}6m~-k֬iӦ :TcyӳgO`̙oߞ3j(gFM?ˍ7xa!wT>VAywL srlnnGܹ\ֻwjUUYWv]ɓ'cW055eǠA4^v LƄ 4hGK&M?~<;wחjwJ(Wzեrisss]\ [f=zCq-ZTTr9Ĥ++? ~K͕֫W/~&$$7ߨ\ԆuѺuk  ˋf͚T6l>$66cmmdggo>LMM gرDFFҥKeVn?;w**> %::wwwtm Sn %%%rUi Luck+}e}Rm@eÆ S+eVؘ0>3LMMfz׿ .IjZ֯ݕ1m4>|Ȝ9sذavMw9r#Gp1-Zo><<}8|0#33իWSZZqUi/CXYYQPPx1O*:5k $--B )zLLL$$$$y^w.h.]|U*.'MDpp0[n̙3nݺOغuà%KWCAK.ۓɔ)SO ̌3gݻwS2=yD]ݻ'tHĥKXb+V7x X+Wҭ[7(**2{]֬Yŋ"==(DuFYl<|'NpA< ] M$8|Pס)}Wh&A/`ɒ%ѯ_?BBBtaƍxyyaii+PA TEU[z3qDT7}töm۪9Dze˴ƞٳg ɓ'tAyQ.5k|2&u…3qDڶmA[/}uؑ NXX ߯0 - _cBW\.g۷_ضKJJdddd|Xvy/Z]ŭ1vX߯1&MB$SRRd\rckDDD0a +~Z{ 4BCCke5QXX;ZꡠLĉ_v-2Ly\jwmK\ΡCpuuUWii)9A1}t.]\_VVFtt43fٙC|r|1̙3oiN:EPPgfΝ4lؐYfgn899/K]vi+tmPPϤA0ݻwٳ'of]"9 cǎeѢE:7mڔ;w࠲ݻ\rƍ?P͛7dlٲ###hذ W=ܹsyw_X k׮`3nIbɌ1BщL8?O,Zgr z-Y|25zٙx LLL16 ///.\HII fffjsNd2>>>e;vTI_h"@@@Fɓ5#}^^^XXX\_^^֭[9q? _p[rm|M G}T 1cCaÆl۶ ӧO3n8.]W1ӧOBǎYt)={T{4Ŷ|rYb2]v޽[mܺGGG.^/BY|9׮]#&&{!N-tKj?Q;v,Gʕ+*ĻvbҤI۷OxHvv6Zb̙*yzz?ORRRh۶->>>;zOum2-EEE*C\N< ɓiРIII%22/C a޼y2pvvM6ЦMZl<}+szꊹ9=%%%3geى|4nܘN:n:,--qpp̌ӧO+k$$$ϻŋmP4ի!٧q6 љʊ|uTe rCR=5۷os\\\LMM󉎎w̟?jСC9wʼ 2Dk>Mq\l̡Ch߾=Ğ={`zC4jԈɓ' &..>@%)666lذ3bIKKSٟ0,XCիFڃm~F;_|\-0:99аaCaȐ!r٣R%""ssslmm5geKLL ؐKddr {&++q䄹qe˖-cVm{QQUmms\`>jrB^ڵkYb˽z*;w8&//|LMMYx1=ZeӼyjwBuAwC?jYնO[)]_AE32Vj@&﯆jX̀@ -%htzAuO>  %''+Jӧ%I$) @%_qqdooHn?>Se.((+=x@ OjMWPڟ= IDATTo߾ҽ{t!i͚5ӥO*ׅIC˥1cHmmKI$I999TPPsƭq\k[-**.9rDcTN3g?Hyyyӧ%{{{)11Q$I:|4nܸjy?.1Bm~Fkڎg#FS_. >\*//PCکO[JS{JWmP?vײm[_ZM*]/[TV\W+ 4\뺞ub( "332w\fnnGܹ\ֻwjUUQ_UNKKC.3sL2sθUK6U-SX*31QӢE ޽{8s ;w67{j((k{4nsss$I|W|WcQm(rIx k|嗬]V%̘1uLݻsαw^4hqۊ):k:t灊6xJʢUV5瞧†|N '//3g(碪4}JME4] T.jr9ׯ_k׮k׮ZҺuk7nL|||֫'C+駟}vUPiӆ5kְk.T)ۨ)oU2b0`O՞Xm*ǭoBq)+++Fɓ'Tƍ`:wnRN&Cmgii)ׯ_ ₷%W^зo_7osMԕ,%z-.\HLL SNE.sҥj:uD y&-ZCʗՃp{7xCcDM(Q"CY2336lj'gƍX[[koll̀*ܹsG9֖-[r)z\_cе}N Gll,ϱL[ѧԄ>_AA1!B`ԨQ[ׯ#6l؀ךgڵ$''oi&4>>>޽LLL$$$$yLz]ʟǑ#GΦ˗/cnnR߿?saŊвeK\½{(**ҫ~<~ +ף4hׯwߥI&z: }MtKg&""B3+++Ǐ_hԕY9}ŋxzz2|p̙^^^?nݺ(',))e˖@ׯWv󫽺ųĢI&֭[9s TkkkƌC^^ӧO'""իWϟgĉg&Mʺuݻ7ᘚVG}b:t(/& @LW>uqu\\j֬#Cر# , <<0ޡC&OLRR7oޤAL6kbҥ@9͍իWcjj͛ cɒ%cccٳ7nPYլY36mDVVfffу͛7rJr9>>>*1̜9ym2@mFhh(:ubƍ*|$yfrrr#""BA agP{j;_[ޫA}?6(\fԣ\u+XiZ)lIt}aذa?^1'=Vʻ{.&L;&OEJKKCui;ƽg}MNG%Bן=(*+J/KJ充{+<~ &rIII]v"{N:&ڃ  ;1!BsFhh(uVw))CAA1DŽ u.??۷G˖-qwwW(xܺu GGǺAb1CK1!  bz} OןcBAAAC9ӑ  3iiiucx|K.o>jݶm`uA^oܸg}Fff^|$&&Pmݎ;GsШQ#v?U8y${/,,o߾lܸv(--U)`llLjj*s̡SN7W_A$x2`vZٴiS Xx899lӦM;[_gqqq*r9|nnn@ڠyuDDŽ +ݻٓ7|C^BGŅcDzh"6mΝ;uLܽ{+WиqPٵks+&M`nnv$IDEE1ydFkccc/_|ommM^pvv&''7xCk Ud111믿ظ`Xp!%%%+G}366I&*.\2mP<>#A1!Pr9v"66GѧO|||hӦ P1taʔ)?D~(//g˖-8qIpvv&11Pڵk5kpA;F.]ػwƲKKK ӧ899充lݺ'NP^^Uѕ… lݺ۷oo2d>juWPP3:t( 6d۶m>}qƱtRwĈ>}:vҥKٳ)˗cnnΊ+iwŵk֘ojUG8::rE~ڷo˹v111ܻwLwZh,]TZرc9rW\wUI&o>Bbb"ٴjՊ3gE'''B۶mw+O}k ܹǏ:uٳ0`ŋdggceeŐ!C7oB7͚538\]]177￧www̙̌ӧOɓ'pE6n֭[8/9$ݫדݻꫯTxе>44 .Lxx8EEE*h˟7Fرc\mYl|xzz2sLHJJR^hSAAA̟?hzϯMm :sΩ ϐ!C:e˖-|:tIzz:AAAٳ 6oެq1DFꫬ,^mmP<~F6(}Z$ITK*$;;;)==]>sO?UY삂o߾҃~giذaztOMM+ݻwO~xxxH֬Y#M>]zr]XX|o:tH\3ftj[qq4p@_$I#K:jܺHoܸ!I999eQQQ.=g=.: P4/(*^(]3P[UT4/_7d/FT&Zab&W 墀pQTǃ9s|>339K>ST*jbŋ*JڰaJR&LJLLsӦMS|UNNNgͺ̙3UNNNZ]rqqQ͘1CUQQRTT:rvvV͚5KRT*'''?GhhI}vJRT3f̨ *!!ART#GT999iΝ;UNNNrRo3vXk*55`~\rE_UUXXJMMUW3Fuuۏ;V{M_}I䤚4ikTRۧ3ffϞ:qDۗ&M/ܦPݿ+'G\ɺ4դIT Cc 6mڤѹuИ4}hu]-mnu;G[mvVvnw67Ԟor2C!Pvv6&&UK*++uQ볲f(̘1#G2m4 _~Ʌ 8zq:u6^M_l5'O2`6lm۶5Ꜫ3GW^@շO֚xOZ0T. Ѯ];^~evލ?Q]AA&))bj=z<ݺuRꃱY.ץKP* 6V>2M0wyG)8)))|ǜ?BT*)C c|'3Q_kcǎz2`*?u о}{x9~8j5i$}Y_ΡC ƆѣG6''p !]S[ݷ~~ (//'&&`SMӐcB!Tyy9JB\oR3?L=CDEE@oh}EE 755e˖-$&&rA|}}qvvfÆ m\\\r ~)^^^uKCL܆b7n;v`ɒ%2~xϩ>ydSCPyX~=nnntܹ6̝;777-ZD׮]Y` ¨ 7T*? Q~^oD3:'' bkkիիf{@3Hm۶Ғ\T*V9;sԤh.[o߾zgu&&&Ijj*qqq:E;v젤 M#KP燙׿≎'::33V=F}qwq L-ݑ#Gԩ...uncllhƾG&u%]Bф011ь?)))GңGZ隊-m۶%66AuS}ҫ999ok.bcciޭ[7سgOGUVVꍥf޸qևdz]FAvv6?S'V;qGa\ښ)SpQ<<܉'OR^^TMR4OHJJ#FBǎ_={ >>^_~E8&&&8::rM>3W?1a u煮yVKToiγ1xoڵ={߿?!!!$$$huJ9r۷qFlll.h>bR߿_~Ya:4MQtL!DbԩB~~>aaaX[[3l0y֭[Grr27n`ӦMZwzԪU+<<<'!!BNJJQ f(}QQ_~%6l0-ZĚ5kK.;wWRZZjTiӆd~G D39V|49#G$44kf(]͸ QCsеpB"##Zӈ.,,@7}c{`` +V|wݛÇͬYXz5ׯܜUe URj:sԝ}eРAddd0{lu> IDAT055exzzßgJJJ3aK߾}4;Hbq5._L`` BcA뒑APPO&''Lvѐ8֬Yڵk۷/jMƾORgɤ矹r 'O!u4M B&w*++:t(~~~lܸoooڷo'njjR$;;yzzbaaAXX׮]ښ9shih}ئL՗onݺEϞ=5͛??y牏W^ɉ{`k׎ &̍78p ۷ouֵјƏoft6 a\t1ZС#CkzGYl۷og˖- >1c10|kީϺu46got899Ç=z4vvvZ$!!!!!!joРAܹ[Fff&^[Ƙ1ciii+~J%;wf֭deeCSOyL+T=ĉY'Nwߥ]vtЁM69c͚;QvZJ%~~~Zϟϒ%K}5L]&&&23i$Mzu Ʀ1=1>ȌFW3|x]}3|ʇ)Q*T=E!E^^>,<<!.B{ĕ+W͛ٳ;믿04>!-1!bvE@@]tݝӧ7wX-Zff&iii6w(-!>6(P!%sL!B@ƮR?)1!B!Bqȡ(J !B0äR.c[1agg!!BsOP0.]D``fSSS.]ĢEO ֭[,_nxjBܛ222߿?s+zMttt`˖-;v۷o3zh0^Pm69B|R3ԩSl۶˗/#0n8.]Z+n޼'Ǐ‚;wpqOΊ+ɓ'sqRSSyGYbY^3m4kWWWN>ͯ=+W… pUYj:uj1uŠ/}CZ|r9y$tڕsB\z5Zػwolmmur=z4?t??? M_1Μ9CEEl޼8>>t֍رӧO5ƍcɒ%PRRƍʊ:>&&&7(mvv67of˖-_^k˗r cǎ,kݺ5cƌ!66VgC!1e$mDDD4w(ͮ! tJY[[T*Q*u+//+++ڷosSQ2C!38q9/4hquueǎ<si6nȶm(((sfϞ͔)SHHH ::ZCCCa޽|Gdggk5` ԩSlذ۷SZZEEE2uT+6l؀[sR(8::k8q" X ˋ3p@(..6 غu+˗/Aff&!!!|deeyft9 t_֭#,,}1ydIOO7*ЧO^{5٣sLkAWLusMlٲe˖q +4y{ ҥK| <7x}ȨQR'OΎqơT*50W&&&Ν;ӻwopL8?O_v;Ҷm[={s\|}QcHۮ]FΝoy饗pssǧÍ O$%%ѫW/ڴi\TRRRBRR֭c͚5nZ> !?}{qqqqM;iӆ۷o~pttΎPs߷n"&&a|wחo߾:c3TTT3о}{z]NJEHH%%%Yj*  tIӰ4cǎп={6{{{NJRRVS_^Suyݻ7ݻwg޼y(677g׮]&::)S@ZZ`Zh u 0kkkΝK^^1נ.cǎٙW_}_3gBZZ񸸸F`` '00HLLL8uP5 +66]g6l_:߸qz:PPP3M}QCHlݺÇciiIXXXsv5>TVVݻ5kVuGeԨQ,X,?ROE}P!dggSYY5Ғ'x+Wh 8V:BmƪtJ%H֭[F8;;3c Fɴi2dV_~%.\ѣ[u16qc8pwEGGkMB\ 666xzzJ\\\H=j2C!#vvvO?i)JRRRӧOzQ+]Sm۶6hsOz5'''|MvEllXnݺĞ={'@US_,5ƍAՒݻԗהZx",[^zaeeEZZZsoff#Xv-gϞ$!!OHH GGGt54IT6򆔑3}tDGG3~kL}:r۷ogƍuٱcGQT:cЗה͛|grq= 0!##;vpEƍgZS]5j4tM?GGG_`cc /@QQ#22w}fF߾}4h̞=vUnSSSΝKDDqqqki ƾOҥKyǘ5k/"666,Zú댭O5i\\k֬aڵۗRJKK5w`eddӧ!33(bcc~!RO[6!___}SYYСC 7x7nۛ3aCR$;;oHj‚0]5sLZeh}ئL՘՗onݺEϞ=Y~aϏ<+{g0۵kDŽ ƍ 8۷ӺuFc]`̙qeBBBj`(}C)]l2oΖ-[>|8cƌJ/VmrnӦ c۶m ]Kbҵ}נ1ׄ ^^^l۶'K.]8|0{ΎRprrӓ?Ç3zh:hBBB "11 BBB y״b N8y5qD}ׁ^z J͛ɉ:gO˗/ҒOڵkQ*ik,YvѡC6mDNN׏͛7$qK=m b3յޤroSocZ-߭[J:UsBR^^>,O<</_G}t'(H1!➕AyG;q\x,133t xyy5>kժUtRҥK9s }=1ϱcС6l`ɤh65T*9s&'Of„ :{n,{GD})22___ @Νܹs3GuTZZJvԹԔ+Wj^0` C:&BԛRdϞ=:t||I֭PuY8tϟ'$$CP(غu+GARɓ'GZ ?૯wDGG׹ l±cǸ}6G+++ ۶mȑ#( yx ?u۶m<#7Kʻ7o㱰`Ν?~ӧb w?~T}QVXAuRb_|9ͯ=+W… pUYj:uR\\?LDDDsT}:%꒗UwuƇ~䡇ҙoHy:TgvvvѦMkӟDϞ=uU݀ӧ*&Mre5evMnضm+VСCnZklҥK| <7xӧO3j(ڶmѣGv~!׮]s|lݺ|z)d OP1W_ӟo߾Z)JHKKc˖-YF&BM&BQ/DEEO>}޽;ܺu8vӦMGGGڴi۷ٿ?8::bggG@@@b[nذxb;ٷo_Ju***xgh߾={uJ"$$֬Y,^ IDAT]j ooo:uiR -Ϻݛݻ3o;ܸq|BCCm{:te˖iӦ:'Sif̟?{{{:u*IIIc4E+**"++ӧOuV>%aaa=! ^O>DDDЪU+-Zč7;z(Fbdee9H=1!^oiiO<+W4X+BJgjڰNOOGT2|LLLoioݺe,J33f`ȑL6!Ch_r=^cW:ց^~ھY>LRRdeeVVVxxx0sL∎fܹ޽JX˼[nXZZRRRb5u=zSҒ˗h=!!ggg4꧓sy QTo!ChժOBPPӧOSN̙3О6 |ԨQU;;vЫ1())|@IIh~?0 P ̑ݛZӾ}{x9~VIxg~:"44FsROHDŽBz*//GTP(>LmpJuQ2s,zc3Ԕ-[ٙ 6hqqqʕ+|z'^lhի޾ӥsƢEڵ+ ,@Pk?Z WWWf̘Ǚ2e Pw94c_իW~z^~e2228uÇלې!C4Clmma…ڲzjzĉ5ۨh۶m_ݻyfe^^^Z c/y Z޳gOߚv )GuF۶mv@U'z S֭۷ǁ`cc'5=@i C9Bԋ&&&OeJc{Q+]Sm۶u7^9'o&v"66VlwĞ={H@UW_,5ƍAՒݻθmŋ)((`ٲe +++ {333y,,,.1[f,c]}_333/^ MP*888`ff/BNٳܜ$1b...tQyS^^/'33dƏߨm 155eĈ?~\k+W44h^L>`}6эdMUJ%9͛qRO<1!^:u*OXX 6Lou֑̍7شi6~~~ݻ1j IHH@PᅮbzCJ_TTė_~Inn.eee$&&biii0 6Ef233ҥ Νիmڴ!99B"##)))Mg>۾+vkkkn޼g}FaaCP58;;JU+?@NNر/2n8+ՇPǏ#/^yɻヒ|bbb @3lo߾ 4 fϞ?vyo>Ν;ǵk׈ˋ hf7vٳg~Onn.ǎz'c뱸7-]{Yf/bccâE;{1f=]~=qqq\v˗/B<4## N>MNNDEEkә zB!חTVV2tPõnϏ7M5fLMMQ*dggz <== ,,k׮amm͜9s4bZ_3)Sh5/..o!""[nѳgO֯_s(ļy'?<+899{v1aqdnZg>۾1+e˖}vl3fV3g˗ ъU=Amڴ_~l۶M󍜡reW\&&&xyy믳m6&N/]tݻ;;;J OOO>s>ѣQ󐘘!!!}6.\>GGG5bn+OVX'4Ý&Ny3ׯgΝDDDƍ쎉cq133c嚹ZZO5Rslݺ,z!z)"##5CfڵkGشi999ӯ_?6oެQ !ǝۯ&Ֆx]}jԯ?fnT*թzB;$//g}xPQ3mڴ;0AvӍz+ >(tQz311 o+_S5Ֆ|wug}P!&55=z

 vEK`!d !B@'e9&B!B!9#33RixC!B&==CwOej2hvvvB!=GnI Ńev!B! B!B4B@1!B!S[CIDŽB!B!tL!B!HDŽBqvv&++it\jJPƎ;/_Zk ٙ 2:+{nQ}}W,[I&1j(fΜaԨQL8)J8 8sֺJߏ'nnn?+WkmWVVFXX=&M6 yqwwgȑxxxx]p///x爌lErr2:'NdܸqYol=~ϝ;oo;v"*B܏vIVVns_|ٳgO155}Z~i"""P*Zi?u?nnngϞ_~S+?#B/qqqmGGGJ%iii\tɏw/JHHޣBAfjjʥKXh\tO>˗Gi{wu[nP|}} ̙3#GoceeŅ 6:>JJJbժU^zB233;zٳgC^^֭#00M6wРA|wZJ%/a+O:&B$222߿?hH|}}:t(;wsתU+>cM篝osssiCۮ];tcީSBѢDGGs8p ~~~tܙpNI&ѩS'Znaaa۷ɓ'HzzzԩSlذ۷SZZ>44G}DvvTTT/SN嫯bÆ Zw ʎ;x'`L8UW@7'N`DEEannŋ-[l284 fϞ͔)SHHH)6l051bC._۷y'̓#}ea333)--_~tQ繩wѣG‚'ObggǸqP*|ᇚbcc;vhͥzjbbbܹ3l۶VY۷GGGFE||>>2m4ר8 غu+˗/Aff&!!!|deeyfMꮸ]FΝoy饗pssG!))^zѦMic?{:(ԤcB!DP(صk+W_~0qD7/2{{̛7[[[u}#vvvhߺu|||Ɔŋk^~ yڷoO޽߿1M;u~o\򠼼(ӧݻw'00[nT5(0`̝;z~9wJ2.\СC`С3nŋ4:ҵkW{W6鴴4qqq!,,@\]]IMM+=zQF`Ӈwy7nOhh(vvvܟW_СC,[M6՚>tf̟?{{{:u*III8 ]qӧٺu+Ғٕ{nf͚Ukzژ}g<3tҥm 5!EΦ0>|$ʪ)YYY( I⚞RdUߺu 1c#Gdڴi 2D9K_dggSYYҒ'x+W_nݰ4 }}6iii\~~CUY~,YD Ceacճ @sG'sy QT;v 0`PuBռC Ur'Nĉ2KHHYkrT&Mij>9t0zhLMM !((ӧөS'̙PgQFTYOLLرc:,5ݫW/jzRM5nLw?BGټpB<==9Ρic U111ܗA1!y&Pujoj,ܹsqsscѢEtڕ P(tB3[@ЎGfjjʖ-[HLL̆ uҗ(J VZsssMYucX 4D#Gwի$&&6 }QHKK˚φ眜.\-WW^gWw>ִm۶4s2dHa?FX&&&Ijj*qqqO5fyyy ROƧQٳ'M{uկ;UwEt֍mr5<<2-Utt4=ѵދȑ#tIӑR[!E޽;J/ZwE Xlzʊ:ngg ?mۖX1999ok.bcc5xu3<ЕVTB>} UO?4gϞ/֩S' Ν;ر#ݻwo瑱eaH6mx饗P(Z:jJJJ#F\k(//Z888`ff/BNٳG_GӝbnnwRL?~1Ǐk-r&?]q瘛3}tDGGy=Galܸyym,B"tЁ1cư~z)))!..Ns͛7(,,4~?Ϗ{U<[dnܸy;T}Axx8 ( ~wĎEEE|RVVFbb"/ҥ ΝիaaaX[[3lذ{Ԇ ɓ'wZ\]]i1k摡O^^^d(Bclذ PQQ#}Geٲel߾-[0|pƌIT*vϏ7M2eVG'q53gwqQU? "b.R367zD%TR4Sq'PwCP@?l0s!\ps{g9޽{l~C1l0 +s\KKKXhSN;a'^ S}T*-*Rŏї_~-{5Xf k׮%))/bff})qrrbԨQlܸ۷ꊝ4k48q5kɖ-[* }v֬Y-[D$tZkY?xży󰲲VZ,\tiѢ/Ç:u+V#Z+:rJ"##qpp <<\ C=.cPTɓ3j5ӦM>zh>ETW4Ɔ ۷/VVV^7]so|u=oRd槩1-oͣ?]$I%AAxJmܸ >ot>/_޾#4ڸoЍM>LLL\{@!PPERoMm^meyTAAL5-4_Z ?ALAAxl޼xx =ʛokV١ B AALZx!7nN: 6?r-P_S9Ag AAL=ѣGe!3g;R2e O~~> .Ã^z1o##(&NȦMhժӧO0gFFK,alڴƍիW aڵxb Κ5kIKK+VVZѬY3}V^͛7K믿زe &LYgxWjU exxx0k,.__f [ne|ۗڵk+:ܾ}^xKKK;ILL߿Ou###9|0aaa,[N_ŝիWxyyh".]d222z*o&iii̛7D]F@@ 4WWW,,,7VIIIѳgOj5k׮eʕZoݺh,,,OCBB]ueff~^z%/Kx";wטz㴟1EE$++U2eT*gΑ#GJ f}v>36nĉ0a֚+l۶ JE޽/,ݻGVV_~[n)NS\Ydff'۶m#,, 777=GV0$''YfR4h111Z7jlN<_|~ZbP!'OI&ױFZuҙxxT3ߏj?jZ P@KMIÇΝ;KOeggKnnnݻ%IÇKg/77Wrqq/''Grrr^Z%.mܸQ~>--Mrrrqٳty[ll%I$ݿ_j߾t _UӧOի޽{K:tfΜ)?^~~uAIyÇK,SK?V۾@RTҩStH$͜9S :w4tP)66Vo΁|PX%J%eff|>))Irrrf̘!o䇷$ItyI?~vڴite:99i$IG<۷o/͜9S{tI1b>,,Lrrr/I$uUrrr $I-[HNNN҄ $IÇKNNNR@@UJMMi?qℼƍRJ>,f\I$iٲepB9McR9? ŮJk޽i9/tIII1FGRrrֶ/JNNNtQ)33Sڽ{"%%%g…Ҕ)SJlWʕ+z_222.ݻwO:4uT{ҭ[tUXX(y{{KӦMnݺ%ݹsG   VxԾ}{ڵk:Hq`$ÇBԩS[o%}וVPOm&}J֭+s>999R߾}-[6o, 4D۷K:y__6Z׾Ue"њju溻J)憮˝XcBsiiihK`aa믿NJJu%+,,O߭J֨Q#,,,Vg5óE/_Zf킂 _jE_?WBc,-?@X՚KnnnT*ݻKє8O~È#pssϏ 0f u}- UCwOOO&O\SrӇyϾ}xwu敟||ܶ# ''' Tj:LMM9wiiiу=zѡCOOÆ dbb ...xzz/ϗgڵkn:طovbϞ=;==cҰaC>4is55 w~'3d\Çqqq1ynРPOiYi.[jޞCHcj׮-bh\z[ 9oqqqsVZŁJ?/'I=oF$~wbbb۷oe07774Wi}4;v`ٲeS^=_揦}:>9𜳴ӓ3w\4h+-No_|9s_>_}ViӦѪU+|||8133LJׯ3oΝ;4uKBB]vΎ8{,3g,lllHKKC$LLLJ]x gkkի?3}}jժE &((ur1,oJSSSzٳQT|:.jO?K.$IիW/ц'N~ԩSxyyѢE ߀}]^= ȑ#qww֭[ȧ~yWfffTZSSSF!~WYr|why}#&A E1uT ر#GMFxx8X[[LMMQդiK_q3j(UFhh(ׯ____ZlY 5iWN-XtO.tZ9}L8e˖ ݻwWTO>0ƌC~~>+:0΁>}n:f̘7c~e .IIIeW(֯_DQIII\x333^~eƍǀ_~}"Ύ-[WC… $&&Jooo9wUT^ۛ={Ϟ= d޼yZ{yyqu|ȩSXbyyy8::u$b޽DFF gVVVԪU 9-Z`S%i/ }}Y> ZʹiӴҎ=>#I/Ν;899kHB'O _]]STE_1|F5?kIt uA;wЧO:腽Au2M>ڸo8B<“Cg p( TC*M)Rߋ2@1!B9y9qDY,s4ܻwsxyyEvvrss~|U?(7oj[oE߾}3gt y:;;8|[h;w{c۶mVQlڴ޽{sQo+y]˯ A [r%̛7<̄ ȑ#èQRh޼Uե? HTTTi֭wބ=//CtҊ B\x$uV⹯+~^׹UVիWYf̙3ptt|i߾=%bҘ;w.<`ɒ%dff`Йg6mؿ6ZqwwWFx]١TdK{iSҷF.b`BA0XYYaeeE~bժU&ƏE."iʔ)jԨQ)JPP?3;w9rZlYѕ]͚5)10qN8Q򤜗^R ߽{]vsNjժ@XX\pAܷ4> @=o999$Sb IDATXwF)Ni6!S9A0j ///wΤIHOOaΝL8JErr2DFFoMXX 55Ug9үY+W@ǎ .:t(˗/W\6mڐ!~^Z]N9s&g端" |8p ;wKꑟ… W^̛7O-EU^URZ5EyrJƌJb9sowyJŔ)S;5;vd߾}Zuٻw/={TT.Z={VkØ+iʃE^wyEj*ƎJۛ'Oj=9/K;߲֭V%%%Kn۷/Ǐ'33@cǎFʕ+ԪUKp[.[Y~= jժF1;|0>>>t҅8frGLLLSSݗzJoz(NSAC L >>=www>}־̘1GGG}u}6/zR;ZO޽Z*vvv~z<<<5k/_[ Κ5kIKK+6$I"--M133SgFFK,alڴƍիW aڵxby}炱uݻ,,,$11^z)*]IU^oooi7od SEry/^õ+^\\\(,,?y&']v`Ȑ!4mڔ_|#GҰaCN>]j,< >>)S`kkKz7n\L_gԩ󴱱!00-[bkk+ѣGӸqc7n\OBYR>+Ǐf͚hr )kFC/Ν 66OOO)&VZakkˈ#sW^UTOcJIIm2aONXXnnn cǎe̘1Uրo ???n߾ @ƍi֬sܽ{ `ggs!O];tB+MYϻ[nO.]iӦO٧~JRRc֬Ylذozf(ۆ^#TkL vŞ={OV+7m/ִxpvAڵi߾cQhYDxk Ø5D?}> O;;;LLL_mj3g_K/Tb?}/(((QFzjkq/ʜ\pN:Y`ĉ4iKKK.\P Pz4lؐ5jp0a'N_y\0n*wa,-->Y7?8sz۷YpViӦfׯω'v999Zu ???>Sy}˛)zb٨T*y֖wq=v <<)fffArr2ܸq3gC a…(ʳ M ,(RI666!Io7oڵk1o<:cǎ%::ƍ}<˪2J;f͚凮u6yXt){C^yyۼ< Lbb"ׯ_ҥK̚5B@ׯĉ\~0~xƌCڵ~#%%RWXlٲ7oDZƦWnn.ϟgȑ+&S~o+yx>TA'L@@-bԩбcG""" ~:8m4Z~`jjZ&--z)Jos! _~Vy#G~cڴi\DO>[3f^~e&NȲeˈŅݻkFZjrulmme˖:.cƌa֭lذw}Wo2t.nEՋ}Pq%BCC裏5kÆ _]񂑥)Uڵӛ}41e;ߕڲc*8zޤvOSMcZd?ߚGg-I. O;wЧO:vY |rr-    r*իWQՆ  ߟ><͑$Xwފ6O- Zիٺu+w7`ڴi4jxt[uV~WBBBر#,Y;v Innn$%%K/UFPP7n`۶m4mڔR'**]vC\]]󅅅,];vPXX(#0ÇYt).]^gϞL0ı>FW^TV+W{n _vy嗙>}:-[|'""$n޼I =z4ok֬ ZC]V)ZcJs>>>xyyѿ@Y?puuȑ#?_|iӦѦME_3tʯ~f)۷ѣ./^Lbb"YYYk׎)S);;pY&}owaرܽ{޽{W_N\\aaaT*e$%%b OjԨ'88Xۛ*U̍77r]-Zsݺuܜݻwγo΍7ppp`رtY[n G^^^^^l3}lmm9wVVZXx1QQQҥKУGy[ժU޽;ӓ+WPV-yPnݺnݚARO?Ӹqcs)v|Ԯ]P-<6oތ  hڢVi&&&DDD)O%TIʼn~*h hCpp03n8B`̙T*/_o@DD?,]Rp9s&Ç_~$''7 Κ5kIKKGlH>LXX˖-#''G+}gff'۶m#,, 77u*,,dƌ8::ѣqww'99Y^A alذ֭[3~xJm|rrr8TZ;;;BCCY~=̚5˗/ תU+5kիKw}Rڎ)##(&NȦMhժӧOjG]R3T;G䧫rEv @v1cׯёnݺq!O>!>>ubooϲeʼ^2]F@@ 4WWW,,,z*sիWxyyhѢSS-Sso&Ν㣏>_~Jρ###˗s2ؾ};}7ndĉL0AkZf ;<_ 5jnoo/gݻVPP/s |Ȇ X|iW`` ۷o/s ~lݺ[[[ƍә>}X~}֭[ӥKu^`HDHHߏ?XߟsNRԆ ذaֶhⱨ!Cȿ9͛7siKPLJTrYj}_5QQQ___vaȑj #FqF^*Oq.SiJk>}(ʯx?ӧ><}\pCѱcGBCCGٳG>svZyw}'(?^x71bQQQ㏸ЫWe(ԬYZj@ƍi֬s% SSS-[әi >|8 =Z>===ٴiP.>>x{{H\\#F &&JI;j(mQFYWCi~>߿xt4ud:On߾ @ tJU Ce4okkkK=pqqaȑ)h߾=Ol߾{>T*e%/Vԩ||zܹsKacc#`XiJ͚57on:Vʲe8p ˗/iӦִ }llltu}Ԕ4hk@Ef/;;[Ae“̙3ܿ3|6lXTIScT10!B9CVSXX\ŤQV= Qͨ~ll7džMMMO'pvv&,,LNӾ}{RRRXnE=UT)Usrr1bnnnѠAƌCaaaĤ4OH 777T*Ce0Tt@)CR(OfȐ!\rÇ"ߡCww4lP^ˡº6gP[8Ǯ]سg111e111<==_Ɩ4ʹ}ÇhwQ0z*[n%!!:u0o <6;;;LLL7jΜ9#ҮK/TbҰaCjԨz^Wʲ9VZ05jDPPW&11Qk_?޾}[^T4mTo|J={ &NH&M…  Ť4O}H*U /PZri+}iNEkcSJ*7 VqppJ*;wڵkcoo/?ͱСCp)|5.]ʀG '~GZرc.⋮gϙ3gЬ RN.SsE988HTL9ŵȔ IDAT^}Uy:g}ƱcINN&99-[Brrj_ <;;wի>}Tǥ O@GHH$$$<;v`ٲeS^ί,EgYZZ9s w%44[[[:uw~/ӧOsm.\fڴiY1CDDr b3ff&[l͛rq,,,ъSN駟rUׯω'v999fQ{MvvfƆ4$I֖wq=44+fC1)S1*ZLzz:W\a={={>V[)mG9cL?0:vj%йիW/?ؼy3cdff2rH7o3gmݺ5W\a̜9X<-,,h޼99992~x;?f 233AR‖i8^5o_HR""5pB+5L,KpR/f!9EPP?9 <灳k}kvJJXΝ㭷"""UVgM6ꤼ:t???ϤI5j9XLc֪U+dȑ)VVV?mڴy̞='Np5oݻw*]o3f $%%qBCC;bcc9z(/_&11)S0au{U.,))`ڴiN\zeSTVWvBjG}̙3)..k׮DEEM=00HUmnnF!33ʽƍvڄse=zzkec8p΅ػw/ܺu '''⡬coȧ~o1b|Foz۷/\~:zjjժUKHHϟ'<PvPosh|>-333L´iXj^^^аaCv ptt]v(K.%$$GR^= u]p!akkKPP-R*u5b̙7Ç̙3$%%NNN©S^W(jN2OY/V{ڷoϻᆱdVZ#@gΜI^^L8Cz3l0EadeeJLLZ?ܹYv-_Ws[<<=='5,XFC``ǫQΚ5K^^^,ZJԲWdZ֛Zi}4楶Ӿ׾,iڟE9X}B<@YYY׏JLJAt(1'٣|>K`` 'Ndĉ5c… F#mq _p|_pD>܁@ P\5^J=SjYJKӷ2+Mr!*OYf)!B!Br!jTZZ.\Օ,5jTM%B!x@cBQO aÆTjB!&sL!B@Ʈ? ?>d !B!BʑF1P! s…Agr}r_OOdDŽcM BБ[r}rB!B!CA:&B!tEM xBIDŽB!B!jtL!B!HDŽB!B!jtL!O3dbccMZn pssk׮x{{B̜9!Cꫯ͂ xyܾ}7oV(׫l4{FLL=yXi4nJ߾}駟 -..f͌7OOOÜ9srN'N0m4ݻ7AAAdggfΜYaԨxd\z´SS۲U%B!ɓL8KntyU$''sN̙C||<^`9tbmm?yуg]sN}s쏲|}}ٸqI={I&_pBnݺ4ӻwo֮]Kxx8gϞe…sWc*55Fܹ0&NȺu]6&LΝ;zӛNMI#ĽBq}Ջ!C0m4˫VZX[[cmm_F={5tlONXh>.'';;j)'==ӧOC||( fff$&&oVҥ %%%ݻa% ˵k*ҥKK/ѰaCyϞ=Cѽ{w֭KBB/_>W^jD=h۶ζvnݺtԉk׮gۧ11o<yhԨb0% prrhW\! zCr^-G4F۷9s +V`jرclْ:u]ʕ+ٰa5"""???FAXX,_{RKrtť_~E]hHMMUvӬYrrIrrr:u*-[Ɔ3g3_}U233/bpGEWp[{VَWWW'իWiccÐ!C(,,d޼y:i u:;;caaSI}i;w1۝?өS'>bccػw/:Lj٧N҉41%g}w}8FFF$++ridzzj"##uT'Sxh'վѣ O]...4lP1}YӪ,iO&!BG666x{{|@FXv-+{7Xx1> 6O?IH7o_ӧO~g UaeeEnXt):uR;ʖU\\^x߾}Çs&O K/Ķm޽;Ew~^ylmm9r6ʖ7uT~7??<~P]נA̖-[;v,^^^\v[n@6m[n7NlgL˖-iҤ 9s:vȱc5jZҹ o,ME9ܹ3uUhݺuSsuE6nHϞ=qrrDYvMRR'446mPPP424I/!3(6ls=NDe۩m^H;}IDŽBqG1sLڵ+QQQ:Hdd$ҷo_?L-Z0uTV^͊+pwwgϞ{>}1c겲ekƪwԯ_G,\#FдiSHOOޭw^uNNN,Y+++խ[ 6矓ٳgEL<ihذ!;v`Æ 8::Ү];#Ç̙3$%%:C]"ݺu㫯b޽FHHGvڄtRmLISC@||ݻw1!O.\S\ģ90 HTǟ;p(KK)Ҿ{J-+^){iUf}B!#4k~{~t"B!ocB!xH͛7pь5ưsj"BQcB!xH'DÆ aСtΜ9+S!OcB!B2vI ''sL!B!B<@OPt4B!O .t>ss272x";&k:!B"ss(GOfwB!B! 1!B!Ä/j:!J:&B!BQccB!B!5F:&B!A/_5BqϞȧr!h >>;;;.]J˖-HمGf)/???|||4hr4 uԹ2 1bg̘1 Cx?U5((r{Œ%KϪ={o߹u :Cnlذs3Ю];&OLӦMx<8}t۷vڅ ݻwZo}.EEE^rrrhݺ5?pssIϦMȠcǎ̞= w֭[ٱciiiXXXƴihذsmٻw/N@@@m.##z`<==與EHHلt(5ĉY'ORRRB׮]1c:Li_=z'ꍡ[n|喛Me>#C:&l۶ÇW_annmMT>c222Xh}+Ãm޷k:~Æ wYfiyO|uυ r&O̴i(**ԩSԫW}3ydRSSqpp}c K.5[nݰRߛSNZn p_; Zz5cooO\\|7<ܹ0i޼9+Wd„ l޼ڵkcnnٳg4iΜ={/ӧ矫eG>[nrJrssYt)DEEK( ܹs,Q#RSSIHHs5J(..&88QF1c Xx1 .dٲej:Sڗ4;vd:k4 Lx !&/^]v<35#mʔ)5#ʪN{= >|/u]\\)߇ڵkIMMO>,\+++E!&&5kְh"6oެA̛77ׯ$''3b^~eM޽{9y1vZMk@pp0$$$퍹9sQlР۷ӓ,~i)+;;]vsNׯ@DD ̙3͛)((xL'Opj%_|wG;::ϻKaafJ2-sA]{"BǞFal߾l^z%iҤ p#G}v~Wڵ+%%%\xEӓd  o~UVlڴ¼XbvΝ;lll/))aժUSRRӠelj*Ο?3}]61wfС̚5ˤ;`vӧiѢf͢]vzWa~~~xxxp!~w7oΜ9s8~8qqq{aoo_2`hԩӧO;~ӧO'**d^JF?~uޝ}bmm;}(,,LJI&|nwwֻX U3$//\ZhIKKӹ(U={$11Qo@VV666fUŋԯ__xgС?NDff&˗/gŊ,Y>W3XQmۆ+OlvJh988hh4im966SV-5eeI;}B^LL {aɒ%lܸ+++&OLQQfܹxxxf^z%8tZ2221w\FIIIaӦM^t).]bÆ |dffjll}tt4$""իWSPPsss ۛ~cKJJ={6...ϏEJJ f21e6oLO} IDAT2e yyy&V\ٺu+͛7Ϗt`:KƶJwjժ#2`.\ȅ L^輴cܾ}//~-[n}̚5K=Ǝ꫱`iѮ];ϼֺu_y嗹|2-"))I]?{lbccqqq{8pGbb"f~r/Z k.21T֤Iرc ,oaԩ;4o;!],'''^LѐϱcXx1W/L*7ni/\vM] /`t+dg^1nܸs1Zl2}U ^t70ψ>cBX+,,dƍ̝;֭[ӴiS.\ȭ[t." ...ԩS;wyfpqqё*P6[nNDՠA&O4 c3k(**^֖VZ@Qg||{=:vH ޞ;wt ]v8880j(ƏOi޼9;vLg{)USy7iժM6eر4nܘ'N'ܽ%[n:Ccu%##EQo*booϬYxqpp`̘1deenx[o_ع;\QF:6m"44Tm#9sйsgYp!>}\_ʕ+}ׯ_穧*]Y@ݙ0adggW)͛Ӻuk>_Nvv6K.ё7nk~wϕ 23VTh&MT#O۷on:F.3}U ~׼k6>#ʒv蓡BZff&:Y[[/.СCJJJt+{k}4 Ǐ ޭ[LZa06c;;;֭ *7߷~IHH08㫍Ck*uW@>$??_tvv&??_g{-Z*Î;8vyyydddpme|)Fa+]O4i5FW˖- ٳgXZ΅v?\w}VV.GGG;Rivvvqk߾=wYaIEL.\իWꫯUWfȐ!YVZaggwn޼YnO~v۷ogҥ4hЀ=zT*-sss aС3zhOz%aLe>L+557o^ӡcEںu+GQLbOlŦMٴirSWe`||йo᧟~ӹIOOW2HIIPuvv‚SNaooOPŠ߲=:TŅ  h4۷m+++u{ɧtN87Y`&%%ڵkGXX)))UꔨXQt;ѣGNxV^MdddVSWe͛y7M۔.N1!xi/8BCCIMM%;;pxW noxbN8u Ȇ *%~~~DEEBII W\!55դb3}nn.~-W^9rkkkt|W4i'== rQ L:uĉܸqlz 1VE\ m_:988pMknܸ>]SZY3VW0m2rHbcc9w/^$>>\>Ǝ)Wq?A9x 1b!!!T{bffinР&77câE;wJfʕԫWchӦ ͛7gٜ8qk׮7|C޽B3hr XZZŋp!.]Dzz:7n$11Qܔ|yѣ\|DL„ ΓRL~IJJb,X6mڨ"k'6}~70`x2)JGr!{|G̜9bvJTT@"##֖}wXk4233} aqQvmù|2=Z] 8PbyyyݻhnݺK,-رc O?71WWW>CǷ^zۗP_NXz5jպXCe݋퐐Ο?OXXXm_:M:իWb ٳ6bk\]vb iٲ%C5LczcXw|Y~cǎѴiSɓˍ^4mڔ~['BO 6dǎlذGGGڵk(F2NҥKٲe _=Zj؜9s&yyy8::2qD0l0EadeeJLL~իGYl.]ʊ˗e;w8~8k׮L h Y>~x~mx25 ܽԫW\,7Be4fky_:y/?Zg}EQVb_;f_~8p@pU>>> 4)&bݻOu֚]p''CGq _p|_pD>܁@ P\5^J=SjYJKӷ2+Mr!N>MfͤSB!Bj$C9iii\pWWWVǐ !B!tL!DO aÆ<^!BacB!B2vI ''sL!B!B<@OPt4B!O .t>ss272x";&k:!B"ss(GOfwB!B! 1!B!Ä/j:!J:&B!BQccB!B!5F:&BP|2/_tB!x=OBG׉7YYY888/3sLlmmLJAU)X<5XAAA$$$`iiI2d#F1bg̘1zN\\͚5R/O3bz-&''m6Ν;իWyy?~<͛77۷oS\\αӧٹs'F]vacc7o}޽;jƦekk 'NSN&(OE0??7{n233[.Zbرth9[o&::{( PNt7n࣏>رc\zggg&Nk'Xf 'O]2c :bjYeUGBvv65Jl୷"88OOOulڴ :vٳiРGeĉz֭~X hضm+W>0N< !xBw߱tRڶmKzx*cذa߿QFj*֭[w_m۶]vqa/#GgϞ Q ϡCkkk~,,,L.ߧG={N:nݚ֭[֗zI^R)Ϫз3fCNN;v䩧ȑ#W9ߪdʕ&;;#GRvm}>3 ^L޽Yv-={ VlJMMU;D㊢̝;wʭ۹s'aaaL8uQvm&Lر#yݻj씔|}}ٸq#ٕ< Q$wL!#ɓdddi&b_>*Tգ^z 8<>ƍwʜ2e}[O>:pQիmlONXh>.'';;wY_ҙޣ~h4y~'O˖-%{q)~gXr%fffPvKvv6vbΝԯ_ 3gpqq`ݺunۢE wKKK GGGyw),,ʪţK'OpjDe͛)((ۦ֮]˴iԻ&!!ooo}qA R(lܸ___ @߾}M?%wL!jFO?LJ={2m4.]cΝL:RRR())!::o[~~~|'L8|}}9vX㏙0a5TKz-<<<1cmXl O>,Z s믿2i$P1%Cɐ!CxW~پsi&|M<==˝ C@;VQܹs O?Uc۶m:o 8_ΕݢOSTTw5 +VPY.)((( <<ooozɌ3/=Dח]-_ԋ̾}zrizm4֩l2 +,sss:w 7C w=ݻӿLBnn.ٸ1vX5mdd$nnn$&&+rGsimm:7\x>,:tT}w0h44M*:.*Ϗ^{m0&Q=mۆ+5J2gff|rfϞ]ϓPV-z3L+66SV-1*̌(N}1!Q111ٳ%KqF[ҼysHOO',,> /_tR.]Ć $**Jo۷ug\z\|;z2cJ<ʫUˀXp!.\C߹߰a۷o'88/cooo1<3:s+P"))I矾W-Ott4$""իWSPP`3cYǎ׻ҥKҶm[6lXњ={6н{w8@@@F=z`^qqq$&&HNNk֬bԼk׮Mrr2Fg}ltߴΞ=Ν;9& A@@.]bСkk*ͱR~ ԩS'NG}TnIuCqyyyb~]@nn.yyyԪU3fA5chٲ:,ǔʪ˪{nn.x{{?Qn켨~׻Uv/Bm._uYo>h砱BکwX|Uϗ PW=@C hQ;w(r uY~~޽[QE5j۷www WWW%==\95j7ߨ333WWWܹs&Yz'O*JVVlƍ(rMsʕ+Wү_ ׭[+}Ut̝;W9}~ԨQJll}0QF)qqqL|8ݺucРA4,557o^ӡd !Dqtť_~E]hHMMun׬YrR!7nݺ'2‚gyƤc'OԩSiٲ%6669sF{}6mFֵOL)u͍9™3gxWLΫ{9W%%%Db3Ɔ!CPXXȼyt 0 N:=NNNK{ .}1n߾ͫJΝ;>RYXX0yd-[F1i}Y}]=z4Çcmm ܝ@N{<ϝ;WϢ"RSSuΙOIOOĉݻuҥѼyse^H4hPʺeW+?OHLLp\wI^0zABB)a/XÇ?BJJ ڵ#,,5k 6T݁ۧw.͛7o2,S~>GSΕ>sy/^(PPPL:{Ǐĉ>}- 0x`rss;v,111,Zsyi@`2O0ƔxűtR;2fʕzL>}pqqܹsl۶~;wzVZŞ={hݺ5ִmۖF͔)St,/}y!߿?&MbԨQo>sѣG|2L2 &CF͛ټy3W^e׮]رÇi?> ,M6j]-=Q)eUoիܾ}#G`mm]#O3hr ]o1`ymi!Q|G̜9bvJTT@"##V}9Lo q2n8j׮Mxx8/_ѣGӮ]rRN:J0$$>,,*EL:իWb ٳI1o<"""0aEEEЦMjԁ~W_1{ly1vINNV(;^ᆪaÆEݺu'$''sY,--iѢ'Of:߰aCv ptt]v(Ç̙3$%%GO,Svuueܸq|7ر=zӡ3uT.]ʖ-[_R333L´iXj^^^u5b̙7 .$884lmm bѢEƣo?%%%SNaaa /)͝;w8~8k׮]d>cqvv&22R箅 h v&ﳾ:ʾWyyyݻhnݺK,]Zxzz>OX:>l0EadeeJLLLGn޼S^ryk5k wq9?GJ̨iB֗S\iZ)F?+r"xdeeѯ_?8`ʦB' L&CÄ/8V8!gffJR?5)RJ)RʾWJ^q4!x,>}f͚PB!B޽EU0D  uȴy-ǔ0 aA@?EA=jf) ^"&e>e(`ƒ 3f73r~53{ڗוּvP"jrssqu 0ňY,=5 &E*//ǎ;]WMODDDDDMsL]o x[>^֏sL569#//r\B"""6]jd-ae[6pqqi"vox[>^Cjf8DDDDD~h"Q5&00ADDDH QXX ""2k L0UUU2???|FsYxyyaʕiӰsNi(:允W^… ,QJ/R)'Nlw%ɓ'q~7\.Gbb"ƎgYw R}jjjsbȑ3f VX[n/1wŇ~___x{{#** /2^z%|ugj{5w11110a1g;wN-ŋbܸq=z4QRRs/S֏ ""fOrr2틌 TUUkÇO3kptt|G(((@ll,"##qUxF4{P9Zn݊'Obڵпjk׮ѣm6DEE~CHH_KUs^uu5N8M6aѢEMo… X:cǎj'K,_3ܺu x衇0e>0dTWW~СC!ɰclܸ?I 9W3g ={6APÆ گ|С:t耊 ڵk8u (;v (((@MM y,ZVVTTTD/8>>^˞={`ii"$$ +Wtz*ƌ+++gϞիaÆ{G}@H$lݺ...{h{0k,`޼y'NDbbP8111ms,_ٳyf|w:t(ƌ5cΝx뭷1~/`V SO!77WX֯_:d24'… HII9N7==vvvpuuEee%?R=CRT#X+rvv_///L:ޘ4i{9HRVVV¾奖 O<qq5 CŜ9s~ 2 C znmmm J8;; sA=!!!B{GjjY.:`SٳaaaxWqQL>]XK/_۷q!|'prrˆ#Lگ#5jaÆA" 22aaa2e :w7xtNPz*O* "www+ rrrrp= :b6կa߾}֭[ꫯ">>z_xx8u놗_~Y>M_[uub`UUUdj cg 0^}霌ȑ#()) ƽ{6vMLs_"`///[Ψr={ BBsՙ[n}w^8q8vv)BuΫ@x2&رcjiN8 Mӧ@-,,䄹sHOO790O GMkݱi&4 .4yz4KKLLDvvг@mO5kִـC!%%E7Xt)mۆ?X-{"##{1˘| 9V% %""2 ,,,O? r9rrrtѣGt14}MMMe î].]yy9N>> Ν^G%ѽ{wrlj_sicm~zXZZbjEįΝ;Mx)&]t;#$8wGy0̙3B )kp5c\xw aڵ+~GP WF^^^?.]sG(9888~8:u^x~:R)Ӆn_sTVVGvv6PxT>?O,^X 'OFYY̙8bʕӧOǺue7B޽g͛71k,_o6.\x̘1VBxx81ӆȑ#PKϞ=쌂\r@5-))All,Ri%""(,,ĵkL&<ƍ Cff&n޼<ٳiiizskҼf͚$$$8|0%߿?# bl%jLԯ>}˗/ŋqm$''o&Ej^k֬A>}PYYJTWW i4!ː|ueP"""O?{g 47n;`PP{{{&H QPP+ԩS6m ׫oΜ9_۷4M#F]֭Q]] G}Pk !""666K)99pqqfϞNvÇcpqq0ᦣ#QQQG}kdd$>S9s_}܄Eaa!222 1ʨ!6 ŋO>_!COC-[Lmrиq \.] 66x衇3 ..NxIǎѩS'lذ7oބ `ӦMjO31d_"""}vB… ضm၀9sȑ#$/kkklڴ 7o{gr7Ĕ)S֬Y\ }̛7oV:hhҗo}L5m|[,WKDl|Yng{'Bqڈc!""jPxq)P26=_wlq_¶5qXPwԨ,Wy)T^Pe*U9]|=z08`lz"""C9Ynn._111œ %c`cǎF׮]6鉈Z21ADDDD8v-5nx [?1ADDDDDDDԄP>>:u*JKKqIt3gī*YYYM6!==xgtR|||0vXĉ/ >>>ѣGE+ i&l޼z߿'O5ڵkUkX[[^@ZZh`Prtt\.\.IR~~~>|8233qUbŊp VBΝo5o { LXXX`ƍN#F.1Xp!Я_?,\Z\O[[[QQQؿ?Ə\~]kTWWRXEu>nܸzJg`ѢEHLLD߾}l2|;_Wݧ*//4b j;w3>u`ᕚzSc)p7O>7oę3g0I;PTTdpYΟ?={uJ1|p;z&Mz*յ=0ADUUUaϞ=Xr%zݻ#$$j I&h׮yd|8x bbb`eeܹsG45k`޼ypuu+&N('C9J}uP1}t:t/Ɔ Di[҆r8QO=rsseL&SN+4~:r9͛ r5550h}~~β^^^:u*1iҤ: 8p.\@JJq_e9eׯΞ қz=)--Çqy#??7h[3f`ɒ%FmNʹJQzaggT|S^xA뽢J8oݺ%ԆOݙ=*_///ʄy۷/6̏9bp7oƍN:M_UU$-wpp={|֭[aOO!GXX^}U=zӧOK.RT(;RY߿]rrrp= :*ibAe4aQF!;;Zz10ADȔdjlpM HhrϞ= }뫫uM7oFvv6<@xyyaݺuB"779ҨMݟJ̞=#G?}Q̟?2̨|L:988Fڤ ;_WP=,,,p%ydff&h}2VY{Nnԭ[7*ԩS&3 5ŬQ O2\qbzQgҭ[7tiii&;&cW0`>رiiij˝]v j6ʢy~ܹ#L tEK\қz=~%bٳ'ڷo+Wԫe C̙3{na/Cϧؽb}O1qDTUUᣏ>~uogϞ?ָįΝ;Mx5S= Ea͚58w~dee!++ DVVV yS>ѥk׮jCr9N8wxrr2n݊:$1r܌8RtEM/88XmSdd$F%l:(Hי777 R^V\O[e< =eADDp䠤QQQpttun+?ŋqlذA-MPPvmt燍7"++ 2 nBNNAMeee8pp}dggjݻwP(&hŋO>_!C4KK/cǎ_{5( lڴ 0`:&v֬Y\ ͛z r]tAll,CgA\\2.j>#GlOa0زep1r8qjp@w]֬A///DDD`툉;DK7rB] "JcdH"-4K4I4>+_*/m233 <ش퉈Qqq1^|E:ukI&5Jzj[|||#11_ȼ %"cngΜ 2d>U9Q׻ X@; {M6ZPu2EdC 9P(ڳ "2O/_F= """je tȐ!>CWl \fV)0dZ!zL/*QEZZݻׯ#&&FNDDDD_m0|6ic7 Lzƾ3X9>Q*lbDyy9v؁_|K, ODDDD-T*OD 9% @kK׷w#koy&T皰 3|ux~}6 Q ıqkTUUݰ I@=)j~ -0C9s` "hNRikk;='Iҟmmmz[U9 jacB3z w^)&ZcB\.##cСC߱hIP(ӧOo6lXJPP,Wڃ&T?"4kʡ|Da`1atkQ}UWUU?|>lذ ͞ 2,Y0dgBb&4"T碰ɫ)|eAu#44$JeYV}(3s)`X_NTQPP~ַծ/5 """"""G[@59f0B5Y>4Sfc m X(?+Jj/r%o P00ADDDDDDQ  RNlKE @hc 諾 4{Jh~VRSD^ѣ2p(Gl f ͞!Yפھ7L>uʀ2B#f a#!""""""#AߐϪ44z  t@:Ti>CI-u=C3oC.mC/t=YC38 CzK8q(`Z/ m;{A({L(>$D5hDDDDDDTzL(k >h۔ ̫DŽf:SzMzf b۫PY"""""""%ƿ\fBsN thƤ3YShy&T{J(M$XkB^ܡ-Qۦ(]$& J4&aaC9'n.S},6`Bؐe0XADDDDDԺ`5ap }A m7YiFtC ,(Oa""""""2X@lJm2 f?,b'*}:wm e=.E``L9gcڂ-d=,mJ zMh[n>[ """"""2![B3`V%1!N_@WϚ'DDDDDDDhR3 ?xajPEhжXmUF""""""jLRaHo%̾`ށ NilBda,0Z6u\oh .}HisC[gJBlX"""""""C%!F_C-`B, cJGDDDDDDd@Dth전iDs6"8n#HYFDDDDDDdH Bu1 mi YOLOX C:COu2uhsB{FJߐO`Bs1ue/ """"""j(K_eڴP(Tי!"""""O\}6tM̡mlL NK/HaH:"""""""Cb 84DP” \ P]ADDDDDDSCJ06aLCf J՘n fy""""""C_C0&fcn nSC0854}L+)y7+sk|ZXQcLlkj 38!#5lקG1ڠOLVRJn6 ,UP0 mQSy4-_C#""""""2o ofZF\ -Qiƿ95UKj\7TY[1QPH(F:ADDDDDDUC?Ehs """""\4 o̲BDDDDDD1-* My |Qk A H(99Lgs,QZ[㹵Pj |lDDDDDD`xo HDDDDDDW H(F{[;^""""""jZu0BU[o'""""""f0WADDDDDDM"4!n'""""""2zxNZ7  \eQpIENDB`bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/data/screenshots/bustle-welcome.png000066400000000000000000000760551506133026500274460ustar00rootroot00000000000000PNG  IHDRoVsBIT|dtEXtSoftwaregnome-screenshot>&tEXtCreation Timesb 25 nov 2023 12:40:26'EG IDATxy$w}OuuWշԭn- PDKB#qc- ׻<;X;Y#? b@ #I>ouwVUqUYˬȳ2#+߯'2"2"S_B.` ;Pkn XJu{T fЬг=64mpnl?/TKf3@--мCRd`^jar<(Lh^*ҏ?k1,0W˟a)|_S-lCF~Y{`~jqCmCF Qsp Ys6T`n\z@q87LXnZ9Z^,kyk+ ǥ~?ͩPZnxFhu`{Ыrrq3=JCoòBBrc yK}ۿ²_B#.5n8h4Bq~ rbc?~CCj\iЮ[tҭBbmN-duo²B_9rcH) [Pk\ͰLPPnHv@ ~~̥t +$>)_`dw1~n,]ռX9w=1SK&,!-FH\\NgJ:>7/WN bs r hu|԰{B:(W;$l/% ӑFUj-70;y+g[Xw,<ҩPH.% 2Pu<{Jr9C2(/\IH.jNPf^HX}W^A!Pڗͅf&h4z3빁\=˾~K3Ac,P4G^!7PX;ȅf6(ESjuK d󸠼 .7: }UsP.5$ A{x2 jGCqnp.%,2[YK)jõʛ}^!k_0sP\((`)0 x7 ϼzǺJW窢Ay!=9߲Y4DfU=L %%Y=2~ec|Kaw!sU|+ ae䈤WmsϭV!_@ oY֑>)IḮLHbPd3lF- ^]c b8dlkWG[ZZ[KA")]mbkȶRJS@ά^5=ǙrHaW_};,%ĉ_ؽ{?k&,!9>w¯ۤAY۽4mP.7$~Ɯ+7$?֭tY? ,Qqaٜ;w6l,R5 ʵy1y=ɹ~dsC [ӇzVe߲jfY_,5ijq-luc r"=Ўwz,[쇏?;o{zVT:Z*&_=WXR|de^{_=BCHTϩ[[lYй˰VJ[KŽuݹUQ?߿JM W~})[+<>h@SI7kzdBGgwDktN:7gv muXڳ1|J p5\#I&m6nܨӧO&IڶzoӁoҁoWt;[ڱ.`Pz;t0 =s2)˙3m[w΅dI>Ǿx6rNG{SyRkz?Υ-QG䀥;vVseYP[Hݺuz!} _Pwwws0JƩ|l+O^_21zUme:?b [:uu:;d)6A}u-ڷE^K?dl]g=Ͻ}$֧\ԟ1YF:xlyyfeg*zʫ/R2*Ϭد~zG*:C>OT|m4?eIP29c-~M>ht1I$X:ξ2ؖn~^9Y]f4{r%?yڵz;ߩ|#Y?M%ImmmUmmmU=_ ԥH$O~'?YR BP<:::j9VX^``ZZZnkkSKKK*Eɞ< |MшCч^ҢH$[bjkk (@BZl$_tZmqf& BD"b (. X_gtwPMPԋ{ w\$on+Sl6:UtyWX_4r?\wGwuֻ (0ǖ!ң,N2(g.^2n~&hYϼB̲3J=UM+4&ɖdKgc\Y.c[k7ym+gz !\X,zn5Oe/ԕ@ (Z9T;Q hHJW+QиR(W4Q_gFDP4|Ak{gBuݒ-> R^Re\W;J} !(|n8l۞{B+ rJhGX42dmJ&Sc bGQ؊Kpm\u[,k[նKXLP3X^ OuFGx(dȒ$찖-t]DWԶ)Ā&$),ֺv[;V{}P:˪ hyecL8.yfeg*zP':jrrRSg_Rg~(NE]>K^gJέ{~⾢WJqCڝڠ{tYET~~:c#ڵrZo K %4&ɖdKgc\Y.c[k77::4TJH,KC5GA:Zw}@S7}VkoV(T^5geY׳3r*]ka?ub oA0 t҆%GXAPy J dR~?ZǻrWۯQ$^(ʜL&j!ÓUocSjmU}pUCW[J{Z;vGX׌A9X#ԏWK$zZH_ޯRT̗g}U_j!Y.DOW\ߋ;%o*}]/뙫/_ A+fjN: -u7t7bO}rGvͲNկ纮Uļvvy][5eW|j2 XvrrG ,IUk{՗hg@2Q !Y[Ly*=UD~_T @% }u(dT מNx|x6e a|~+#K':8<2^kP 2!NHNnu҂д:̘خƇwFe>ev48}9Nܗߡs IDAT? (|/ HICnp`tJN[׬ J&,(^_tRg/ ֬3=N c@C!(|- vELwU 65-ϼ# /Ԕ SHwvLS5{j"޳3PZB3/cc:yfv  AP^8V}ɵΜ]ϲf6KR}B lP(N7:[džN-si[gN ^ƊַŚ#gLTiK3[{>A[e6ݲs=3j,kAZM@ p8e?uNвmxor*mLU TZ ;]z;2bYےə@HV:L'93r: ԷmC:qd@==҉JLgwm(Z՛"YSI=Yū7}|2t?5o.lnbZveYٶ?L̹rexÝjiiɪѬEo^nvԲ>9]Wɓòra7_Q[ZH01Mi֡=:;\.(ErꬠoL'eAm޾Sc[4oJcRߠ4(/01Cߤw w@@;nng5\u{P#ceRq526|nk.rivnۢΟq]G{7WڲugL&byE( (4ڎJLYLjt֮];w| %r0oܧ9=Hs_zwkxxT3OjיGa<.T߭[ܽł {/(*xe@ :ѪQ<'Vh3 ^}cH1ŝ6^otoۨq>)}Y&M4 :(DtޫW""S˶w?mw FBkԉ}Mo{"ȼ2]Z4wz~#uڭrzSxbnXQ'1s݉[w߹_3pGWu[&-b!GѠCzW,;߬h4:\fہ,۵}F:wzˉ-=jӓwx3|nz1/,+w]qtojmJy^.]@tc^jG=ok7S6]jο9LkOWll'ou:::O7Hz su@@wU]#uCNyq&rYֺ;5Ə[nSKKKY/wx_{]Z'òWoP+a|ۣ[ߺzV] ˡi=7^.^II}uk=nGV}j#4 lX,o7:ikt +%'/&KL Fuz :v-n׮ =8 a4[nуyy]1.+$ fI;]}kbw-ں=xLOZG]RK8aCڹW7o־[_+2w e(Ү]t饗رdu߽m޾Kء6Y3Ν̬uǎؙnJ[ w.]X[kG-;yD’kꮴvnۢ;0W_Oh42x tG*]yz ?wM}W]5-6ַ嗋-7*yWjMy|X2_G5 iEϿnݺa]nλ+{LAДjXKA$,$r뺞×m 9HGK;/ mws;V]_ߵW eRI,|^ysW? PKtoq,L׳s"@b|o b!Zk^{|>[^uH2Jffg|\^_WAZ<۱]׊YzB K'{-;::@xbf*v^c{fX7?4ˬg^ex}fٙJ*gHnPά¹z*ٰq6l i~'(E}$ْ,c̾wk22el]wbC~/e﹮e˖ջ ,# A ,(w5ï~CP4z@ v'U2ʕjiiwi]ߚ]., 7(Au_h$"ix|R_ƍ֬s he@CwGy]yQw)mPߎ@;(oݺ1)+d@ (B݇^ByK},!e/ 6kvKʞ {195Ha:Xbџrϙyy54 @ ws ^n@ovmYaPBW|;2̮rrs|mjvL| FCPFmrJ.ʽ^)|fwb 8 =\p ]( Z+:'BPJ5ٗەѮ|* ~:J S(,g,$01 ]AKł`UN\͡ ~ҰKHA[\Jwoyur{} A{\/:W%.3~VRɌхUsbkfpg}`Rw,@ /h]וeYEgYּʹf9Q_)'Ke@S(=l7۶d2!.YݭQEڴ)!mQ] +_ix(ՊF Bs4'D&z)z֊)mEomSO{B6h7cڲ#b+v^c{fX7?4ˬg^ex}fٙJ*g,A97+u599ju;8a'ДnѲ;֖Ձ-Ņ8rTg?TiF5yuJ=}EE}$ْ,c̾wk22el]wbñ>AJ ɶmkpbyμt-5Y£Rϫ^he@ hֶ|{QBrF$׺T_﹢hwW%$ḡ[} ˄c@3"(b]q52赴Fs~x<^qE;a닞973\g@3 (BnԲ,Ǻհ"v@c#粆`{ ?|Dfq:MW%XjG@U".KĄnŏD(V]e@S#(AtɤV먒ɤ\KR*KuM_R"xwe>me)y]ЀF8wu:Vk(M9,˚[622XHnoe}۶H +22Z>:2iH~A0N}DrGqdmyMu] a͹Uǎn FP4h/(+[{$YN5*2YMNLNb`u]W ɻMAyR{r̰JU:nMwLdP=l<>ֆSx hne@SJ ȭÄYm+=[sI[vj0U2)_75xn*%u AKnv]GsJjy#Ts_ h:@@rJu='$3 5mz-Op] 秒vc㣋~];>l79gjRKTtz23cQA2iM'kY-Ir['J,tR'^ܛ Y)Ir\WǦZgGGr4sos>A42A4k*xv7Trn:f{:xaׁ@@pX綥GGGJ՟d+e;:2WʹxЦ slŔh}C6{zBǞ=UaԨ&Rtpֱi ONY#\IPFڿ/+({}tKAt6n١y\Qz')+0-{Gӧ(Rǣ.-; @ ;wȺy;rVFr̮jl:OݓlJ]}6_=k1MGX:\un95mk8!{Bl]@]PL rT\!9iKn\57ueg\-뭲^zUh`|RF + + I8l[鴥x2+ ޞm7ҊSK^&|fDmm:ɼǺ#{jHNrDPHPPLumge,3Sw'ku)Lm ܺ_ -{I *$)<)=٭o77MeFh4UB.Kpzˢ3ުOhڵY370TjzsVSjzm{vڹCh,ue@SA]w>տӔݹhמXzõo  u޽r-Y;>]ޣko!dfEP,YH$}ޥ?)kykOQ4WW]w˻ߥeUZޡ߽[zg}f@P4ifѡn~9yTzC-:0ijٲeY}ͬ ޺j%6oD}yM4W^2n~&hYϼB̲3J=Uhp#gDBOgZ._KE"]3"y;,K:>rnVJ*9^u& g?S[՚U4m]uNy 5z~.|IhFIf\\:̺ur]㽩سKVb%/YŲm:~жtW\8(!٭ӓ]7k׮] f%Uj,3='ZsL`.!("a ^[coY|g}j#(@s"(e@M]U2uQU-'m@fDL51ݡuNlרu\r.2\IG٫3G*zڏԲ^5$MhrE&6J]G[wUV!Av%(jyo۶544qMMMɲfr8V[[/_իWkݺuy\fGHFPy ʹwP\}~ ͭr'@inBuޛJBȽ#$ATei~4i(^s~fAP4rgضB>:2iyu3ۥYNx,rXV`Sm)Xp L%](RBPPJ -<X x$K] ~FFP ~~Fw AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC{L_җ}Ν;T2@l[V7={G?Q emt׾5}_o[6mBhdz/ jʕZz6nܨ;CW$qTCB!]:th޶^:tH===YzZ߼y3!E9!;vLO<>Ο?_eh0{OӬmwߝ^<ٶW^y%kۍ7޸JQM>.\0g}ScE ӷ-_tAW( :}.ҹmGt~>X,Vjݻwk֭J$:|ȑ#TE~kh0֭۳̻_{g}6k=wu$u]J_;SNա2 ȫkk׿կ_שuUYB!ɾ6o\jX8^@ڻw}Ѭm%&K3òmV(4o~+}WwwN:˗k۶m馛O۫o}[:yN>!ZJ7n޽{kk|__}Q=s뺺Koã>vl.\ )P0Xh)rQVbJEѢX|DAQZZ (h(-  H ^?p׳My}vp IDATg&3gڵӀԵkW /viɒ%Z|vޭ cǎӟT駟jڲev:t蠁jA쮻ʕ+'ϩ{5k_ƽM6ҥ׼ɓ'wLl6Z3}wh՞鼼fD؄o ɒMv . }Z4%ARo_^=묳;ϫ;(oܸkoիWk^.7ί|pP>3-^o~FF5k湽~3ϲ?zWNN+W-.K_=_{ >\r:ڷo4hY>|O$Y,M2E}… 5o3s=^uصku4ِ!C$l4oqԳgOM8ѯ|,Mum޼Y/ƌ۷kuֺ袋ti~~z"@r (@jٲZlz{Z`] eXsluIɮ~nY`gqrss=%%%:x ATQQq9)Б#G6okbm۶^aH.K%l|[RRY(86XViF Rϲ`ƍ[[~-]TK.ըQt뭷F}k ^sN.]tQzV{e溑jԨQeE6\uVu"`$傕1 %&*\QQGzC %Ԓ%KtmgϞO~܋/۷G\H%-ºu릌 OkfB=sA;QNN_,-\Pm۶V`{rz5j( mٲųlݺuZl.2IwQ}EIRz+vcm6`~iy^h"]}o~EEu5kt|ٳg{/վ}{I`5V-  eggK.}Ν;~:v_ 3?|pM>3} ]wuզMl6۷O{8PYԻwo5J $=zWծ];믿_QFI:y%דO>)I~]sm6FDMY0۷xL͛7%,[}{[NëСC=׀,X D˗/״itg@7Vyy֭[W鱋|g H>eHqru{KKKSݵb =zju:t֯_5HSEE.]Wo JOOnv˖-ڲe`Yw7tM:om_7o%<ꡇ /n 4wܡÇ, =ft='ñј1ck)[n%E\s5nZvm?5{.Ug H>(@k߾4i7@SNbM999;5bڱcvޭ"իWO5RAAuSO=koQZҒ%Kk.eddk SO=UfҚ5kj׮]ڽ{N4iN;M^z4iYI&zG)77W۷iaÆ.6jr̂ԫW/-ZH;vPQQ4iVZȑ#r>6jѢZhO?]C=4tPhB/m&ժ:K#FP׮]ua-[o=ͦsO>ц t\M4QӦMէO 8P>vlɥC\Vo[gkiX=~ksnEEŚ* Y6|IG$9$ٍge<2evMo~yAAAAAAAAAAAAAAAAAAAAA@B}_]ͮcƌ… u-o߾5j{9shРA/\C R#Fr飏>x"x/r}g[ /L3f!Cl\6{z+h{{_K/ &5Ú;w,X͛k޼y8qx edd詧뽶gϞ|\l*((ݻÖOԽ{w9?=sѣG5a7N\r8ߌB&عe߿z!9NY'rJ]{~emoŊzGrdX$IVҐ!CB~B9`'csd[pU?W8[[ogyF-Z[?wJ$Q$?=j֬fΜ1chذazr?Qsĉ=^uqTe c4| >\-ҠA4i$}W!׻ꪫԾ}{hB_7o͛7{+5x`uAYYYwRo߮FI&oUVI:pBu]ر~򓟨Vee讻RAAZh?O*--j}ϊ -YDw}:t蠖-[czڶm.j9p'YsW$?n>u o;TΝb{?:uFy3zhjJ[ZwNMΕ|@<Т rrr4rH]}Zz^~e7т ԡC>|X˗/׿o=zT{kqܹsޮ]t:u7zZvJKK%I~tgmk޽رg^vv:w;w}={tz];w$SOUvv?{Vk{}4g5k,OǏ 7ܠnдiԡC9Θ=h?_]z-O=*I߿BNSk~FFl6[r:5'hlB_ڷoJ'z999>|9mذ!XV͚5K=l6;B}6߿oʕK_իWK:}_~5ZT Ou0TmUhߪ~S_֩'62ƍ{g=nݪÇ[oU핓J}]D7f͚)'''h-Ztj֭~Zl)Iڸqg9U$\*;[h7o=z(##C~~~g$lرkovI&iZj8<ܹq w>tMǏ׆ W_0dyS{Վ;qƈZC9ɱBEc[>LE*+SL~+h!(H?\3fڵkjZ`nݪ}J6m7j:~5jcǎ9>@ɮ|n+5jz)]VC}l"IU>}ciΝ*++իuq+#<͛7X?uy}Ϝ]~6m6oެj7Z۫u1m޼Y3fŋuwG*p8TYYJhŊZ~s=ojڻwNJIRII~m N6lPvvrss#ܹ곑";V.bM2E{9XVl6[ӧOW.]<([nE `ErN:Pǧ&?g"V^^'ϔHQ$;h;Q0 6trTNYYYرfϞ-.Ӛ5k4buU=~i;wz>}D~ѣG+33SFiȑ͘1C7|*++աC?UvvƏٳg뮻nW=4s̰as9sƏ jj޼yDd{.k^SFFڵk>[/_X fɒ%ZdwA<ڷo/I~|P#FPտ(GՊ+SOTmڴԩSi&곉|߿/^pDbSO&LП'%%%:Sn/zΩPWɱT$ۺ5m4m߾]?ϔ*(|N1%qD!VߤhŘ~6XG?v?VTT¾@s! 8P+Wuݮ]4b;^-ה)STl=f/$$x{Zƴy.)5QfPm۶M-Z $UԽ{wߪR]tz >PܹSVұcǴsN͙3G#GLt{ϯ+jNNf̘@e= S&M4x` 4(ժ٣J{s=e@P\ @GP@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P r% X,x$\pL`}B3D#(H"k V3O`(eq!!jUzzҔ&y$ A@\!trnBVU٪_V+@-rt:UYYǏ\6MX,VfqEP7nAv:*//$*=$K,%9++Kv]GnWffu ^=0CnWYY233OH(==]TYYRwA@^t:u կ__999  ~:qWP&qt:p8T^^leee%J$fbo1e=p$իW/ $Ѫ 2РADW"4hr9DW@BP3CiiiHp"##Ciiir8*2p'UJOHX#()=-6-UiQv? bVrX *까[EbS)[,j, '+22@u;@<D SCz+iRAUnu'0uXM2F\ PGEz_PТ Jb(==]6j 2@(#1B[nu{\.-\P:-[z3o>o^Ncz|>|gڷѣGuEyNSJ^ 4(Hk9s̙3}v{I&X7իkV]wWCi֬YZfJKKնm[tM %I7n_T[ѣueUr;w{={-t/H5e)jٲx ۷ϯڵk5d|PZR#~kj̙:u:,kUP>}TPP'|Rw~Gu]'ժzKĉug׽ޫ-[sΞm_^_|y_^rzs=W.֭\AZn]#$8qBs=ܣvi۶mW^S4ĉ5ydmݺU7tZn>}xVzuꩧ/Ԟ={<(**RaanV?}ݧSO=U]trTs e^n)6hURƒ%K4|pOĉ5p@rە6mh͚5ZdI={?_jҤ:vXz[۷oדO>޽{RǏÇըQ#+K=_Z+WԢEzӢE ƏiA>sZh޼qׯ,Y ٣_]͛7$jkȰao꣏> HuNS_gy1_ꬉDѣաCϼM*77ׯl۶mFjСvU<ԤI/ST^z%wyǧ~<--ڽLrIݻu{wSOWJJJ_>Q ;iݺtV{n_~-[zBr %%%z'5j( 0@_*V;uƍ:v옜NgO> <>}/t e)ml4mu J.ܝr)z״a_ŋ`-^X5+ԫW/tӦM##h.go3-^!HRNNNzUE$02}u8^'cu=ȑ#7nZj4~~GRQffze˖9=236!(H W~~ǎWo*U6m<6l۷{N;dddxZ0G.H7nT߾}հa[VuVy۶mS˖-kPl6_KT}"9پ}W_yuر^{5۷ϯu^:y]VSNUn$@n޽~- HEo[9]VǎKt az e :TK,сt:5o<9rGь3sNׇ~aÆywI[nr/ѣ^xb}b &;֮]/:ڴiիWX^-O49rDӧOΝ;ojժU2dgyղeKyϴw^ZJ򊤓-Zڵkt:eOTdXԹsgM0Asу> hQ2ƌb >\۷oM{r9Rٺu{Wk׮ 7ܠ.i浍_O?JmVq5 uɮCG*Y, ޶%kgd7>ҌG\￿^zWVmn(--}3??c:Ƴa0 ֺ]B0Gcu (>}կ*n?drEޥ7I拫X(G}Z->C*g_~Yg*z6m[noK:Z}%l^+"WuT fjgYV ~onѢŸ*R{ٮ]EJzH]Ks{%,('uMv,حB}t]vlڴjM6߮]Wt%N[DI᳗Օni1~4 hUۥ]t:޽{f;=:@QZZԩS'9r2a9}a9o+]}˄ ƾ)~h$l ۼ~*)CRĉ[79Yfl6i.I)bUTTlۿ y衇v]1owks}])PIk#y\e3D^:xn+ b7CyoKo86n+؝|)ijo]KLrr).g>gY֩\;#xsqHEv}Z8{ΫJ=&r0U vsâ{yO~ f DPtpS&A9P{l%mI}.p%nY{)z N74*$:k-h:#m%pm>|(ݼT8\@a97:^/_KG6m ]Fv$XWP#Weɻ5:ب֑&'hnZ^+tҏep!eMerc0ew IZJeriUu@cv/fkz@2 }5Ɂʅf9VU)WmjQufK[Aܜ>Vewy~;vd,8CL+ ^Br,ĥ:Y^KG6癷]w@/E]b:3xJ 5WBsJkrRgVkׁn[v"-@pFI`Jnג71Tuk`w+<`u tRy 몄`aqkwN9U9H "2#XPٜ8\koMd)9[ f}[}C,_H[ ˷>LW7$D*,K[8{#$t&!9[ʁF:WUtգ1T+HpuHqV@˪-TRUV@eHpJ&KnXP U 0/\ղQ잮-2Y$؜WL$I `Wr"] ^ە:C`W;*C@miKr5 SgT;\ b!صp#]j~MF]2֡a9\p9r" U5G#$W|T%KVX6E#F%ZU U $4$Kb#npL j"\o$A-Un$[N}beST>P媳J0XD8V@2JqM֭n%(˰l'qP7E3p֤Ź45դ^YZYujt*$Keu@4Ԥ;6n${i~Pk Rjd pmc*^J Z6[FF)!"ݨ vvܤr@eS@U2ĦT@v 0P)5%[m ~ɸX'K2dSԶ0WREd,k@25Td]GZZk RñǺL ƾ8:} #qP[ F@~<kIENDB`bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/deny.toml000066400000000000000000000004401506133026500223550ustar00rootroot00000000000000[advisories] yanked = "deny" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ ] [licenses] allow = [ "Apache-2.0 WITH LLVM-exception", "Apache-2.0", "MIT", "LGPL-2.1", "Unicode-3.0", ] bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/hooks/000077500000000000000000000000001506133026500216465ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/hooks/pre-commit.hook000077500000000000000000000031731506133026500246130ustar00rootroot00000000000000#!/bin/sh # Source: https://gitlab.gnome.org/GNOME/fractal/blob/master/hooks/pre-commit.hook install_rustfmt() { if ! which rustup >/dev/null 2>&1; then curl https://sh.rustup.rs -sSf | sh -s -- -y export PATH=$PATH:$HOME/.cargo/bin if ! which rustup >/dev/null 2>&1; then echo "Failed to install rustup. Performing the commit without style checking." exit 0 fi fi if ! rustup component list|grep rustfmt >/dev/null 2>&1; then echo "Installing rustfmt…" rustup component add rustfmt fi } if ! which cargo >/dev/null 2>&1 || ! cargo fmt --help >/dev/null 2>&1; then echo "Unable to check the project’s code style, because rustfmt could not be run." if [ ! -t 1 ]; then # No input is possible echo "Performing commit." exit 0 fi echo "" echo "y: Install rustfmt via rustup" echo "n: Don't install rustfmt and perform the commit" echo "Q: Don't install rustfmt and abort the commit" echo "" while true do printf "%s" "Install rustfmt via rustup? [y/n/Q]: "; read yn < /dev/tty case $yn in [Yy]* ) install_rustfmt; break;; [Nn]* ) echo "Performing commit."; exit 0;; [Qq]* | "" ) echo "Aborting commit."; exit 1 >/dev/null 2>&1;; * ) echo "Invalid input";; esac done fi echo "--Checking style--" cargo fmt --all -- --check if test $? != 0; then echo "--Checking style fail--" echo "Please fix the above issues, either manually or by running: cargo fmt --all" exit 1 else echo "--Checking style pass--" fi bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/meson.build000066400000000000000000000040511506133026500226650ustar00rootroot00000000000000project( 'bustle', 'rust', version: '0.13.0', meson_version: '>= 1.7', license: 'LGPL-2.1-or-later', ) i18n = import('i18n') gnome = import('gnome') base_id = 'org.freedesktop.Bustle' dependency('gio-2.0', version: '>= 2.66') dependency('gio-2.0', version: '>= 2.66') dependency('glib-2.0', version: '>= 2.66') dependency('gtk4', version: '>= 4.15.3') dependency('libadwaita-1', version: '>= 1.8.alpha') glib_compile_resources = find_program('glib-compile-resources', required: true) glib_compile_schemas = find_program('glib-compile-schemas', required: true) desktop_file_validate = find_program('desktop-file-validate', required: false) appstreamcli = find_program('appstreamcli', required: false) cargo = find_program('cargo', required: true) version = meson.project_version() prefix = get_option('prefix') bindir = prefix / get_option('bindir') localedir = prefix / get_option('localedir') datadir = prefix / get_option('datadir') pkgdatadir = datadir / meson.project_name() iconsdir = datadir / 'icons' podir = meson.project_source_root() / 'po' gettext_package = meson.project_name() if get_option('profile') == 'development' profile = 'Devel' vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip() if vcs_tag == '' version_suffix = '-devel' else version_suffix = '-@0@'.format(vcs_tag) endif application_id = '@0@.@1@'.format(base_id, profile) else profile = '' version_suffix = '' application_id = base_id endif meson.add_dist_script( 'build-aux/dist-vendor.sh', meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version, meson.project_source_root() ) if get_option('profile') == 'development' # Setup pre-commit hook for ensuring coding style is always consistent message('Setting up git pre-commit hook..') run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit', check: false) endif subdir('data') subdir('po') subdir('src') gnome.post_install( gtk_update_icon_cache: true, glib_compile_schemas: true, update_desktop_database: true, ) bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/meson.options000066400000000000000000000003011506133026500232530ustar00rootroot00000000000000option( 'profile', type: 'combo', choices: [ 'default', 'development' ], value: 'default', description: 'The build profile for Bustle. One of "default" or "development".' ) bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/000077500000000000000000000000001506133026500211415ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/LINGUAS000066400000000000000000000000771506133026500221720ustar00rootroot00000000000000az de en_GB es eu fr he hi hu ia nl pt_BR ru sl sv tr uk zh_CN bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/POTFILES.in000066400000000000000000000010771506133026500227230ustar00rootroot00000000000000data/org.freedesktop.Bustle.desktop.in.in data/org.freedesktop.Bustle.gschema.xml.in data/org.freedesktop.Bustle.metainfo.xml.in.in data/resources/ui/address_dialog.ui data/resources/ui/details_view.ui data/resources/ui/diagram_header.ui data/resources/ui/durations_page.ui data/resources/ui/filter_pane.ui data/resources/ui/frequencies_page.ui data/resources/ui/shortcuts.ui data/resources/ui/sizes_page.ui data/resources/ui/statistics.ui data/resources/ui/window.ui src/application.rs src/details_view.rs src/message.rs src/message_tag.rs src/message_type.rs src/window.rs bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/az.po000066400000000000000000000401561506133026500221210ustar00rootroot00000000000000# Azerbaijani translation for bustle. # Copyright (C) 2025 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Ramal Rəhimov , 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-08-08 14:14+0000\n" "PO-Revision-Date: 2025-08-09 14:28+0300\n" "Last-Translator: Ramal Rəhimov \n" "Language-Team: Azerbaijani \n" "Language: az\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" "X-Generator: Gtranslator 48.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "D-Bus fəaliyyətini vizuallaşdırın" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "xəta ayırma;profil;d-bus;dbus;ardıcıllıq;monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Yeni Pəncərə" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Pəncərə eni" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Pəncərə hündürlüyü" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Pəncərənin maksimumlaşdırılmış vəziyyəti" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle, D-Bus fəaliyyətinin ardıcıllıq diaqramlarını çəkir. Siqnal " "emissiyalarını, metod çağırışlarını və onların müvafiq qayıdışlarını, hər " "bir fərdi hadisə üçün vaxt damğaları və hər metod çağırışının müddəti ilə " "göstərir. Bu, arzuolunmaz D-Bus trafikini yoxlamağa və D-Bus əsaslı " "tətbiqinizin istədiyiniz kimi işləməməsinin səbəbini müəyyənləşdirməyə kömək " "edə bilər. Həmçinin siqnal tezlikləri və orta metod çağırış vaxtları kimi " "statistikalar təqdim edir." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "D-Bus fəaliyyətinin ardıcıllıq diaqramlarını araşdırın" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Jurnalı xülasə edən statistikalara baxın" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Bu sakitləşdirici boz rəngli qarşılanma səhifəsi ilə rahatlayın" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Bustle tərtibatçıları" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Ünvanı qeyd et" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "Ünvanlar haqqında ətraflı məlumat üçün D-Bus " "spesifikasiyasına baxın." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_İmtina et" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Qeyd et" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Server ünvanı" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Əlaqəli mesaj" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "busctl kimi kopyala" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Mesaj" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Növ" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Yol" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "İnterfeys" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Üzv" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Xəta növü" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Təfərrüatlar" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Göndərən" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Təyinat" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Komponent" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Bədən ölçüsü" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Cavab vaxtı" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "İmza" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Bayraqlar" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Bədən" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Format haqqında sənədləri aç" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Genişlət" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Müddətlər" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Hər metod çağırışının ümumi və orta vaxtı" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Metod" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Ümumi" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Çağırışlar" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Orta" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Bütün etiketləri seç" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Bütün etiketlərin seçimini ləğv et" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Bütün adları seç" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Bütün adların seçimini ləğv et" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Hərəkətlər" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Tezliklər" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Metod çağırışlarının və siqnalların tezlikləri" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Mesaj növü" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Tezlik" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Ümumi" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Qısayolları göstər" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Yeni Pəncərə" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Pəncərəni Bağla" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Çıx" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Qeyd" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Sessiya avtobusunu qeyd et" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Sistem avtobusunu qeyd et" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Ünvanı qeyd et" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Jurnal faylını aç" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diaqram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "PCAP faylı kimi saxla" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "DOT qrafik faylı kimi saxla" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Xidmətləri süz" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistikalar" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Ölçülər" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Hər mesajın ən kiçik, orta və ən böyük ölçüsü" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Ən kiçik" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Ən böyük" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistikalar" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Yeni _Pəncərə" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Aç" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Jurnal cütlərini aç" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Kimi saxla…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT Qrafik" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistikalar" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Klaviatura qısayolları" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Bustle haqqında" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Sessiya avtobusunu qeyd et" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Sistem avtobusunu qeyd et" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Ünvanı qeyd et…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Jurnal faylını aç" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Əsas Menyu" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Bustle-a xoş gəldiniz" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "D-Bus fəaliyyətini qeyd etməyə başlayın, həmçinin əmri işə sala bilərsiniz" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Kopyala" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Yüklənir" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Dayandır" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Xidmətləri süz" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Təfərrüat görünüşü" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Xahiş edirik, gözləyin" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "D-Bus trafiki gözlənilir…" #: src/application.rs:37 msgid "Open a new window" msgstr "Yeni pəncərə aç" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Buferə kopyalandı" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "busctl əmri yaradıla bilmədi" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Cavab gözlənilmir" #: src/message.rs:209 msgid "No Auto Start" msgstr "Avtomatik başlama yoxdur" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "İnteraktiv autentifikasiyaya icazə ver" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Əlçatanlıq" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Coğrafi yerləşdirmə" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Daxil etmə" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Daxil olma" #: src/message_tag.rs:133 msgid "Network" msgstr "Şəbəkə" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Siyasət nəzarəti" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portallar" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Axtarış təminatçısı" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Gizli məlumatlar" #: src/message_tag.rs:138 msgid "Session" msgstr "Sessiya" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Qabıq" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Enerji" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Naməlum" #: src/message_type.rs:34 msgid "Method Call" msgstr "Metod çağırışı" #: src/message_type.rs:35 msgid "Method Return" msgstr "Metod qayıdışı" #: src/message_type.rs:36 msgid "Error" msgstr "Xəta" #: src/message_type.rs:37 msgid "Signal" msgstr "Siqnal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Sessiya avtobusu qeyd olunur…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Sessiya avtobusunu qeyd etmək alınmadı" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Sistem avtobusu qeyd olunur…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Sistem avtobusunu qeyd etmək alınmadı" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "{address} qeyd olunur…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Ünvanı qeyd etmək alınmadı" #: src/window.rs:194 msgid "Failed to open file" msgstr "Faylı açmaq alınmadı" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "PCAP kimi saxlamaq alınmadı" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Qeyd PCAP kimi saxlanıldı" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "DOT qrafik kimi saxlamaq alınmadı" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Qeyd DOT qrafik kimi saxlanıldı" #: src/window.rs:415 msgid "translator-credits" msgstr "Ramal Rəhimov " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP faylları" #: src/window.rs:517 msgid "Open Log" msgstr "Jurnal aç" #: src/window.rs:542 msgid "Save Log" msgstr "Jurnalı saxla" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Jurnalı DOT qrafik faylı kimi saxla" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} mesaj qeyd olundu" msgstr[1] "{n_messages} mesaj qeyd olundu" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/de.po000066400000000000000000000362021506133026500220740ustar00rootroot00000000000000# German translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # # Jürgen Benvenuti , 2024. # Gabriel Brand , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2024-04-01 19:50+0000\n" "PO-Revision-Date: 2024-04-08 19:51+0200\n" "Last-Translator: Jürgen Benvenuti \n" "Language-Team: German \n" "Language: de\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" "X-Generator: Poedit 3.4.2\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "D-Bus-Aktivität visualisieren" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "" "debug;profile;d-bus;dbus;sequence;monitor;Fehlersuche;Profile;D-Bus;Sequenz;" "Monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Neues Fenster" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Fensterbreite" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Fensterhöhe" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Fenstermaximierungsstatus" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle zeichnet Sequenzdiagramme von D-Bus-Aktivitäten. Es zeigt " "Signalemissionen, Methodenaufrufe und ihre entsprechenden Rückgaben, mit " "Zeitstempeln für jedes einzelne Ereignis und die Dauer jedes " "Methodenaufrufs. Dies kann Ihnen helfen, unerwünschten D-Bus-Verkehr zu " "erkennen und herauszufinden, warum Ihre D-Bus-basierte Anwendung nicht so " "gut funktioniert, wie Sie es sich wünschen. Es werden auch Statistiken wie " "Signalhäufigkeiten und durchschnittliche Methodenaufrufzeiten bereitgestellt." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Erkunden Sie Sequenzdiagramme der D-Bus-Aktivität" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Sehen Sie Statistiken, die das Protokoll zusammenfassen" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Entspannen Sie mit dieser beruhigenden Startseite in Graustufen" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:134 src/window.rs:408 msgid "The Bustle developers" msgstr "Die Bustle-Entwickler" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Adresse aufzeichnen" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Weitere Informationen zu Adressen finden Sie in der D-Bus-Spezifikation." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "A_bbrechen" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:91 #: data/resources/ui/window.ui:203 msgid "_Record" msgstr "_Aufzeichnen" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Server-Adresse" #: data/resources/ui/details_view.ui:22 msgid "Associated Message" msgstr "Dazugehörige Meldung" #: data/resources/ui/details_view.ui:38 msgid "Message" msgstr "Meldung" #: data/resources/ui/details_view.ui:41 msgid "Type" msgstr "Typ" #: data/resources/ui/details_view.ui:50 msgid "Path" msgstr "Pfad" #: data/resources/ui/details_view.ui:59 msgid "Interface" msgstr "Schnittstelle" #: data/resources/ui/details_view.ui:68 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Element" #: data/resources/ui/details_view.ui:77 msgid "Error Type" msgstr "Fehlertyp" #: data/resources/ui/details_view.ui:89 msgid "Details" msgstr "Details" #: data/resources/ui/details_view.ui:92 msgid "Sender" msgstr "Sender" #: data/resources/ui/details_view.ui:101 msgid "Destination" msgstr "Ziel" #: data/resources/ui/details_view.ui:110 msgid "Component" msgstr "Komponente" #: data/resources/ui/details_view.ui:125 msgid "Body Size" msgstr "Rumpfgröße" #: data/resources/ui/details_view.ui:135 msgid "Response Time" msgstr "Antwortzeit" #: data/resources/ui/details_view.ui:145 msgid "Signature" msgstr "Signatur" #: data/resources/ui/details_view.ui:154 msgid "Flags" msgstr "Flags" #: data/resources/ui/details_view.ui:165 msgid "Body" msgstr "Rumpf" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Erweitern" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Dauern" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Die gesamte und mittlere Zeit, die jeder Methodenaufruf brauchte" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Methode" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Gesamt" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Aufrufe" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Mittelwert" #: data/resources/ui/filter_pane.ui:51 msgid "Reset All" msgstr "Alles zurücksetzen" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Häufigkeiten" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Die Häufigkeiten von Methodenaufrufen und Signalen" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Meldungstyp" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Häufigkeit" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Allgemein" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Tastenkürzel anzeigen" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "Neues Fenster" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Beenden" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "Aufzeichnung" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Sitzungsbus aufzeichnen" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Systembus aufzeichnen" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "Adresse aufzeichnen" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "Protokolldatei öffnen" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagramm" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Als PCAP-Datei speichern" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Als DOT-Graph-Datei speichern" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "Dienste filtern" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistiken" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Größen" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Die kleinste, mittlere und größte Größe jeder Meldung" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Kleinste" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Größte" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistiken" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Neues _Fenster" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "Ö_ffnen" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Protokollpaar öffnen" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Speichern unter …" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:558 msgid "DOT Graph" msgstr "DOT-Graph" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistiken" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Tastenkürzel" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Info zu Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Sitzungsbus aufzeichnen" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Systembus aufzeichnen" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Adresse aufzeichnen …" #: data/resources/ui/window.ui:104 data/resources/ui/window.ui:235 msgid "Open Log File" msgstr "Protokolldatei öffnen" #: data/resources/ui/window.ui:112 data/resources/ui/window.ui:167 #: data/resources/ui/window.ui:251 msgid "Main Menu" msgstr "Hauptmenü" #: data/resources/ui/window.ui:119 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Willkommen bei Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:120 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "Starten Sie die Aufzeichnung der D-Bus-Aktivität, Sie können auch den " "folgenden Befehl ausführen" #: data/resources/ui/window.ui:140 msgid "Copy" msgstr "Kopieren" #: data/resources/ui/window.ui:174 msgid "Loading" msgstr "Wird geladen" #. This is a verb #: data/resources/ui/window.ui:219 msgid "_Stop" msgstr "_Stoppen" #: data/resources/ui/window.ui:241 msgid "Filter Services" msgstr "Dienste filtern" #: data/resources/ui/window.ui:257 msgid "Details View" msgstr "Detailansicht" #: data/resources/ui/window.ui:267 msgid "Please Hold" msgstr "Bitte warten" #: data/resources/ui/window.ui:268 msgid "Waiting for D-Bus Traffic…" msgstr "Warten auf D-Bus-Verkehr …" #: src/message.rs:193 msgid "No Reply Expected" msgstr "Keine Antwort erwartet" #: src/message.rs:194 msgid "No Auto Start" msgstr "Kein automatischer Start" #: src/message.rs:196 msgid "Allow Interactive Authentication" msgstr "Interaktive Authentifizierung zulassen" #: src/message_tag.rs:110 msgid "Accessibility" msgstr "Barrierefreiheit" #: src/message_tag.rs:111 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:112 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:113 msgid "Geolocalization" msgstr "Geolokalisierung" #: src/message_tag.rs:114 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:115 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:116 msgid "Input" msgstr "Eingabe" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:118 msgid "Login" msgstr "Anmeldung" #: src/message_tag.rs:119 msgid "Network" msgstr "Netzwerk" #: src/message_tag.rs:120 msgid "Policy Control" msgstr "Richtlinienkontrolle" #: src/message_tag.rs:121 msgid "Portals" msgstr "Portale" #: src/message_tag.rs:122 msgid "Search Provider" msgstr "Suchanbieter" #: src/message_tag.rs:123 msgid "Session" msgstr "Sitzung" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:125 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:126 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:128 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:129 msgid "Power" msgstr "Energie" #: src/message_tag.rs:130 msgid "Unknown" msgstr "Unbekannt" #: src/message_type.rs:34 msgid "Method Call" msgstr "Methodenaufruf" #: src/message_type.rs:35 msgid "Method Return" msgstr "Methodenrückgabe" #: src/message_type.rs:36 msgid "Error" msgstr "Fehler" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:131 msgid "Recording session bus…" msgstr "Aufzeichnung des Sitzungsbusses …" #: src/window.rs:135 msgid "Failed to record session bus" msgstr "Aufzeichnung des Sitzungsbusses fehlgeschlagen" #: src/window.rs:141 msgid "Recording system bus…" msgstr "Aufzeichnung des Systembusses …" #: src/window.rs:146 msgid "Failed to record system bus" msgstr "Aufzeichnung des Systembusses fehlgeschlagen" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:160 msgid "Recording {address}…" msgstr "Aufzeichnung von {address} …" #: src/window.rs:169 msgid "Failed to record address" msgstr "Aufzeichnung der Adresse fehlgeschlagen" #: src/window.rs:186 msgid "Failed to open file" msgstr "Datei konnte nicht geöffnet werden" #: src/window.rs:198 msgid "Failed to save as PCAP" msgstr "Speichern als PCAP fehlgeschlagen" #: src/window.rs:201 msgid "Recording saved as PCAP" msgstr "Aufzeichnung als PCAP gespeichert" #: src/window.rs:212 msgid "Failed to save as DOT graph" msgstr "Speichern als DOT-Graph fehlgeschlagen" #: src/window.rs:215 msgid "Recording saved as DOT graph" msgstr "Aufzeichnung als DOT-Graph gespeichert" #: src/window.rs:407 msgid "translator-credits" msgstr "" "Jürgen Benvenuti \n" "Gabriel Brand " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:497 src/window.rs:521 msgid "PCAP Files" msgstr "PCAP-Dateien" #: src/window.rs:504 msgid "Open Log" msgstr "Protokoll öffnen" #: src/window.rs:528 msgid "Save Log" msgstr "Protokoll speichern" #: src/window.rs:565 msgid "Save Log as DOT Graph File" msgstr "Protokoll als DOT-Graph-Datei speichern" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:634 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} Meldung protokolliert" msgstr[1] "{n_messages} Meldungen protokolliert" #: src/window.rs:651 msgid "Copied to clipboard" msgstr "In die Zwischenablage kopiert" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/en_GB.po000066400000000000000000000367241506133026500224670ustar00rootroot00000000000000# British English translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Andi Chandler , 2024. # Bruce Cowan , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-27 14:45+0000\n" "PO-Revision-Date: 2025-09-02 11:15+0100\n" "Last-Translator: Bruce Cowan \n" "Language-Team: British English \n" "Language: en_GB\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" "X-Generator: Poedit 3.7\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualise D-Bus activity" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "debug;profile;d-bus;dbus;sequence;monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "New Window" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Window width" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Window height" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Window maximised state" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Explore sequence diagrams of D-Bus activity" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "See statistics summarising the log" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Relax with this soothing grey scale welcome page" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "The Bustle developers" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Record Address" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "For more information about addresses, see the D-Bus " "specification." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Cancel" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Record" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Server Address" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Associated Message" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Copy as busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Message" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Type" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Path" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Interface" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Member" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Error Type" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Details" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Sender" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Destination" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Component" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Body Size" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Response Time" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Signature" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Flags" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Body" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Open Format Documentation" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Expand" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Durations" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "The total and mean time every method call took" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Method" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Calls" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Mean" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Select All Tags" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Unselect All Tags" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Select All Names" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Unselect All Names" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Actions" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frequencies" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "The frequencies of method calls and signals" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Message Type" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frequency" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "General" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Show Shortcuts" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "New Window" #: data/resources/ui/shortcuts.ui:21 #| msgid "New Window" msgctxt "shortcut window" msgid "Close Window" msgstr "Close Window" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Quit" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Recording" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Record Session Bus" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Record System Bus" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Record Address" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Open Log File" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Save as PCAP File" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Save as DOT Graph File" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filter Services" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistics" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Sizes" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "The smallest, mean and largest size of every message" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Smallest" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Largest" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistics" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "New _Window" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Open" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Open Pair of Logs" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Save As…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT Graph" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistics" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Keyboard Shortcuts" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_About Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Record Session Bus" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Record System Bus" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Record Address…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Open Log File" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Main Menu" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Welcome to Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Start recording D-Bus activity, you can also run the command" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Copy" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Loading" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Stop" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Filter Services" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Details View" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Please Hold" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Waiting for D-Bus Traffic…" #: src/application.rs:37 msgid "Open a new window" msgstr "Open a new window" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Copied to clipboard" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "Could not generate busctl command" #: src/message.rs:208 msgid "No Reply Expected" msgstr "No Reply Expected" #: src/message.rs:209 msgid "No Auto Start" msgstr "No Auto Start" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Allow Interactive Authentication" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Accessibility" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolocalisation" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Input" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Login" #: src/message_tag.rs:133 msgid "Network" msgstr "Network" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Policy Control" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portals" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Search Provider" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Secrets" #: src/message_tag.rs:138 msgid "Session" msgstr "Session" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Power" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Unknown" #: src/message_type.rs:34 msgid "Method Call" msgstr "Method Call" #: src/message_type.rs:35 msgid "Method Return" msgstr "Method Return" #: src/message_type.rs:36 msgid "Error" msgstr "Error" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Recording session bus…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Failed to record session bus" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Recording system bus…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Failed to record system bus" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Recording {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Failed to record address" #: src/window.rs:194 msgid "Failed to open file" msgstr "Failed to open file" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Failed to save as PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Recording saved as PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Failed to save as DOT graph" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Recording saved as DOT graph" #: src/window.rs:415 msgid "translator-credits" msgstr "" "Andi Chandler \n" "Bruce Cowan " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP Files" #: src/window.rs:517 msgid "Open Log" msgstr "Open Log" #: src/window.rs:542 msgid "Save Log" msgstr "Save Log" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Save Log as DOT Graph File" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Logged {n_messages} message" msgstr[1] "Logged {n_messages} messages" #~ msgid "Reset All" #~ msgstr "Reset All" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/es.po000066400000000000000000000370241506133026500221160ustar00rootroot00000000000000# Spanish translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Sergio Gil , 2024-2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-04-13 14:52+0000\n" "PO-Revision-Date: 2025-05-10 17:30+0200\n" "Last-Translator: Sergio Gil \n" "Language-Team: es_ES \n" "Language: es_ES\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" "X-Generator: Gtranslator 48.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualizar la actividad de D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "depurar;perfilar;d-bus;secuencia;monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Nueva ventana" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Anchura de ventana" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Altura de ventana" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Estado de ventana maximizada" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle representa diagramas de secuencia de la actividad de D-Bus. Muestra " "las emisiones de señales, llamadas de métodos y los retornos de esta, junto " "con marcas de tiempo para cada evento individual y la duración de los " "métodos. Esto puede ayudarle a comprobar si hay actividad de D-Bus no " "deseada y determinar por qué su aplicación basada en D-Bus no rinde como le " "gustaría. También proporciona estadísticas como la frecuencia de las señales " "y los tiempos promedios de llamadas a métodos." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Explora diagramas de secuencia del tráfico de D-Bus." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Ver estadísticas que resumen el diario" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "" "Relájese con esta reconfortante página de bienvenida en escala de grises" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:147 src/window.rs:415 msgid "The Bustle developers" msgstr "Los desarrolladores de Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Dirección del registro" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "Para más información sobre las direcciones, consulte la especificación " "de D-Bus (en inglés)." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Cancelar" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "G_rabar" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Dirección del servidor" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Mensaje asociado" #: data/resources/ui/details_view.ui:35 msgid "Message" msgstr "Mensaje" #: data/resources/ui/details_view.ui:38 msgid "Type" msgstr "Tipo" #: data/resources/ui/details_view.ui:47 msgid "Path" msgstr "Ruta" #: data/resources/ui/details_view.ui:56 msgid "Interface" msgstr "Interfaz" #: data/resources/ui/details_view.ui:65 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Miembro" #: data/resources/ui/details_view.ui:74 msgid "Error Type" msgstr "Tipo de error" #: data/resources/ui/details_view.ui:86 msgid "Details" msgstr "Detalles" #: data/resources/ui/details_view.ui:89 msgid "Sender" msgstr "Remitente" #: data/resources/ui/details_view.ui:98 msgid "Destination" msgstr "Destino" #: data/resources/ui/details_view.ui:107 msgid "Component" msgstr "Componente" #: data/resources/ui/details_view.ui:122 msgid "Body Size" msgstr "Longitud del cuerpo" #: data/resources/ui/details_view.ui:132 msgid "Response Time" msgstr "Tiempo de respuesta" #: data/resources/ui/details_view.ui:142 msgid "Signature" msgstr "Firma" #: data/resources/ui/details_view.ui:151 msgid "Flags" msgstr "Banderas" #: data/resources/ui/details_view.ui:162 msgid "Body" msgstr "Cuerpo" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Expandir" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Duraciones" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "La duración total y media de cada llamada de método" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Método" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Llamadas" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Media" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Seleccionar todas las etiquetas" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Desmarcar todas las etiquetas" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Seleccionar todos los nombres" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Desmarcar todos los nombres" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Acciones" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frecuencias" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Las frecuencias de llamadas de métodos y señales" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Tipo de mensaje" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frecuencia" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "General" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Mostrar atajos del teclado" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "Nueva ventana" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Salir" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "Grabando" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Grabar sesión del bus" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Grabar bus del sistema" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "Grabar dirección" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "Abrir diario" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagrama" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Guardar como PCAP" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Guardar como DOT Graph" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtrar servicios" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "Estadísticas" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Tamaños" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "El tamaño más pequeño, el medio y el más largo de cada mensaje" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "El más pequeño" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Más largo" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Estadísticas" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Nueva _ventana" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Abrir" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Abrir par de diarios" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Guardar como…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:568 msgid "DOT Graph" msgstr "Gráfico DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "E_stadísticas" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "A_tajos de teclado" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "A_cerca de Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Grabar sesión del bus" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Grabar bus del sistema" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Grabar dirección…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Abrir diario" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Menú principal" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Bienvenido a Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "Empezar a grabar actividad de D-Bus. También puede ejecutar el comando." #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Copiar" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Cargando" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Detener" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Filtrar servicios" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Vista de detalles" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Espere" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Esperando tráfico de D-Bus…" #: src/application.rs:37 msgid "Open a new window" msgstr "Abrir una nueva ventana" #: src/message.rs:204 msgid "No Reply Expected" msgstr "No se espera respuesta" #: src/message.rs:205 msgid "No Auto Start" msgstr "No comenzar automáticamente" #: src/message.rs:207 msgid "Allow Interactive Authentication" msgstr "Permitir autenticación interactiva" #: src/message_tag.rs:118 msgid "Accessibility" msgstr "Accesibilidad" #: src/message_tag.rs:119 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:120 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:121 msgid "Geolocalization" msgstr "Geolocalización" #: src/message_tag.rs:122 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:123 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:124 msgid "Input" msgstr "Entrada" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:126 msgid "Login" msgstr "Inicio de sesión" #: src/message_tag.rs:127 msgid "Network" msgstr "Red" #: src/message_tag.rs:128 msgid "Policy Control" msgstr "Control de políticas" #: src/message_tag.rs:129 msgid "Portals" msgstr "Portales" #: src/message_tag.rs:130 msgid "Search Provider" msgstr "Proveedor de búsqueda" #: src/message_tag.rs:131 msgid "Session" msgstr "Sesión" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:133 msgid "Shell" msgstr "Intérprete" #: src/message_tag.rs:134 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:136 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:137 msgid "Power" msgstr "Energía" #: src/message_tag.rs:138 msgid "Unknown" msgstr "Desconocido" #: src/message_type.rs:34 msgid "Method Call" msgstr "Llamada de método" #: src/message_type.rs:35 msgid "Method Return" msgstr "Retorno del método" #: src/message_type.rs:36 msgid "Error" msgstr "Error" #: src/message_type.rs:37 msgid "Signal" msgstr "Señal" #: src/window.rs:134 msgid "Recording session bus…" msgstr "Grabando el bus de sesión…" #: src/window.rs:139 msgid "Failed to record session bus" msgstr "No se pudo grabar el bus de sesión" #: src/window.rs:146 msgid "Recording system bus…" msgstr "Grabando el bus del sistema…" #: src/window.rs:151 msgid "Failed to record system bus" msgstr "No se pudo grabar el bus del sistema" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:168 msgid "Recording {address}…" msgstr "Grabando {address}…" #: src/window.rs:177 msgid "Failed to record address" msgstr "No se pudo grabar la dirección" #: src/window.rs:193 msgid "Failed to open file" msgstr "Error al abrir el archivo" #: src/window.rs:205 msgid "Failed to save as PCAP" msgstr "No se pudo guardar como PCAP" #: src/window.rs:208 msgid "Recording saved as PCAP" msgstr "Grabación guardada como PCAP" #: src/window.rs:219 msgid "Failed to save as DOT graph" msgstr "No se pudo grabar como gráfico DOT" #: src/window.rs:222 msgid "Recording saved as DOT graph" msgstr "Grabación guardada como gráfico DOT" #: src/window.rs:414 msgid "translator-credits" msgstr "Sergio Gil , 2024-2025" #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:508 src/window.rs:533 msgid "PCAP Files" msgstr "Archivos PCAP" #: src/window.rs:516 msgid "Open Log" msgstr "Abrir registro" #: src/window.rs:541 msgid "Save Log" msgstr "Guardar registro" #: src/window.rs:575 msgid "Save Log as DOT Graph File" msgstr "Guardar registro como DOT Graph" # ¿A lo mejor es mejor reemplazar {n_messages} en «1» por «un»? #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:643 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Registrado {n_messages} mensaje" msgstr[1] "Registrados {n_messages} mensajes" #: src/window.rs:660 msgid "Copied to clipboard" msgstr "Copiado al potapapeles" #~ msgid "Reset All" #~ msgstr "Restablecer todo" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/eu.po000066400000000000000000000354441506133026500221240ustar00rootroot00000000000000# Basque translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Asier Sarasua Garmendia , 2024. # msgid "" msgstr "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2024-03-25 18:47+0000\n" "PO-Revision-Date: 2024-03-31 21:06+0000\n" "Last-Translator: Asier Sarasua Garmendia \n" "Language-Team: Basque \n" "Language: eu\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" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Bistaratu D-Bus jarduera" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "arazketa;profila;d-bus;sbus;sekuentzia;monitorizazioa;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Leiho berria" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Leihoaren zabalera" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Leihoaren altuera" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Leiho maximizatuaren egoera" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "Bustle aplikazioak D-Bus jardueraren sekuentzia-diagramak marrazten ditu. Seinale-igorpenak, metodo-deiak eta horiek itzulerak erakusten ditu, bakoizaren denbora-zigiluak eta iraupena ere erakutsiz. Horri esker, nahi gabeko D-Bus trafikoa aztertu daiteke eta D-Bus sisteman oinarritutako aplikazioak zergatik ez diren ongi portatzen. Seinale-maiztasunak eta metodo-deien batez besteko denbora bezalako estatistikak ere eskaintzen ditu." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Arakatu D-Bus jardueraren sekuentzia-diagramak" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Ikusi egunkaria laburbiltzen duten estatistikak" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Erlaxatu gris koloreko ongi etorriko orri honekin" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:134 src/window.rs:408 msgid "The Bustle developers" msgstr "Bustle garatzaileak" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Grabatu helbidea" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "Helbideei buruzko informazio gehiagorako, ikus D-Bus espezifikazioa." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Utzi" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:91 #: data/resources/ui/window.ui:203 msgid "_Record" msgstr "_Grabatu" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Zerbitzari-helbidea" #: data/resources/ui/details_view.ui:22 msgid "Associated Message" msgstr "Lotutako mezua" #: data/resources/ui/details_view.ui:38 msgid "Message" msgstr "Mezua" #: data/resources/ui/details_view.ui:41 msgid "Type" msgstr "Mota" #: data/resources/ui/details_view.ui:50 msgid "Path" msgstr "Bide-izena" #: data/resources/ui/details_view.ui:59 msgid "Interface" msgstr "Interfazea" #: data/resources/ui/details_view.ui:68 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Kidea" #: data/resources/ui/details_view.ui:77 msgid "Error Type" msgstr "Errore mota" #: data/resources/ui/details_view.ui:89 msgid "Details" msgstr "Xehetasunak" #: data/resources/ui/details_view.ui:92 msgid "Sender" msgstr "Bidaltzailea" #: data/resources/ui/details_view.ui:101 msgid "Destination" msgstr "Helburua" #: data/resources/ui/details_view.ui:110 msgid "Component" msgstr "Osagaia" #: data/resources/ui/details_view.ui:125 msgid "Body Size" msgstr "Gorputzaren tamaina" #: data/resources/ui/details_view.ui:135 msgid "Response Time" msgstr "Erantzun-denbora" #: data/resources/ui/details_view.ui:145 msgid "Signature" msgstr "Sinadura" #: data/resources/ui/details_view.ui:154 msgid "Flags" msgstr "Banderak" #: data/resources/ui/details_view.ui:165 msgid "Body" msgstr "Gorputza" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Hedatu" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Iraupenak" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Metodo-dei bakoitzak behar duen batez besteko denbora eta denbora osoa" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Metodoa" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Guztira" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Deiak" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Batez bestekoa" #: data/resources/ui/filter_pane.ui:51 msgid "Reset All" msgstr "Berrezarri dena" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Maiztasunak" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Metodo-deien eta seinaleen maiztasunak" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Mezu mota" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Maiztasuna" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Orokorra" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Erakutsi lasterbideak" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "Leiho berria" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Irten" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "Grabazioa" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Grabazioaren saioaren bus-a" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Grabazioaren sistemaren bus-a" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "Grabatu helbidea" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "Ireki egunkari-fitxategia" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagrama" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Gorde PCAP fitxategi gisa" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Gorde DOT grafikoen fitxategi gisa" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "Iragazi zerbitzuak" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "Estatistikak" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Tamainak" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Mezu bakoitzaren tamaina txikiena, batez bestekoa eta handiena" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Txikiena" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Handiena" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Estatistikak" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Leiho _berria" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Ireki" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Ireki egunkari-bikotea" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Gorde honela…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:558 msgid "DOT Graph" msgstr "DOT grafikoa" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Estatistikak" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "Las_ter-teklak" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "Bustle aplikazioari _buruz" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Grabatu saioaren bus-a" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Grabatu sistemaren bus-a" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Grabatu helbidea…" #: data/resources/ui/window.ui:104 data/resources/ui/window.ui:235 msgid "Open Log File" msgstr "Ireki egunkari-fitxategia" #: data/resources/ui/window.ui:112 data/resources/ui/window.ui:167 #: data/resources/ui/window.ui:251 msgid "Main Menu" msgstr "Menu nagusia" #: data/resources/ui/window.ui:119 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Ongi etorri Bustle aplikaziora" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:120 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Hasi D-Bus jarduera grabatzen, honako komandoa ere exekutatu dezakezu:" #: data/resources/ui/window.ui:140 msgid "Copy" msgstr "Kopiatu" #: data/resources/ui/window.ui:174 msgid "Loading" msgstr "Kargatzen" #. This is a verb #: data/resources/ui/window.ui:219 msgid "_Stop" msgstr "_Gelditu" #: data/resources/ui/window.ui:241 msgid "Filter Services" msgstr "Iragazi zerbitzuak" #: data/resources/ui/window.ui:257 msgid "Details View" msgstr "Xehetasunen ikuspegia" #: data/resources/ui/window.ui:267 msgid "Please Hold" msgstr "Itxaron" #: data/resources/ui/window.ui:268 msgid "Waiting for D-Bus Traffic…" msgstr "D-Bus trafikoaren zain…" #: src/message.rs:193 msgid "No Reply Expected" msgstr "Ez da erantzunik espero" #: src/message.rs:194 msgid "No Auto Start" msgstr "Ez dago abio automatikorik" #: src/message.rs:196 msgid "Allow Interactive Authentication" msgstr "Onartu autentifikazio interaktiboa" #: src/message_tag.rs:110 msgid "Accessibility" msgstr "Erabilerraztasuna" #: src/message_tag.rs:111 msgid "Bluetooth" msgstr "Bluetootha" #: src/message_tag.rs:112 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:113 msgid "Geolocalization" msgstr "Geokokapena" #: src/message_tag.rs:114 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:115 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:116 msgid "Input" msgstr "Sarrera" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:118 msgid "Login" msgstr "Saio-hasiera" #: src/message_tag.rs:119 msgid "Network" msgstr "Sarea" #: src/message_tag.rs:120 msgid "Policy Control" msgstr "Politika-kontrola" #: src/message_tag.rs:121 msgid "Portals" msgstr "Atariak" #: src/message_tag.rs:122 msgid "Search Provider" msgstr "Bilaketa-hornitzailea" #: src/message_tag.rs:123 msgid "Session" msgstr "Saioa" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:125 msgid "Shell" msgstr "Shell-a" #: src/message_tag.rs:126 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:128 msgid "Tracker" msgstr "Aztarnaria" #: src/message_tag.rs:129 msgid "Power" msgstr "Energia" #: src/message_tag.rs:130 msgid "Unknown" msgstr "Ezezaguna" #: src/message_type.rs:34 msgid "Method Call" msgstr "Metodo-deia" #: src/message_type.rs:35 msgid "Method Return" msgstr "Metodo-itzulera" #: src/message_type.rs:36 msgid "Error" msgstr "Errorea" #: src/message_type.rs:37 msgid "Signal" msgstr "Seinalea" #: src/window.rs:131 msgid "Recording session bus…" msgstr "Saioaren bus-a grabatzen…" #: src/window.rs:135 msgid "Failed to record session bus" msgstr "Saioaren bus-a grabatzeak huts egin du" #: src/window.rs:141 msgid "Recording system bus…" msgstr "Sistemaren bus-a grabatzen…" #: src/window.rs:146 msgid "Failed to record system bus" msgstr "Sistemaren bus-a grabatzeak huts egin du" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:160 msgid "Recording {address}…" msgstr "{address} grabatzen…" #: src/window.rs:169 msgid "Failed to record address" msgstr "Helbidea grabatzeak huts egin du" #: src/window.rs:186 msgid "Failed to open file" msgstr "Fitxategia irekitzeak huts egin du" #: src/window.rs:198 msgid "Failed to save as PCAP" msgstr "PCAP gisa gordetzeak huts egin du" #: src/window.rs:201 msgid "Recording saved as PCAP" msgstr "Grabazioa PCAP gisa gorde da" #: src/window.rs:212 msgid "Failed to save as DOT graph" msgstr "DOT grafiko gisa gordetzeak huts egin du" #: src/window.rs:215 msgid "Recording saved as DOT graph" msgstr "Grabazioa DOT grafiko gisa gorde da" #: src/window.rs:407 msgid "translator-credits" msgstr "translator-credits" #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:497 src/window.rs:521 msgid "PCAP Files" msgstr "PCAP fitxategiak" #: src/window.rs:504 msgid "Open Log" msgstr "Ireki egunkaria" #: src/window.rs:528 msgid "Save Log" msgstr "Gorde egunkaria" #: src/window.rs:565 msgid "Save Log as DOT Graph File" msgstr "Gorde egunkaria DOT grafikoen fitxategi gisa" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:634 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Mezu {n_messages} erregistratu da" msgstr[1] "{n_messages} mezu erregistratu dira" #: src/window.rs:651 msgid "Copied to clipboard" msgstr "Arbelean kopiatu da" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/fr.po000066400000000000000000000345751506133026500221260ustar00rootroot00000000000000# French translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Alexandre Franke , 2024. # Vincent Chatelain , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2024-02-09 11:39+0000\n" "PO-Revision-Date: 2024-02-10 11:40+0100\n" "Last-Translator: Vincent Chatelain \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" "X-Generator: Gtranslator 45.3\n" "X-DamnedLies-Scope: partial\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualisez l’activité D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "déboguer;profiler;d-bus;dbus;séquencer;surveiller;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Nouvelle fenêtre" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle dessine des diagrammes de séquence sur l’activité D-Bus. Ces " "diagrammes montrent les émissions des signaux, les appels de méthodes et " "leurs résultats, avec un marqueur temporel pour chaque évènement unique et " "la durée de chaque appel de méthode. Cela peut vous permettre de vérifier un " "trafic D-Bus inattendu, et de comprendre pourquoi votre application " "utilisant D-Bus n’atteint pas les performances attendues. Bustle fournit " "également des statistiques, notamment sur les fréquence des signaux et les " "durées moyennes d’appel de méthode." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Explorer les diagrammes séquence de l’activité de D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Voir les statistiques résumant le journal" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "" "Détendez-vous avec cette page d’accueil reposante tout en nuances du gris" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:119 src/window.rs:409 msgid "The Bustle developers" msgstr "Les développeurs de Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Capturer l’adresse" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Pour plus d’informations sur les adresses, référez-vous à la spécification D-Bus (en anglais)." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "A_nnuler" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:91 #: data/resources/ui/window.ui:203 msgid "_Record" msgstr "_Capturer" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Adresse du serveur" #: data/resources/ui/details_view.ui:22 msgid "Associated Message" msgstr "Message associé" #: data/resources/ui/details_view.ui:38 msgid "Message" msgstr "Message" #: data/resources/ui/details_view.ui:41 msgid "Type" msgstr "Type" #: data/resources/ui/details_view.ui:50 msgid "Path" msgstr "Chemin" #: data/resources/ui/details_view.ui:59 msgid "Interface" msgstr "Interface" #: data/resources/ui/details_view.ui:68 msgid "Member" msgstr "Membre" #: data/resources/ui/details_view.ui:77 msgid "Error Type" msgstr "Type d’erreur" #: data/resources/ui/details_view.ui:89 msgid "Details" msgstr "Détails" #: data/resources/ui/details_view.ui:92 msgid "Sender" msgstr "Émetteur" #: data/resources/ui/details_view.ui:101 msgid "Destination" msgstr "Destination" #: data/resources/ui/details_view.ui:110 msgid "Component" msgstr "Composant" #: data/resources/ui/details_view.ui:125 msgid "Body Size" msgstr "Taille du contenu" #: data/resources/ui/details_view.ui:135 msgid "Response Time" msgstr "Temps de réponse" #: data/resources/ui/details_view.ui:145 msgid "Signature" msgstr "Signature" #: data/resources/ui/details_view.ui:154 msgid "Flags" msgstr "Drapeaux" #: data/resources/ui/details_view.ui:165 msgid "Body" msgstr "Contenu" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Développer" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Durées" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Le temps moyen et total pour chaque appel de méthode" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Méthode" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Appels" #: data/resources/ui/durations_page.ui:110 msgid "Mean" msgstr "Moyen" #: data/resources/ui/filter_pane.ui:51 msgid "Reset All" msgstr "Tout réinitialiser" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Fréquences" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Les fréquences des appels de méthode et des signaux" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Général" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Afficher les raccourcis" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "Nouvelle fenêtre" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Quitter" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "Capture en cours" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Capturer le bus session" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Capturer le bus système" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "Capturer l’adresse" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "Ouvrir un fichier journal" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagramme" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Enregistrer comme fichier PCAP" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Enregistrer comme graphe DOT" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtrer les services" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistiques" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Tailles" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "La taille minimale, moyenne et maximale de chaque message" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistiques" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Nouvelle _fenêtre" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Ouvrir" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Ouvrir une paire de journaux" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Enregistrer sous…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:559 msgid "DOT Graph" msgstr "Graphe DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistiques" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "Raccourcis c_lavier" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "À _propos de Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Capturer le bus session" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Capturer le bus système" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Capturer l’adresse…" #: data/resources/ui/window.ui:104 data/resources/ui/window.ui:235 msgid "Open Log File" msgstr "Ouvrir un fichier journal" #: data/resources/ui/window.ui:112 data/resources/ui/window.ui:167 #: data/resources/ui/window.ui:251 msgid "Main Menu" msgstr "Menu principal" #: data/resources/ui/window.ui:119 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Bienvenue sur Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:120 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "Commencez à capturer l’activité D-Bus. Vous pouvez également exécuter la " "commande" #: data/resources/ui/window.ui:140 msgid "Copy" msgstr "Copier" #: data/resources/ui/window.ui:174 msgid "Loading" msgstr "Chargement" #. This is a verb #: data/resources/ui/window.ui:219 msgid "_Stop" msgstr "_Arrêter" #: data/resources/ui/window.ui:241 msgid "Filter Services" msgstr "Filtrer les services" #: data/resources/ui/window.ui:257 msgid "Details View" msgstr "Vue détaillée" #: data/resources/ui/window.ui:267 msgid "Please Hold" msgstr "Veuillez patienter" #: data/resources/ui/window.ui:268 msgid "Waiting for D-Bus Traffic…" msgstr "Attente du trafic D-Bus…" #: src/message.rs:193 msgid "No Reply Expected" msgstr "Aucun réponse attendue" #: src/message.rs:194 msgid "No Auto Start" msgstr "Pas de démarrage automatique" #: src/message.rs:196 msgid "Allow Interactive Authentication" msgstr "Autoriser l’authentification interactive" #: src/message_tag.rs:110 msgid "Accessibility" msgstr "Accessibilité" #: src/message_tag.rs:111 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:112 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:113 msgid "Geolocalization" msgstr "Géolocalisation" #: src/message_tag.rs:114 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:115 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:116 msgid "Input" msgstr "Entrée" #: src/message_tag.rs:117 msgid "Login" msgstr "Identifiant" #: src/message_tag.rs:118 msgid "Network" msgstr "Réseau" #: src/message_tag.rs:119 msgid "Policy Control" msgstr "Réglementation" #: src/message_tag.rs:120 msgid "Portals" msgstr "Portails" #: src/message_tag.rs:121 msgid "Search Provider" msgstr "Fournisseur de services de recherche" #: src/message_tag.rs:122 msgid "Session" msgstr "Session" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:124 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:125 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:127 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:128 msgid "Power" msgstr "Puissance" #: src/message_tag.rs:129 msgid "Unknown" msgstr "Inconnu" #: src/message_type.rs:34 msgid "Method Call" msgstr "Appel de méthode" #: src/message_type.rs:35 msgid "Method Return" msgstr "Retour de méthode" #: src/message_type.rs:36 msgid "Error" msgstr "Erreur" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:132 msgid "Recording session bus…" msgstr "Capture du bus session…" #: src/window.rs:136 msgid "Failed to record session bus" msgstr "Échec de capture du bus session" #: src/window.rs:142 msgid "Recording system bus…" msgstr "Capture du bus système…" #: src/window.rs:147 msgid "Failed to record system bus" msgstr "Échec de capture du bus système" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:161 msgid "Recording {address}…" msgstr "Capture de {address}…" #: src/window.rs:170 msgid "Failed to record address" msgstr "Échec de capture de l’adresse" #: src/window.rs:187 msgid "Failed to open file" msgstr "Échec d’ouverture du fichier" #: src/window.rs:199 msgid "Failed to save as PCAP" msgstr "Échec de capture en tant que PCAP" #: src/window.rs:202 msgid "Recording saved as PCAP" msgstr "Capture enregistrée en tant que PCAP" #: src/window.rs:213 msgid "Failed to save as DOT graph" msgstr "Échec de capture en tant que graphe DOT" #: src/window.rs:216 msgid "Recording saved as DOT graph" msgstr "Capture enregistré en tant graphe DOT" #: src/window.rs:408 msgid "translator-credits" msgstr "Vincent Chatelain " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:498 src/window.rs:522 msgid "PCAP Files" msgstr "Fichiers PCAP" #: src/window.rs:505 msgid "Open Log" msgstr "Ouvrir un journal" #: src/window.rs:529 msgid "Save Log" msgstr "Enregistrer le journal" #: src/window.rs:566 msgid "Save Log as DOT Graph File" msgstr "Enregistrer le journal en tant que graphe DOT" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:635 msgid "Logged {n_messages} messages" msgstr "{n_messages} messages journalisés" #: src/window.rs:650 msgid "Copied to clipboard" msgstr "Copié vers le presse-papier" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/he.po000066400000000000000000000407311506133026500221020ustar00rootroot00000000000000# Hebrew translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Yaron Shahrabani , 2024. # Yosef Or Boczko , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-03-08 18:45+0000\n" "PO-Revision-Date: 2025-03-14 11:47+0200\n" "Last-Translator: Yaron Shahrabani \n" "Language-Team: Hebrew\n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? " "2 : 3);\n" "X-Generator: Poedit 3.5\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "המחשת הפעילות של D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "" "ניפוי שגיאות;פרופיל;דיבאס;די-באס;די-בס;d-bus;רצף;תרשים " "הרצף;מעקב;צג;מוניטור;אפיק המערכת;אפיק נתונים;אפיק ההפעלה;אפיק הפעלה;אפיק " "מערכת;אפיק;באס;בס;באסטל;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "חלון חדש" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "רוחב חלון" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "גובה חלון" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "חלון במצב מוגדל" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "היישום Bustle מצייר תרשימי רצף של פעילות D-Bus. הוא מציג פליטות של אותות, " "קריאות לשיטות והערכים שחוזרים מהן, עם חותמת זמן לכל אירוע ואירוע והמשך של כל " "קריאה לשיטה. הניתוח הזה יכול לסייע לך לבדוק אם מתקיימת תעבורת D-Bus לא רצויה " "ולהצביע על הסיבה לכך שהיישום מבוסס ה־D-Bus שלך לא מתפקד כמו שציפית. היישום " "מספק גם סטטיסטיקה כגון תדירות האותו ואת משך הזמן הממוצע לקריאות לשיטות." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "חקירת תרשימי רצף של פעילות D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "הצגת סטטיסטיקה שמסכמת את היומן" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "אפשר לנשום לרווחה מול מסך קבלת הפנים הזה בגווני אפור" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:137 src/window.rs:415 msgid "The Bustle developers" msgstr "מפתחי Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "הקלטת כתובת" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "למידע נוסף על כתובות, ניתן לראות במפרט של D-Bus." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_ביטול" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "ה_קלטה" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "כתובת שרת" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "הודעה משויכת" #: data/resources/ui/details_view.ui:35 msgid "Message" msgstr "הודעה" #: data/resources/ui/details_view.ui:38 msgid "Type" msgstr "סוג" #: data/resources/ui/details_view.ui:47 msgid "Path" msgstr "נתיב" #: data/resources/ui/details_view.ui:56 msgid "Interface" msgstr "מנשק" #: data/resources/ui/details_view.ui:65 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "חבר" #: data/resources/ui/details_view.ui:74 msgid "Error Type" msgstr "סוג שגיאה" #: data/resources/ui/details_view.ui:86 msgid "Details" msgstr "פרטים" #: data/resources/ui/details_view.ui:89 msgid "Sender" msgstr "מוען" #: data/resources/ui/details_view.ui:98 msgid "Destination" msgstr "יעד" #: data/resources/ui/details_view.ui:107 msgid "Component" msgstr "רכיב" #: data/resources/ui/details_view.ui:122 msgid "Body Size" msgstr "גודל מסר" #: data/resources/ui/details_view.ui:132 msgid "Response Time" msgstr "זמן תגובה" #: data/resources/ui/details_view.ui:142 msgid "Signature" msgstr "חתימה" #: data/resources/ui/details_view.ui:151 msgid "Flags" msgstr "דגלונים" #: data/resources/ui/details_view.ui:162 msgid "Body" msgstr "מסר" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "הרחבה" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "פרקי זמן" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "הזמן הכולל והממוצע שלקח לכל קריאה לשיטה" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "שיטה" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "סך הכול" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "קריאות" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "ממוצע" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "בחירה של כל התגיות" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "ביטול בחירת התגיות" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "בחירת כל השמות" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "ביטול בחירת השמות" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "פעולות" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "תדירויות" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "תדירויות שיטות הקריאה והאותות" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "סוג הודעה" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "תדירות" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "כללי" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "הצגת קיצורי מקלדת" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "חלון חדש" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "יציאה" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "הקלטה" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "הקלטת אפיק הפעלה" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "הקלטת אפיק מערכת" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "הקלטת כתובת" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "פתיחת קובץ יומן" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "תרשים" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "שמירה כקובץ PCAP" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "שמירה כקובץ תרשים DOT" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "סינון שירותים" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "סטטיסטיקה" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "גדלים" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "הגודל הקטן ביותר, הממוצע והגדול ביותר של כל הודעה" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "הקטן ביותר" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "הגדול ביותר" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "סטטיסטיקה" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "חלון _חדש" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_פתיחה" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "פתיחת צמד יומנים" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "שמירה בשם…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:568 msgid "DOT Graph" msgstr "תרשים DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_סטטיסטיקה" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_קיצורי מקלדת" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_על Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "הקלטת אפיק ההפעלה" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "הקלטת אפיק מערכת" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "הקלטת כתובת…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "פתיחת קובץ יומן" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "תפריט ראשי" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "ברוך בואך ל־Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "אפשר להתחיל להקליט את הפעילות של D-Bus, אפשר גם להריץ את הפקודה" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "העתקה" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "טעינה" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_עצירה" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "סינון שירותים" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "תצוגה מפורטת" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "נא להמתין" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "בהמתנה לתעבורת D-Bus…" #: src/message.rs:204 msgid "No Reply Expected" msgstr "לא הייתה ציפייה לתגובה" #: src/message.rs:205 msgid "No Auto Start" msgstr "אין התחלה אוטומטית" #: src/message.rs:207 msgid "Allow Interactive Authentication" msgstr "לאפשר אימות אינטראקטיבי" #: src/message_tag.rs:118 msgid "Accessibility" msgstr "נגישות" #: src/message_tag.rs:119 msgid "Bluetooth" msgstr "בלוטות׳" #: src/message_tag.rs:120 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:121 msgid "Geolocalization" msgstr "מיקום גאוגרפי" #: src/message_tag.rs:122 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:123 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:124 msgid "Input" msgstr "קלט" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:126 msgid "Login" msgstr "כניסה" #: src/message_tag.rs:127 msgid "Network" msgstr "רשת" #: src/message_tag.rs:128 msgid "Policy Control" msgstr "בקרת מדיניות" #: src/message_tag.rs:129 msgid "Portals" msgstr "שערים" #: src/message_tag.rs:130 msgid "Search Provider" msgstr "ספק חיפוש" #: src/message_tag.rs:131 msgid "Session" msgstr "הפעלה" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:133 msgid "Shell" msgstr "מעטפת" #: src/message_tag.rs:134 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:136 msgid "Tracker" msgstr "עוקב" #: src/message_tag.rs:137 msgid "Power" msgstr "חשמל" #: src/message_tag.rs:138 msgid "Unknown" msgstr "לא ידוע" #: src/message_type.rs:34 msgid "Method Call" msgstr "קריאה לשיטה" #: src/message_type.rs:35 msgid "Method Return" msgstr "החזרה משיטה" #: src/message_type.rs:36 msgid "Error" msgstr "שגיאה" #: src/message_type.rs:37 msgid "Signal" msgstr "אות" #: src/window.rs:134 msgid "Recording session bus…" msgstr "אפיק ההפעלה מוקלט…" #: src/window.rs:139 msgid "Failed to record session bus" msgstr "הקלטת אפיק ההפעלה נכשלה" #: src/window.rs:146 msgid "Recording system bus…" msgstr "אפיק המערכת מוקלט…" #: src/window.rs:151 msgid "Failed to record system bus" msgstr "הקלטת אפיק המערכת נכשלה" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:168 msgid "Recording {address}…" msgstr "מתבצעת הקלטה של {address}…" #: src/window.rs:177 msgid "Failed to record address" msgstr "הקלטת הכתובת נכשלה" #: src/window.rs:193 msgid "Failed to open file" msgstr "פתיחת הקובץ נכשלה" #: src/window.rs:205 msgid "Failed to save as PCAP" msgstr "השמירה כ־PCAP נכשלה" #: src/window.rs:208 msgid "Recording saved as PCAP" msgstr "ההקלטה נשמרה כ־PCAP" #: src/window.rs:219 msgid "Failed to save as DOT graph" msgstr "השמירה כתרשים DOT נכשלה" #: src/window.rs:222 msgid "Recording saved as DOT graph" msgstr "ההקלטה נשמרה כתרשים DOT" #: src/window.rs:414 msgid "translator-credits" msgstr "ירון שהרבני " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:508 src/window.rs:533 msgid "PCAP Files" msgstr "קובצי PCAP" #: src/window.rs:516 msgid "Open Log" msgstr "פתיחת היומן" #: src/window.rs:541 msgid "Save Log" msgstr "שמירת היומן" #: src/window.rs:575 msgid "Save Log as DOT Graph File" msgstr "שמירת היומן כקובץ תרשים DOT" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:643 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "תועדה הודעה אחת" msgstr[1] "תועדו שתי הודעות" msgstr[2] "תועדו {n_messages} הודעות" msgstr[3] "תועדו {n_messages} הודעות" #: src/window.rs:660 msgid "Copied to clipboard" msgstr "הועתק ללוח הגזירים" #~ msgid "Reset All" #~ msgstr "איפוס של הכול" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/hi.po000066400000000000000000000446541506133026500221160ustar00rootroot00000000000000# Hindi translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Scrambled777 , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2024-04-01 19:50+0000\n" "PO-Revision-Date: 2024-04-05 19:40+0530\n" "Last-Translator: Scrambled777 \n" "Language-Team: Hindi\n" "Language: hi\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" "X-Generator: Gtranslator 46.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "डी-बस गतिविधि को देखें" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "डिबग;प्रोफाइल;डी-बस;डीबस;अनुक्रम;निगरानी;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "नई विंडो" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "विंडो चौड़ाई" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "विंडो ऊंचाई" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "विंडो अधिकतमीकृत स्थिति" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle डी-बस गतिविधि के अनुक्रम आरेख बनाता है। यह प्रत्येक व्यक्तिगत घटना के लिए समय " "मोहरों और प्रत्येक विधि कॉल की अवधि के साथ संकेत उत्सर्जन, विधि कॉल और उनके संबंधित " "विवरणियां दिखाता है। यह आपको अवांछित डी-बस ट्रैफ़िक की जाँच करने में मदद कर सकता है, और " "यह पता लगा सकता है कि आपका डी-बस-आधारित अनुप्रयोग आपकी इच्छानुसार अच्छा प्रदर्शन क्यों " "नहीं कर रहा है। यह संकेत आवृत्तियों और औसत विधि कॉल समय जैसे आँकड़े भी प्रदान करता है।" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "डी-बस गतिविधि के अनुक्रम आरेखों का अन्वेषण करें" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "लॉग को सारांशित करने वाले आँकड़े देखें" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "इस सुखदायक ग्रेस्केल स्वागत पृष्ठ के साथ आराम करें" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:134 src/window.rs:408 msgid "The Bustle developers" msgstr "Bustle विकासक" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "पता रिकॉर्ड करें" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "पतों के बारे में अधिक जानकारी के लिए, D-Bus विशिष्टता देखें।" #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "रद्द (_C)" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:91 #: data/resources/ui/window.ui:203 msgid "_Record" msgstr "रिकॉर्ड (_R)" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "सर्वर पता" #: data/resources/ui/details_view.ui:22 msgid "Associated Message" msgstr "संबद्ध संदेश" #: data/resources/ui/details_view.ui:38 msgid "Message" msgstr "संदेश" #: data/resources/ui/details_view.ui:41 msgid "Type" msgstr "प्रकार" #: data/resources/ui/details_view.ui:50 msgid "Path" msgstr "पथ" #: data/resources/ui/details_view.ui:59 msgid "Interface" msgstr "इंटरफ़ेस" #: data/resources/ui/details_view.ui:68 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "सदस्य" #: data/resources/ui/details_view.ui:77 msgid "Error Type" msgstr "त्रुटि प्रकार" #: data/resources/ui/details_view.ui:89 msgid "Details" msgstr "विवरण" #: data/resources/ui/details_view.ui:92 msgid "Sender" msgstr "प्रेषक" #: data/resources/ui/details_view.ui:101 msgid "Destination" msgstr "गंतव्य" #: data/resources/ui/details_view.ui:110 msgid "Component" msgstr "अवयव" #: data/resources/ui/details_view.ui:125 msgid "Body Size" msgstr "शारीरिक आकार" #: data/resources/ui/details_view.ui:135 msgid "Response Time" msgstr "प्रतिक्रिया समय" #: data/resources/ui/details_view.ui:145 msgid "Signature" msgstr "हस्ताक्षर" #: data/resources/ui/details_view.ui:154 msgid "Flags" msgstr "झंडे" #: data/resources/ui/details_view.ui:165 msgid "Body" msgstr "तन" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "विस्तृत" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "अवधियां" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "प्रत्येक विधि कॉल में लिया गया कुल और औसत समय" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "विधि" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "कुल" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "कॉल" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "औसत" #: data/resources/ui/filter_pane.ui:51 msgid "Reset All" msgstr "सभी रीसेट करें" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "आवृत्तियाँ" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "विधि कॉल और संकेतों की आवृत्तियाँ" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "संदेश प्रकार" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "आवृत्ति" #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" msgstr "सामान्य" #: data/resources/ui/shortcuts.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "शॉर्टकट दिखाएँ" #: data/resources/ui/shortcuts.ui:20 msgctxt "shortcut window" msgid "New Window" msgstr "नई विंडो" #: data/resources/ui/shortcuts.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "छोड़ें" #: data/resources/ui/shortcuts.ui:34 msgctxt "shortcut window" msgid "Recording" msgstr "रिकॉर्डिंग" #: data/resources/ui/shortcuts.ui:37 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "सत्र बस रिकॉर्ड करें" #: data/resources/ui/shortcuts.ui:43 msgctxt "shortcut window" msgid "Record System Bus" msgstr "प्रणाली बस रिकॉर्ड करें" #: data/resources/ui/shortcuts.ui:49 msgctxt "shortcut window" msgid "Record Address" msgstr "पता रिकॉर्ड करें" #: data/resources/ui/shortcuts.ui:55 msgctxt "shortcut window" msgid "Open Log File" msgstr "लॉग फाइल खोलें" #: data/resources/ui/shortcuts.ui:63 msgctxt "shortcut window" msgid "Diagram" msgstr "आरेख" #: data/resources/ui/shortcuts.ui:66 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "PCAP फाइल के रूप में सहेजें" #: data/resources/ui/shortcuts.ui:72 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "DOT रेखाचित्र फाइल के रूप में सहेजें" #: data/resources/ui/shortcuts.ui:78 msgctxt "shortcut window" msgid "Filter Services" msgstr "सेवाएं छानें" #: data/resources/ui/shortcuts.ui:84 msgctxt "shortcut window" msgid "Statistics" msgstr "आंकड़े" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "आकार" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "प्रत्येक संदेश का सबसे छोटा, औसत और सबसे बड़ा आकार" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "सबसे छोटा" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "सबसे बड़ा" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "आंकड़े" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "नई विंडो (_W)" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "खोलें (_O)" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "लॉग की जोड़ी खोलें" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "ऐसे सहेजें…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:558 msgid "DOT Graph" msgstr "DOT रेखाचित्र" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "आंकड़े (_S)" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "कीबोर्ड शॉर्टकट (_K)" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "Bustle के बारे में (_A)" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "सत्र बस रिकॉर्ड करें" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "प्रणाली बस रिकॉर्ड करें" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "पता रिकार्ड करें…" #: data/resources/ui/window.ui:104 data/resources/ui/window.ui:235 msgid "Open Log File" msgstr "लॉग फाइल खोलें" #: data/resources/ui/window.ui:112 data/resources/ui/window.ui:167 #: data/resources/ui/window.ui:251 msgid "Main Menu" msgstr "मुख्य मेनू" #: data/resources/ui/window.ui:119 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Bustle में आपका स्वागत है" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:120 msgid "Start recording D-Bus activity, you can also run the command" msgstr "डी-बस गतिविधि रिकॉर्ड करना प्रारंभ करें, आप कमांड भी चला सकते हैं" #: data/resources/ui/window.ui:140 msgid "Copy" msgstr "कॉपी" #: data/resources/ui/window.ui:174 msgid "Loading" msgstr "लोड हो रहा है" #. This is a verb #: data/resources/ui/window.ui:219 msgid "_Stop" msgstr "रोकें (_S)" #: data/resources/ui/window.ui:241 msgid "Filter Services" msgstr "सेवाएं छानें" #: data/resources/ui/window.ui:257 msgid "Details View" msgstr "विवरण दृश्य" #: data/resources/ui/window.ui:267 msgid "Please Hold" msgstr "कृपया प्रतीक्षा करें" #: data/resources/ui/window.ui:268 msgid "Waiting for D-Bus Traffic…" msgstr "डी-बस यातायात की प्रतीक्षा में…" #: src/message.rs:193 msgid "No Reply Expected" msgstr "कोई उत्तर अपेक्षित नहीं" #: src/message.rs:194 msgid "No Auto Start" msgstr "कोई स्वतः प्रारंभ नहीं" #: src/message.rs:196 msgid "Allow Interactive Authentication" msgstr "अंतर्क्रियात्मक प्रमाणीकरण की अनुमति दें" #: src/message_tag.rs:110 msgid "Accessibility" msgstr "अभिगम्यता" #: src/message_tag.rs:111 msgid "Bluetooth" msgstr "ब्लूटूथ" #: src/message_tag.rs:112 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:113 msgid "Geolocalization" msgstr "भू-स्थानीकरण" #: src/message_tag.rs:114 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:115 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:116 msgid "Input" msgstr "इनपुट" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:118 msgid "Login" msgstr "लॉग इन" #: src/message_tag.rs:119 msgid "Network" msgstr "नेटवर्क" #: src/message_tag.rs:120 msgid "Policy Control" msgstr "नीति नियंत्रण" #: src/message_tag.rs:121 msgid "Portals" msgstr "पोर्टल" #: src/message_tag.rs:122 msgid "Search Provider" msgstr "खोज प्रदाता" #: src/message_tag.rs:123 msgid "Session" msgstr "सत्र" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:125 msgid "Shell" msgstr "शैल" #: src/message_tag.rs:126 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:128 msgid "Tracker" msgstr "ट्रैकर" #: src/message_tag.rs:129 msgid "Power" msgstr "ऊर्जा" #: src/message_tag.rs:130 msgid "Unknown" msgstr "अज्ञात" #: src/message_type.rs:34 msgid "Method Call" msgstr "विधि कॉल" #: src/message_type.rs:35 msgid "Method Return" msgstr "विधि वापसी" #: src/message_type.rs:36 msgid "Error" msgstr "त्रुटि" #: src/message_type.rs:37 msgid "Signal" msgstr "संकेत" #: src/window.rs:131 msgid "Recording session bus…" msgstr "सत्र बस रिकॉर्ड कर रहा है…" #: src/window.rs:135 msgid "Failed to record session bus" msgstr "सत्र बस रिकॉर्ड करने में विफल" #: src/window.rs:141 msgid "Recording system bus…" msgstr "प्रणाली बस रिकॉर्ड कर रहा है…" #: src/window.rs:146 msgid "Failed to record system bus" msgstr "प्रणाली बस रिकॉर्ड करने में विफल" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:160 msgid "Recording {address}…" msgstr "रिकॉर्डिंग {address}…" #: src/window.rs:169 msgid "Failed to record address" msgstr "पता रिकार्ड करने में असफल" #: src/window.rs:186 msgid "Failed to open file" msgstr "फाइल खोलने में असफल" #: src/window.rs:198 msgid "Failed to save as PCAP" msgstr "PCAP के रूप में सहेजने में विफल" #: src/window.rs:201 msgid "Recording saved as PCAP" msgstr "रिकॉर्डिंग PCAP के रूप में सहेजी गई" #: src/window.rs:212 msgid "Failed to save as DOT graph" msgstr "DOT रेखाचित्र के रूप में सहेजने में विफल" #: src/window.rs:215 msgid "Recording saved as DOT graph" msgstr "रिकॉर्डिंग को DOT रेखाचित्र के रूप में सहेजा गया" #: src/window.rs:407 msgid "translator-credits" msgstr "Scrambled777 " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:497 src/window.rs:521 msgid "PCAP Files" msgstr "PCAP फाइलें" #: src/window.rs:504 msgid "Open Log" msgstr "लॉग खोलें" #: src/window.rs:528 msgid "Save Log" msgstr "लॉग सहेजें" #: src/window.rs:565 msgid "Save Log as DOT Graph File" msgstr "लॉग को DOT रेखाचित्र फाइल के रूप में सहेजें" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:634 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} संदेश को लॉग किया गया" msgstr[1] "{n_messages} संदेशों को लॉग किया गया" #: src/window.rs:651 msgid "Copied to clipboard" msgstr "क्लिपबोर्ड पर कॉपी किया गया" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/hu.po000066400000000000000000000405171506133026500221240ustar00rootroot00000000000000# Hungarian translation for Bustle. # Copyright (C) 2024, 2025 Free Software Foundation, Inc. # This file is distributed under the same license as the bustle package. # # Balázs Úr , 2024, 2025. msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-08-21 15:41+0000\n" "PO-Revision-Date: 2025-08-25 17:06+0200\n" "Last-Translator: Balázs Úr \n" "Language-Team: Hungarian \n" "Language: hu\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" "X-Generator: Lokalize 24.12.3\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "D-busz tevékenység láthatóvá tétele" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "hibakeresés;profil;d-bus;d-busz;dbus;sorozat;szekvencia;megfigyelés;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Új ablak" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Ablak szélessége" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Ablak magassága" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Ablak teljes méretű állapota" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "A Bustle szekvenciadiagramokat rajzol a D-busz-tevékenységről. Megjeleníti a " "szignálok kibocsátását, a metódushívásokat és a hozzájuk tartozó " "visszatérési értékeket az egyes eseményekhez tartozó időbélyegekkel és az " "egyes metódushívások időtartamával. Ez segíthet a nem kívánt D-busz-forgalom " "ellenőrzésében, és pontosan megállapíthatja, hogy a D-busz-alapú alkalmazás " "miért nem teljesít olyan jól, ahogy szeretné. Olyan statisztikákat is " "szolgáltat, mint a szignálok gyakorisága és az átlagos metódushívási idők." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "A D-busz-tevékenység szekvenciadiagramjainak felfedezése" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "A naplót összegző statisztikák megtekintése" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Lazítson ezzel a megnyugtató szürkeárnyalatos üdvözlő oldallal" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "A Bustle fejlesztői" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Cím rögzítése" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "A címekkel kapcsolatos további információkért nézze meg a D-busz " "specifikációját." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Mégse" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Rögzítés" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Kiszolgáló címe" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Hozzárendelt üzenet" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Másolás busctl-ként" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Üzenet" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Típus" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Útvonal" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Csatoló" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Tag" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Hiba típusa" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Részletek" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Küldő" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Cél" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Összetevő" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Törzs mérete" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Válaszidő" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Aláírás" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Jelzők" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Törzs" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Formázási dokumentáció megnyitása" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Kinyitás" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Időtartamok" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Az egyes metódushívások teljes és átlagos időtartama" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Metódus" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Összesen" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Hívások" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Átlag" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Összes címke kijelölése" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Összes címke kijelölésének megszüntetése" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Összes név kijelölése" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Összes név kijelölésének megszüntetése" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Műveletek" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Gyakoriságok" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "A metódushívások és szignálok gyakorisága" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Üzenet típusa" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Gyakoriság" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Általános" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Gyorsbillentyűk megjelenítése" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Új ablak" #: data/resources/ui/shortcuts.ui:21 #| msgid "New Window" msgctxt "shortcut window" msgid "Close Window" msgstr "Ablak bezárása" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Kilépés" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Rögzítés" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Munkamenetbusz rögzítése" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Rendszerbusz rögzítése" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Cím rögzítése" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Naplófájl megnyitása" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Mentés PCAP-fájlként" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Mentés DOT-grafikonfájlként" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Szolgáltatások szűrése" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statisztikák" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Méretek" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Az egyes üzenetek legkisebb, átlagos és legnagyobb mérete" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Legkisebb" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Legnagyobb" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statisztikák" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Új _ablak" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Megnyitás" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Naplópárok megnyitása" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Mentés másként…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT-grafikon" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statisztikák" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Gyorsbillentyűk" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "A Bustle _névjegye" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Munkamenetbusz rögzítése" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Rendszerbusz rögzítése" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Cím rögzítése…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Naplófájl megnyitása" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Főmenü" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Üdvözli a Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "A D-busz-tevékenység rögzítésének megkezdése, a következő parancsot is " "futtathatja" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Másolás" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Betöltés" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Leállítás" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Szolgáltatások szűrése" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Részletek nézet" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Tartsa nyomva" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Várakozás a D-busz-forgalomra…" #: src/application.rs:37 msgid "Open a new window" msgstr "Új ablak megnyitása" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Vágólapra másolva" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "Nem sikerült előállítani a busctl parancsot" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Nem várható válasz" #: src/message.rs:209 msgid "No Auto Start" msgstr "Nincs automatikus indítás" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Interaktív hitelesítés engedélyezése" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Akadálymentesítés" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolokalizáció" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Bemenet" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Bejelentkezés" #: src/message_tag.rs:133 msgid "Network" msgstr "Hálózat" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Irányelvek ellenőrzése" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portálok" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Keresési szolgáltató" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Titkok" #: src/message_tag.rs:138 msgid "Session" msgstr "Munkamenet" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Energiagazdálkodás" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Ismeretlen" #: src/message_type.rs:34 msgid "Method Call" msgstr "Metódushívás" #: src/message_type.rs:35 msgid "Method Return" msgstr "Metódus-visszatérés" #: src/message_type.rs:36 msgid "Error" msgstr "Hiba" #: src/message_type.rs:37 msgid "Signal" msgstr "Szignál" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Munkamenetbusz rögzítése…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Nem sikerült rögzíteni a munkamenetbuszt" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Rendszerbusz rögzítése…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Nem sikerült rögzíteni a rendszerbuszt" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "A(z) {address} rögzítése…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Nem sikerült rögzíteni a címet" #: src/window.rs:194 msgid "Failed to open file" msgstr "Nem sikerült megnyitni a fájlt" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Nem sikerült elmenteni PCAP-ként" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Rögzítés elmentve PCAP-ként" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Nem sikerült elmenteni DOT-grafikonként" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Rögzítés elmentve DOT-grafikonként" #: src/window.rs:415 msgid "translator-credits" msgstr "Úr Balázs , 2024." #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP-fájlok" #: src/window.rs:517 msgid "Open Log" msgstr "Napló megnyitása" #: src/window.rs:542 msgid "Save Log" msgstr "Napló mentése" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Napló mentése DOT-grafikonfájlként" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} üzenet naplózva" msgstr[1] "{n_messages} üzenet naplózva" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/ia.po000066400000000000000000000343271506133026500221030ustar00rootroot00000000000000# Interlingua translation for bustle. # Copyright (C) 2025 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # FIRST AUTHOR , YEAR. # Emilio Sepúlveda , 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-04 10:51+0000\n" "PO-Revision-Date: 2025-07-10 19:55-0400\n" "Last-Translator: Emilio Sepúlveda \n" "Language-Team: Interlingua \n" "Language: ia\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" "X-DL-Lang: ia\n" "X-DL-Module: bustle\n" "X-DL-Branch: main\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" "X-Generator: Gtranslator 48.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualisa le activitate de D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "depuration;profilo;d-bus;dbus;sequentia;monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Nove fenestra" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Largor de fenestra" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Altor de fenestra" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Stato maximisate de fenestra" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Le disveloppatores de Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Registrar adresse" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Cancellar" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Registrar" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Adresse de servitor" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Message associate" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Copiar como busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Message" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Typo" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Percurso" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Interfacie" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Membro" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Typo de error" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Detalios" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Inviator" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Destination" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Componente" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Dimension de corpore" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Tempore de responsa" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Signatura" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Bandieras" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Corpore" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Aperir documentation de formato" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Expander" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Durationes" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Methodo" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Appellos" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Seliger tote le etiquettas" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Disseliger tote le etiquettas" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Seliger tote le nomines" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Disseliger tote le nomines" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Actiones" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frequentias" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Le frequentias de signales e appellos de methodo" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Typo de message" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frequentia" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "General" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Monstrar vias breve" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Nove fenestra" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Clauder fenestra" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Quitar" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Registration" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Registrar bus de session" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Registrar bus de systema" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Registrar adresse" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Aperir file de registro" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Salvar como file PCAP" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Salvar como file de grapho DOT" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtrar servicios" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statisticas" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Dimensiones" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Le plus parve" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Le plus longe" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statisticas" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "_Nove fenestra" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Aperir" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Salvar como…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "Grapho DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statisticas" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "Vias breve de _claviero" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_A proposito de Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Benvenite a Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Copiar" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Stoppar" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Vista de detalios" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "" #: src/application.rs:37 msgid "Open a new window" msgstr "Aperir un nove fenestra" #: src/details_view.rs:265 src/window.rs:670 msgid "Copied to clipboard" msgstr "Copiate al area de transferentia" #: src/details_view.rs:269 src/details_view.rs:273 msgid "Could not generate busctl command" msgstr "" #: src/message.rs:208 msgid "No Reply Expected" msgstr "" #: src/message.rs:209 msgid "No Auto Start" msgstr "" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Accessibilitate" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "" #: src/message_tag.rs:128 msgid "GTK" msgstr "" #: src/message_tag.rs:129 msgid "GVFS" msgstr "" #: src/message_tag.rs:130 msgid "Input" msgstr "" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Initio de session" #: src/message_tag.rs:133 msgid "Network" msgstr "Rete" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "" #: src/message_tag.rs:135 msgid "Portals" msgstr "" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "" #: src/message_tag.rs:137 msgid "Secrets" msgstr "" #: src/message_tag.rs:138 msgid "Session" msgstr "Session" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "" #: src/message_tag.rs:141 msgid "systemd" msgstr "" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "" #: src/message_tag.rs:144 msgid "Power" msgstr "" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Incognite" #: src/message_type.rs:34 msgid "Method Call" msgstr "" #: src/message_type.rs:35 msgid "Method Return" msgstr "" #: src/message_type.rs:36 msgid "Error" msgstr "Error" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "" #: src/window.rs:147 msgid "Recording system bus…" msgstr "" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "" #: src/window.rs:178 msgid "Failed to record address" msgstr "" #: src/window.rs:194 msgid "Failed to open file" msgstr "" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "" #: src/window.rs:415 msgid "translator-credits" msgstr "Emilio Sepúlveda " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "" #: src/window.rs:517 msgid "Open Log" msgstr "" #: src/window.rs:542 msgid "Save Log" msgstr "" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "" msgstr[1] "" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/meson.build000066400000000000000000000002021506133026500232750ustar00rootroot00000000000000i18n.gettext(gettext_package, args: ['--keyword=gettext_f', '--keyword=ngettext_f:1,2'], preset: 'glib')bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/nl.po000066400000000000000000000377141506133026500221260ustar00rootroot00000000000000# Dutch translation for bustle. # Copyright (C) 2025 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Cirilla , 2025. # Nathan Follens , 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-27 14:45+0000\n" "PO-Revision-Date: 2025-08-21 17:39+0200\n" "Last-Translator: Nathan Follens \n" "Language-Team: GNOME-NL https://matrix.to/#/#nl:gnome.org\n" "Language: nl\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" "X-DL-Lang: nl\n" "X-DL-Module: bustle\n" "X-DL-Branch: main\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" "X-Generator: Poedit 3.6\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualiseer D-Bus-activiteit" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "debug;profile;d-bus;dbus;sequence;monitor;profiel;sequentie;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Nieuw venster" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Vensterbreedte" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Vensterhoogte" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Venster in gemaximaliseerde staat" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle tekent sequentiediagrammen van D-Bus-activiteit. Het toont " "signaalemissies, methode-oproepen en hun overeenkomende returns, met " "tijdstempels voor elke aparte gebeurtenis en de duur van elke methode-" "oproep. Dit kan u helpen om ongewenst D-Bus-verkeer op te sporen, en na te " "gaan waarom uw toepassing gebaseerd op D-Bus niet zo vloeiend werkt als u " "zou willen. Het levert ook statistieken zoals signaalfrequenties en de " "gemiddelde duur van methode-oproepen." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Ontdek sequentiediagrammen van D-Bus-activiteit" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Bekijk samenvattende statistieken over het log" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Ontspan met deze kalmerende welkomstpagina in grijstinten" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "De ontwikkelaars van Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Adres opnemen" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus " "specification." msgstr "" "Bekijk de D-Bus-specificatie voor meer informatie " "over adressen." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Annuleren" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Opnemen" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Serveradres" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Bijbehorend bericht" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Kopiëren als busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Bericht" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Type" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Pad" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Interface" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Lid" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Fouttype" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Details" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Afzender" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Bestemming" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Component" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Inhoudsgrootte" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Reactietijd" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Ondertekening" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Vlaggen" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Inhoud" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Formaatdocumentatie openen" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Uitvouwen" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Duur" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "De totale en gemiddelde duur voor elke methode-oproep" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Methode" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Totaal" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Oproepen" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Gemiddeld" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Alle tags selecteren" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Alle tags deselecteren" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Alle namen selecteren" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Alle namen deselecteren" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Acties" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frequenties" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "De frequenties van methode-oproepen en signalen" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Berichttype" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frequentie" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Algemeen" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Sneltoetsen tonen" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Nieuw venster" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Venster sluiten" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Afsluiten" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Opnemen" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Sessiebus opnemen" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Systeembus opnemen" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Adres opnemen" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Logbestand openen" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Opslaan als PCAP-bestand" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Opslaan als DOT-grafiekbestand" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Diensten filteren" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistieken" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Groottes" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "De kleinste, gemiddelde en grootste groottes van elk bericht" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Kleinst" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Grootst" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistieken" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Nieuw _venster" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Openen" # ? - Nathan #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Logboekpaar openen" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Opslaan als…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT-grafiek" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistieken" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "Sneltoetsen" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Over Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Sessiebus opnemen" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Systeembus opnemen" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Adres opnemen…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Logbestand openen" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Hoofdmenu" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Welkom bij Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "Begin met opnemen van D-Bus-activiteit, u kunt ook de opdracht uitvoeren" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Kopiëren" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Bezig met laden" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Stoppen" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Diensten filteren" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Detailoverzicht" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Even geduld" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Wachten op D-Bus-verkeer…" #: src/application.rs:37 msgid "Open a new window" msgstr "Een nieuw venster openen" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Gekopieerd naar klembord" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "Kon busctl-opdracht niet genereren" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Geen antwoord verwacht" # Misschien nog aan te passen - Nathan #: src/message.rs:209 msgid "No Auto Start" msgstr "Start niet automatisch" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Interactieve aanmelding toestaan" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Toegankelijkheid" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolokalisatie" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Invoer" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Gebruikersnaam" #: src/message_tag.rs:133 msgid "Network" msgstr "Netwerk" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Beleidscontrole" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portalen" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Zoekprovider" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Geheimen" #: src/message_tag.rs:138 msgid "Session" msgstr "Sessie" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Energie" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Onbekend" #: src/message_type.rs:34 msgid "Method Call" msgstr "Methode-oproep" # ? - Nathan #: src/message_type.rs:35 msgid "Method Return" msgstr "Methode-return" #: src/message_type.rs:36 msgid "Error" msgstr "Fout" #: src/message_type.rs:37 msgid "Signal" msgstr "Signaal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Sessiebus wordt opgenomen…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Kon sessiebus niet opnemen" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Systeembus wordt opgenomen…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Kon systeembus niet opnemen" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "{address} wordt opgenomen…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Kon adres niet opnemen" #: src/window.rs:194 msgid "Failed to open file" msgstr "Kon bestand niet openen" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Kon niet opslaan als PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Opname opgeslagen als PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Kon niet opslaan als DOT-grafiek" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Opname opgeslagen als DOT-grafiek" #: src/window.rs:415 msgid "translator-credits" msgstr "" "Cirilla \n" "Nathan Follens \n" "Meer info over GNOME-NL https://nl.gnome.org" #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP-bestanden" #: src/window.rs:517 msgid "Open Log" msgstr "Logboek openen" #: src/window.rs:542 msgid "Save Log" msgstr "Log opslaan" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Log opslaan als DOT-grafiekbestand" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} bericht gelogd" msgstr[1] "{n_messages} berichten gelogd" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/pt_BR.po000066400000000000000000000403131506133026500225100ustar00rootroot00000000000000# Brazilian Portuguese translation for bustle. # Copyright (C) 2025 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Jhonata Fernandes , 2024. # Rafael Fontenelle , 2024-2025. # Álvaro Burns <>, 2025. # Juliano de Souza Camargo , 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-09-10 11:24+0000\n" "PO-Revision-Date: 2025-09-13 10:55-0300\n" "Last-Translator: Juliano de Souza Camargo \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\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" "X-Generator: Gtranslator 48.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualize atividade do D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "depuração;perfil;d-bus;dbus;sequência;debug;profile;monitor;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Nova janela" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Largura da janela" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Altura da janela" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Estado maximizado da janela" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle desenha diagramas de sequência da atividade do D-Bus. Ele mostra " "emissões de sinais, chamadas de métodos e seus retornos correspondentes, com " "carimbos de data/hora para cada evento individual e a duração de cada " "chamada de método. Isso pode ajudá-lo a verificar tráfego indesejado de D-" "Bus e identificar por que seu aplicativo baseado em D-Bus não está " "funcionando tão bem quanto você gostaria. Ele também fornece estatísticas " "como frequências de sinal e tempos médios de chamada de método." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Explore diagramas de sequência da atividade do D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Veja estatísticas resumindo o log" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Relaxe com esta página de boas-vindas relaxante em tons de cinza" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Os desenvolvedores do Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Gravar endereço" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Para mais informações sobre endereços, veja a especificação do D-" "Bus." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Cancelar" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Gravar" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Endereço do servidor" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Mensagem associada" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Copiar como busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Mensagem" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Tipo" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Caminho" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Interface" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Membro" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Tipo de erro" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Detalhes" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Remetente" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Destinatário" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Componente" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Tamanho do corpo" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Tempo de resposta" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Assinatura" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Sinalizadores" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Corpo" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Abrir documentação de formatação" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Expandir" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Durações" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "O tempo total e médio que cada chamada de método levou" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Método" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Chamadas" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Média" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Selecionar todas tags" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Desmarcar seleção de todas tags" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Selecionar todos nomes" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Desmarcar seleção de todos nomes" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Ações" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frequências" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "As frequências de sinais de chamadas de métodos" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Tipo de mensagem" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frequência" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Geral" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Mostrar atalhos" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Nova janela" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Fecha a janela" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Sai" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Gravação" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Grava o barramento da sessão" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Grava o barramento do sistema" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Grava um endereço" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Abri um arquivo de log" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagrama" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Salva como arquivo PCAP" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Salva como arquivo de grafo DOT" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtra serviços" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Estatísticas" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Tamanhos" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "O menor, médio e maior tamanho de cada mensagem" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Menor" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Maior" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Estatísticas" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Nova _janela" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Abrir" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Abrir par de logs" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Salvar como…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "Grafo DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Estatísticas" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "Atalhos de _teclado" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Sobre o Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Gravar barramento de sessão" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Gravar barramento de sistema" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Gravar endereço…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Abrir arquivo log" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Menu principal" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Boas vindas ao Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "" "Comece a gravar a atividade do D-Bus. Você também pode executar o comando" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Copiar" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Carregando" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Parar" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Filtrar serviços" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Visão de detalhes" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Por favor, aguarde" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Aguardando pelo tráfego do D-Bus…" #: src/application.rs:37 msgid "Open a new window" msgstr "Abrir uma nova janela" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Copiado para a área de transferências" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "Não foi possível gerar comando busctl" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Nenhuma resposta esperada" #: src/message.rs:209 msgid "No Auto Start" msgstr "Sem autoinicialização" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Permitir autenticação interativa" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Acessibilidade" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolocalização" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Entrada" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Login" #: src/message_tag.rs:133 msgid "Network" msgstr "Rede" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Controle de políticas" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portais" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Provedor de pesquisa" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Segredos" #: src/message_tag.rs:138 msgid "Session" msgstr "Sessão" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Energia" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Desconhecido" #: src/message_type.rs:34 msgid "Method Call" msgstr "Chamada de método" #: src/message_type.rs:35 msgid "Method Return" msgstr "Retorno de método" #: src/message_type.rs:36 msgid "Error" msgstr "Erro" #: src/message_type.rs:37 msgid "Signal" msgstr "Sinal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Gravando barramento de sessão…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Falha ao gravar barramento de sessão" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Gravando barramento de sistema…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Falha ao gravar barramento de sistema" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Gravando {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Falha ao gravar endereço" #: src/window.rs:194 msgid "Failed to open file" msgstr "Falha ao abrir arquivo" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Falha ao salvar como PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Gravação salva como PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Falha ao salvar como grafo DOT" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Gravação salva como grafo DOT" #: src/window.rs:415 msgid "translator-credits" msgstr "" "Rafael Fontenelle \n" "Jhonata Fernandes " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "Arquivos PCAP" #: src/window.rs:517 msgid "Open Log" msgstr "Abrir log" #: src/window.rs:542 msgid "Save Log" msgstr "Salvar log" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Salva o log como arquivo de grafo DOT" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Registrada {n_messages} mensagem" msgstr[1] "Registradas {n_messages} mensagens" #~ msgid "Reset All" #~ msgstr "Redefinir tudo" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/ru.po000066400000000000000000000450711506133026500221360ustar00rootroot00000000000000# Russian translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Sergej A. , 2024. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-15 19:18+0000\n" "PO-Revision-Date: 2025-07-25 23:58+1000\n" "Last-Translator: Ser82-png \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.0.1\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Визуализация активности D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "debug;profile;d-bus;dbus;sequence;monitor;отладка;профиль;монитор;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Новое окно" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Ширина окна" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Высота окна" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Состояние развернутого окна" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle рисует диаграммы последовательности действий D-Bus. На них " "отображаются места появления сигналов, вызовы методов и соответствующие им " "результаты, с временными метками для каждого отдельного события и " "длительностью каждого вызова метода. Это может помочь выявить наличие " "нежелательного трафика по D-Bus и точно определить, почему ваше основанное " "на D-Bus приложение работает не так хорошо, как вам хотелось бы. Приложение " "также предоставляет статистические данные, такие как частота сигналов и " "среднее время вызова метода." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Изучите диаграммы последовательностей вызовов D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Просмотрите обобщённую статистику в журнале" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "" "Расслабьтесь во время показа этой успокаивающей приветственной серой страницы" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Разработчики Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Записать адрес" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Для получения дополнительной информации об адресах смотрите спецификацию D-Bus." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Отменить" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Записать" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Адрес сервера" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Связанное сообщение" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Копировать как busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Сообщение" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Тип" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Путь" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Интерфейс" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Член" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Тип ошибки" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Подробности" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Отправитель" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Назначение" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Компонент" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Размер содержимого" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Время отклика" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Подпись" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Флаги" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Содержимое" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Документация в открытом формате" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Развернуть" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Продолжительность" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Общее и среднее время, затраченное на каждый вызов метода" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Метод" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Общее" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Вызовы" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Среднее" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Выделить все метки" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Снять выделение со всех меток" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Выделить все названия" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Снять выделение со всех названий" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Действия" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Частоты" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Частота методов вызовов и сигналов" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Тип сообщения" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Частота" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Общее" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Показать комбинации клавиш" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Новое окно" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Закрыть окно" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Выход" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Запись" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Записать сессионную шину" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Записать системную шину" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Записать адрес" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Открыть файл журнала" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Диаграмма" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Сохранить как файл PCAP" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Сохранить как файл описания графов DOT" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Фильтровать службы" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Статистика" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Размеры" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Наименьший, средний и наибольший размер каждого сообщения" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Наименьший" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Наибольший" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Статистика" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Новое _окно" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Открыть" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Открыть пару журналов" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Сохранить как…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT Graph" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Статистика" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Комбинации клавиш" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_О приложении" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Записать сессионную шину" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Записать системную шину" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Записать адрес…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Открыть файл журнала" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Главное меню" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Добро пожаловать в Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Начать запись активности D-Bus вы можете из командной строки" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Копировать" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Загрузка" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Остановить" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Фильтровать службы" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Просмотр деталей" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Пожалуйста, подождите" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Ожидание обмена данными D-Bus…" #: src/application.rs:37 msgid "Open a new window" msgstr "Открыть новое окно" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Скопировано в буфер обмена" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "Не удалось сгенерировать команду busctl" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Ответ не ожидается" #: src/message.rs:209 msgid "No Auto Start" msgstr "Без автозапуска" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Разрешить интерактивную авторизацию" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Специальные возможности" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Геолокация" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Ввод" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Вход" #: src/message_tag.rs:133 msgid "Network" msgstr "Сеть" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Управление политиками" #: src/message_tag.rs:135 msgid "Portals" msgstr "Порталы" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Поисковый провайдер" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Ключи" #: src/message_tag.rs:138 msgid "Session" msgstr "Сессия" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Питание" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Неизвестно" #: src/message_type.rs:34 msgid "Method Call" msgstr "Вызов метода" #: src/message_type.rs:35 msgid "Method Return" msgstr "Возврат результата" #: src/message_type.rs:36 msgid "Error" msgstr "Ошибка" #: src/message_type.rs:37 msgid "Signal" msgstr "Сигнал" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Запись сессионной шины…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Не удалось записать сессионную шину" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Запись системной шины…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Не удалось записать системную шину" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Запись {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Не удалось записать адрес" #: src/window.rs:194 msgid "Failed to open file" msgstr "Не удалось открыть файл" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Не удалось сохранить как PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Запись сохранена как PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Не удалось сохранить как файл описания графов DOT" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Запись сохранена как файл описания графов DOT" #: src/window.rs:415 msgid "translator-credits" msgstr "Ser82-png " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "Файлы PCAP" #: src/window.rs:517 msgid "Open Log" msgstr "Открыть журнал" #: src/window.rs:542 msgid "Save Log" msgstr "Сохранить журнал" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Сохранить журнал как файл описания графов DOT" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "В журнал записано {n_messages} сообщение" msgstr[1] "В журнал записано {n_messages} сообщения" msgstr[2] "В журнал записано {n_messages} сообщений" #~ msgid "Reset All" #~ msgstr "Сбросить всё" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/sl.po000066400000000000000000000377441506133026500221360ustar00rootroot00000000000000# Slovenian translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # # Martin , 2024,2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-03 15:55+0000\n" "PO-Revision-Date: 2025-07-03 22:04+0200\n" "Last-Translator: Martin Srebotnjak \n" "Language-Team: Slovenian \n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n" "%100<=4 ? 2 : 3);\n" "X-Generator: Poedit 2.2.1\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Ponazoritev dejavnosti vodila D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "" "debug;razhroščevanje;odpravljanje;napake;napak;profil;profiliranje;vodilo;d-" "bus;dbus; zaporedje; monitor;beleženje;spremljanje;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Novo okno" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Širina okna" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Višina okna" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Stanje razpetega glavnega okna" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle nariše diagrame zaporedja dejavnosti D-Bus. Prikazuje emisije " "signalov, klice metod in njihove ustrezne donose, s časovnimi žigi za vsak " "posamezen dogodek in trajanjem vsakega klica metode. To vam lahko pomaga " "preveriti neželen promet D-Bus in ugotoviti, zakaj vaš program, ki temelji " "na D-Busu, ne deluje tako dobro, kot želite. Zagotavlja tudi statistične " "podatke, kot so frekvence signalov in povprečni čas klicev." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Raziščite diagrame zaporedja dejavnosti D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Oglejte si statistične podatke, ki povzemajo zapisnik" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Sprostite se s to pomirjujočo pozdravno sivinsko stranjo" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Razvijalci programa Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Beleži naslov" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Za več informacij o naslovih glejte specifikacijo D-Bus." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "Pre_kliči" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Posnemi" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Naslov strežnika" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Povezano sporočilo" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Kopiraj kot busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Sporočilo" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Vrsta" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Pot" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Vmesnik" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Član" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Vrsta napake" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Podrobnosti" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Pošiljatelj" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Cilj" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Komponenta" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Velikost telesa" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Odzivni čas" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Podpis" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Zastavice" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Telo" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Dokumentacija odprtega zapisa" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Razširi" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Trajanja" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Skupni in povprečni čas, ki ga je opravil vsak klic metode" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Metoda" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Skupno" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Klici" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Povprečna vrednost" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Izberi vse oznake" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Prekliči izbor vseh oznak" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Izberi vsa imena" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Prekliči izbor vseh imen" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Dejanja" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frekvence" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Frekvence klicev in signalov metod" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Vrsta sporočila" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frekvenca" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Splošno" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Pokaže tipkovne bližnjice‫" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Novo okno" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Zapre okno" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Končaj" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Snemanje" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Snemaj vodilo seje" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Snemaj sistemsko vodilo" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Beleži naslov" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Odpri zapisniško datoteko" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Shrani kot datoteko PCAP" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Shrani kot datoteko grafikon DOT" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtriraj storitve" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistika" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Velikosti" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Najmanjša, srednja in največja velikost vsakega sporočila" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "najmanjša" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "največja" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistika" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Novo _okno" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Odpri" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Odpri par zapisnikov" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Shrani kot …" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "Grafikon DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistika" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Tipkovne bližnjice" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_O programu Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Snemaj vodilo seje" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Snemaj sistemsko vodilo" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Snemaj naslov ..." #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Odpri zapisniško datoteko" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Glavni meni" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Dobrodošli v Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Začnite snemati dejavnost D-Busa, lahko tudi zaženete ukaz" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Kopiraj" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Nalaganje" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Zaustavi" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Filtriraj storitve" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Podroben pogled" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Počakajte trenutek" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Čakanje na promet D-Bus ..." #: src/application.rs:37 msgid "Open a new window" msgstr "Odpri novo okno" #: src/details_view.rs:265 src/window.rs:670 msgid "Copied to clipboard" msgstr "Kopirano na odložišče" #: src/details_view.rs:269 src/details_view.rs:273 msgid "Could not generate busctl command" msgstr "Ukaza busctl ni bilo mogoče ustvariti" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Odgovor ni pričakovan" #: src/message.rs:209 msgid "No Auto Start" msgstr "Brez samodejnega zagona" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Dovoli interaktivno preverjanje pristnosti" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Dostopnost" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolokalizacija" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Vhod" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Prijava" #: src/message_tag.rs:133 msgid "Network" msgstr "Omrežje" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Nadzor pravilnika" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portali" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Ponudnik iskanja" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Skrivnosti" #: src/message_tag.rs:138 msgid "Session" msgstr "Seja" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Lupina" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Sledilnik" #: src/message_tag.rs:144 msgid "Power" msgstr "Napajanje" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Neznano" #: src/message_type.rs:34 msgid "Method Call" msgstr "Klic metode" #: src/message_type.rs:35 msgid "Method Return" msgstr "Vračilo metode" #: src/message_type.rs:36 msgid "Error" msgstr "Napaka" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Snemanje vodila seje ..." #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Snemanje vodila sejne ni uspelo" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Snemanje sistemskega vodila ..." #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Sistemskega vodila ni bilo mogoče posneti" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Snemanje {address} ..." #: src/window.rs:178 msgid "Failed to record address" msgstr "Beleženje naslova ni uspelo" #: src/window.rs:194 msgid "Failed to open file" msgstr "Odpiranje datoteke ni uspelo" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Ni bilo mogoče shraniti kot PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Posnetek, shranjeno kot PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Ni bilo mogoče shraniti kot grafikon DOT" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Posnetek, shranjen kot grafikon DOT" #: src/window.rs:415 msgid "translator-credits" msgstr "Martin Srebotnjak " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "Datoteke PCAP" #: src/window.rs:517 msgid "Open Log" msgstr "Odpri zapisnik" #: src/window.rs:542 msgid "Save Log" msgstr "Shrani zapisnik" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Shrani zapisnik kot datoteko DOT Graph" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Zabeleženo {n_messages} sporočilo" msgstr[1] "Zabeleženi {n_messages} sporočili" msgstr[2] "Zabeležena {n_messages} sporočila" msgstr[3] "Zabeleženih {n_messages} sporočil" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/sv.po000066400000000000000000000374211506133026500221400ustar00rootroot00000000000000# Swedish translation for bustle. # Copyright © 2024-2025 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # Anders Jonsson , 2024, 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-03 15:55+0000\n" "PO-Revision-Date: 2025-06-15 21:37+0200\n" "Last-Translator: Anders Jonsson \n" "Language-Team: Swedish \n" "Language: sv\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" "X-Generator: Poedit 3.6\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Visualisera D-Bus-aktivitet" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "debug;felsök;profil;d-bus;dbus;sekvens;övervakning;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Nytt fönster" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Fönsterbredd" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Fönsterhöjd" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Maximerat tillstånd för fönster" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle ritar sekvensdiagram med D-Bus-aktivitet. Den visar " "signalutsändningar, metodanrop och deras motsvarande returer, med " "tidsstämplar för varje individuell händelse och varaktigheten för varje " "metodanrop. Detta kan hjälpa dig söka oönskad D-Bus-trafik, och peka ut " "varför ditt D-Bus-baserade program inte har så bra prestanda som du önskar. " "Det tillhandahåller också statistik som signalfrekvenser och genomsnittlig " "metodanropstid." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Utforska sekvensdiagram med D-Bus-aktivitet" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Se statistik som sammanfattar loggen" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Slappna av med denna avkopplande gråtonade välkomstsida" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Bustles utvecklare" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Spela in adress" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "För mer information om adresser, se D-Bus-specifikationen." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "A_vbryt" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "S_pela in" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Serveradress" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Associerat meddelande" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Kopiera som busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Meddelande" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Typ" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Sökväg" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Gränssnitt" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Medlem" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Feltyp" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Detaljer" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Avsändare" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Mål" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Komponent" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Textstorlek" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Svarstid" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Signatur" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Flaggor" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Text" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Öppna formatdokumentation" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Expandera" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Varaktigheter" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Den totala tiden och medeltiden som varje metodanrop tog" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Metod" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Total" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Anrop" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Medelvärde" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Markera alla taggar" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Avmarkera alla taggar" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Markera alla namn" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Avmarkera alla namn" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Åtgärder" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frekvenser" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Frekvenserna för metodanrop och signaler" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Meddelandetyp" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Frekvens" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Allmänt" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Visa kortkommandon" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Nytt fönster" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Stäng fönster" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Avsluta" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Inspelning" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Spela in sessionsbuss" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Spela in systembuss" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Spela in adress" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Öppna loggfil" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Spara som PCAP-fil" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Spara som DOT-graffil" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Filtrera tjänster" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Statistik" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Storlekar" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Den minsta, genomsnittliga och största storleken för varje meddelande" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Minsta" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Största" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Statistik" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Nytt _fönster" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "Ö_ppna" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Öppna par med loggar" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Spara som…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT-graf" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "_Statistik" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Tangentbordsgenvägar" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Om Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Spela in sessionsbuss" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Spela in systembuss" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Spela in adress…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Öppna loggfil" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Huvudmeny" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Välkommen till Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Börja spela in D-Bus-aktivitet, du kan också köra kommandot" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Kopiera" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Läser in" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Stoppa" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Filtrera tjänster" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Detaljvy" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Vänta" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Väntar på D-Bus-trafik…" #: src/application.rs:37 msgid "Open a new window" msgstr "Öppna ett nytt fönster" #: src/details_view.rs:265 src/window.rs:670 msgid "Copied to clipboard" msgstr "Kopierat till urklipp" #: src/details_view.rs:269 src/details_view.rs:273 msgid "Could not generate busctl command" msgstr "Kunde inte generera busctl-kommando" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Inget svar väntades" #: src/message.rs:209 msgid "No Auto Start" msgstr "Ingen autostart" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Tillåt interaktiv autentisering" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Hjälpmedel" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Geolokalisering" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Inmatning" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Inloggning" #: src/message_tag.rs:133 msgid "Network" msgstr "Nätverk" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Policykontroll" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portaler" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Sökleverantör" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Hemligheter" #: src/message_tag.rs:138 msgid "Session" msgstr "Session" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Skal" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Ström" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Okänd" #: src/message_type.rs:34 msgid "Method Call" msgstr "Metodanrop" #: src/message_type.rs:35 msgid "Method Return" msgstr "Metodretur" #: src/message_type.rs:36 msgid "Error" msgstr "Fel" #: src/message_type.rs:37 msgid "Signal" msgstr "Signal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Spelar in sessionsbuss…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Misslyckades med att spela in sessionsbuss" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Spelar in systembuss…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Misslyckades med att spela in systembuss" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Spelar in {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Misslyckades med att spela in adress" #: src/window.rs:194 msgid "Failed to open file" msgstr "Misslyckades med att öppna fil" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Misslyckades med att spara som PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Inspelning sparad som PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Misslyckades med att spara som DOT-graf" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Inspelning sparad som DOT-graf" #: src/window.rs:415 msgid "translator-credits" msgstr "" "Anders Jonsson \n" "\n" "Skicka synpunkter på översättningen till\n" "." #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP-filer" #: src/window.rs:517 msgid "Open Log" msgstr "Öppna logg" #: src/window.rs:542 msgid "Save Log" msgstr "Spara logg" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Spara logg som DOT-graffil" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "Loggade {n_messages} meddelande" msgstr[1] "Loggade {n_messages} meddelanden" #~ msgid "Reset All" #~ msgstr "Återställ alla" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/tr.po000066400000000000000000000400241506133026500221260ustar00rootroot00000000000000# Turkish translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # # Sabri Ünal , 2024. # Emin Tufan Çetin , 2024. # Muhammet Kara , 2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-08-25 15:07+0000\n" "PO-Revision-Date: 2025-08-28 22:59+0300\n" "Last-Translator: Sabri Ünal \n" "Language-Team: Turkish \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.6\n" #: data/org.freedesktop.Bustle.desktop.in.in:2 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "D-Bus etkinliğini görselleştir" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:9 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "" "debug;profile;d-bus;dbus;sequence;monitor;hata ayıkla;sekans;dizi;monitör;" "izle;" #: data/org.freedesktop.Bustle.desktop.in.in:19 msgid "New Window" msgstr "Yeni Pencere" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Pencere genişliği" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Pencere yüksekliği" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Pencerenin ekranı kaplama durumu" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle, D-Bus etkinliğinin sıra diyagramlarını çizer. Sinyal yayımlarını, " "yöntem çağrılarını, karşı dönüşlerini her tekil etkinlik için zaman " "damgalarıyla ve her yöntem çağrısının süresiyle gösterir. Bu istenmedik D-" "Bus trafiğini denetlemenize yardım eder ve D-Bus tabanlı uygulamanızın neden " "istediğiniz gibi işlemediğini vurgular. Ayrıca sinyal frekansları ve " "ortalama yöntem çağrı zamanları gibi istatistikleri sağlar." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "D-Bus etkinliklerinin sıra diyagramlarını keşfet" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Günlüğü özetleyen istatistikleri gör" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Rahatlatıcı gri tonlamalı hoş geldiniz sayfasıyla rahatlayın" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Bustle geliştiricileri" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Kayıt Adresi" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Adreslerle ilgili daha çok bilgi için D-Bus belirtimine göz atın." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "İ_ptal" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "_Kaydet" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Sunucu Adresi" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "İlişkili İleti" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "busctl olarak kopyala" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "İleti" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Tür" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Yol" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Arayüz" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Üye" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Hata Türü" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Ayrıntılar" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Gönderici" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Hedef" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Bileşen" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Gövde Ölçüsü" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Yanıt Süresi" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "İmza" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Bayraklar" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Gövde" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Biçim Belgelendirmesini Aç" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Genişlet" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Süre" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Her yöntem çağrısının toplam ve ortalama süreleri" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Yöntem" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Toplam" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Çağrılar" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Ortalama" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Tüm Etiketleri Seç" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Tüm Etiketlerin Seçimini Kaldır" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Tüm Adları Seç" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Tüm Adların Seçimini Kaldır" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Eylemler" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Frekanslar" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Yöntem çağrılarının ve sinyallerin frekansları" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "İleti Türü" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Sıklık" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Genel" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Kısayolları göster" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Yeni Pencere" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "Pencereyi Kapat" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Çık" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Kayıt" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Oturum Veri Yolunu Kaydet" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Sistem Veri Yolunu Kaydet" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Kayıt Adresi" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Günlük Dosyasını Aç" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Diyagram" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "PCAP Dosyası Olarak Kaydet" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "DOT Grafik Dosyası Olarak Kaydet" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Hizmetleri Süz" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "İstatistikler" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Boyutlar" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Her iletinin en küçük, ortalama ve en büyük boyutu" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "En Küçük" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "En Büyük" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "İstatistikler" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Yeni _Pencere" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Aç" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Günlük Çiftini Aç" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Farklı Kaydet…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT Grafiği" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "İ_statistikler" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Klavye Kısayolları" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "Bustle _Hakkında" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Oturum Veri Yolunu Kaydet" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Sistem Veri Yolunu Kaydet" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Kayıt Adresi…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Günlük Dosyasını Aç" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Ana Menü" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Bustleʼa Hoş Geldiniz" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "D-Bus etkinliğini kaydetmeye başla, şu komutu da çalıştırabilirsiniz" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Kopyala" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Yükleniyor" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "_Durdur" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Hizmetleri Süz" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Ayrıntılar Görünümü" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Lütfen Bekleyin" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "D-Bus trafiğini bekleniyor…" #: src/application.rs:37 msgid "Open a new window" msgstr "Yeni bir pencere aç" #: src/details_view.rs:266 src/window.rs:670 msgid "Copied to clipboard" msgstr "Panoya kopyalandı" #: src/details_view.rs:270 src/details_view.rs:274 msgid "Could not generate busctl command" msgstr "busctl komutu oluşturulamadı" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Yanıt Beklenmiyor" #: src/message.rs:209 msgid "No Auto Start" msgstr "Kendiliğinden Başlatma Yok" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Etkileşimli Kimlik Doğrulamaya İzin Ver" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Erişilebilirlik" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Coğrafi Konumlandırma" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Girdi" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Giriş" #: src/message_tag.rs:133 msgid "Network" msgstr "Ağ" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "İlke Denetimi" #: src/message_tag.rs:135 msgid "Portals" msgstr "Portallar" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Arama Sağlayıcı" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Gizler" #: src/message_tag.rs:138 msgid "Session" msgstr "Oturum" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Kabuk" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" # Dikkat: Uygulama adı! #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Güç" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Bilinmiyor" #: src/message_type.rs:34 msgid "Method Call" msgstr "Yöntem Çağrısı" #: src/message_type.rs:35 msgid "Method Return" msgstr "Yöntem Dönüşü" #: src/message_type.rs:36 msgid "Error" msgstr "Hata" #: src/message_type.rs:37 msgid "Signal" msgstr "Sinyal" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Oturum veri yolu kaydediliyor…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Oturum veri yolu kaydedilemedi" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Sistem veri yolu kaydediliyor…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Sistem veri yolu kaydedilemedi" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "{address} kaydediliyor…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Adres kaydedilemedi" #: src/window.rs:194 msgid "Failed to open file" msgstr "Dosya açılamadı" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "PCAP olarak kaydedilemedi" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Kayıt, PCAP olarak kaydedildi" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "DOT grafiği olarak kaydedilemedi" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Kayıt, DOT grafiği olarak kaydedildi" #: src/window.rs:415 msgid "translator-credits" msgstr "" "Sabri Ünal \n" "Emin Tufan Çetin " #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP Dosyaları" #: src/window.rs:517 msgid "Open Log" msgstr "Günlüğü Aç" #: src/window.rs:542 msgid "Save Log" msgstr "Günlüğü Kaydet" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Günlüğü DOT Grafik Dosyası Olarak Kaydet" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "{n_messages} ileti günlüklendi" #~ msgid "Reset All" #~ msgstr "Tümünü Sıfırla" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/uk.po000066400000000000000000000452151506133026500221270ustar00rootroot00000000000000# Ukrainian translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # # Yuri Chornoivan , 2024, 2025. msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-03 15:55+0000\n" "PO-Revision-Date: 2025-07-03 19:46+0300\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 23.04.3\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "Візуалізація дій D-Bus" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "" "debug;profile;d-bus;dbus;sequence;monitor;діагностика;профіль;ді-бас;" "послідовність;монітор;спостереження;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "Нове вікно" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "Ширина вікна" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "Висота вікна" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "Стан розгорнутого вікна" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle малює діаграми послідовностей дій D-Bus. Програма показує місця " "видання сигналів, виклики методів та відповідні результати з часовими " "позначками для кожної окремої події та тривалостями для кожного виклику " "методу. Це допоможе вам стежити за небажаним обміном даними D-Bus та " "визначати точні причини того, що ваша заснована на D-Bus програма поводить " "себе не так, як ви сподівалися. Також програма надає статистичні дані, " "зокрема частоти сигналів та середні тривалості викликів методів." #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "Вивчіть діаграми послідовностей викликів D-Bus" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "Перегляньте резюме статистичних даних журналу" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "Розслабтеся під час показу цієї заспокійливої сірої сторінки вітання" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Розробники Bustle" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "Записати адресу" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "Щоб дізнатися більше про адреси, ознайомтеся зі специфікацією D-Bus." #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "_Скасувати" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "З_аписати" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "Адреса сервера" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "Пов'язане повідомлення" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "Копіювати як busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "Повідомлення" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "Тип" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "Шлях" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "Інтерфейс" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "Член" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "Тип похибки" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "Подробиці" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "Відправник" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "Призначення" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "Компонент" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "Розмір вмісту" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "Тривалість відповіді" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "Підпис" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "Прапорці" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "Вміст" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "Документація у відкритому форматі" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "Розгорнути" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "Тривалості" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "Загальний і середній час виклику кожного методу" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "Метод" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "Загалом" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "Виклики" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "Середнє" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "Позначити усі мітки" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "Зняти позначення з усіх міток" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "Позначити усі назви" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "Зняти позначення з усіх назв" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "Дії" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "Частоти" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "Частоти викликів методу та сигналів" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "Тип повідомлення" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "Частота" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "Загальне" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Показати клавіатурні скорочення" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "Відкрити нове вікно" #: data/resources/ui/shortcuts.ui:21 #| msgid "New Window" msgctxt "shortcut window" msgid "Close Window" msgstr "Закрити вікно" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "Вийти з програми" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "Запис" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "Записувати шину сеансу" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "Записувати системну шину" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "Записати адресу" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "Відкрити файл журналу" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "Діаграма" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "Зберегти як файл PCAP" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "Зберегти як файл графу DOT" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "Фільтрувати служби" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "Статистика" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "Розміри" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "Найменший, середній і найбільший розмір кожного повідомлення" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "Найменший" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "Найбільший" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "Статистика" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "Створити _вікно" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "_Відкрити" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "Відкрити пару журналів" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "Зберегти як…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "граф DOT" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "С_татистика" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "_Клавіатурні скорочення" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "_Про Bustle" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "Записувати шину сеансу" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "Записувати системну шину" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "Записати адресу…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "Відкрити файл журналу" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "Головне меню" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "Вітаємо у Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "Почати записувати обмін даними D-Bus; ви також можете віддати команду" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "Копіювати" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "Завантаження" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "З_упинити" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "Фільтрувати служби" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "Докладний перегляд" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "Будь ласка, зачекайте" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "Очікуємо на обмін даними D-Bus…" #: src/application.rs:37 msgid "Open a new window" msgstr "Відкрити нове вікно" #: src/details_view.rs:265 src/window.rs:670 msgid "Copied to clipboard" msgstr "Скопійовано до буфера обміну" #: src/details_view.rs:269 src/details_view.rs:273 msgid "Could not generate busctl command" msgstr "Не вдалося створити команду busctl" #: src/message.rs:208 msgid "No Reply Expected" msgstr "Відповіді не очікується" #: src/message.rs:209 msgid "No Auto Start" msgstr "Без автозапуску" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "Дозволити інтерактивне розпізнавання" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "Доступність" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "Bluetooth" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "Геопозиціювання" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "Введення" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "Вхід" #: src/message_tag.rs:133 msgid "Network" msgstr "Мережа" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "Керування правилами" #: src/message_tag.rs:135 msgid "Portals" msgstr "Портали" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "Надавач пошуку" #: src/message_tag.rs:137 msgid "Secrets" msgstr "Ключі" #: src/message_tag.rs:138 msgid "Session" msgstr "Сеанс" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Оболонка" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "Живлення" #: src/message_tag.rs:145 msgid "Unknown" msgstr "Невідомий" #: src/message_type.rs:34 msgid "Method Call" msgstr "Виклик методу" #: src/message_type.rs:35 msgid "Method Return" msgstr "Результат методу" #: src/message_type.rs:36 msgid "Error" msgstr "Помилка" #: src/message_type.rs:37 msgid "Signal" msgstr "Сигнал" #: src/window.rs:135 msgid "Recording session bus…" msgstr "Записуємо канал сеансу…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "Не вдалося записати канал сеансу" #: src/window.rs:147 msgid "Recording system bus…" msgstr "Записуємо канал системи…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "Не вдалося записати канал системи" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "Записуємо {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "Не вдалося записати адресу" #: src/window.rs:194 msgid "Failed to open file" msgstr "Не вдалося відкрити файл" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "Не вдалося зберегти як PCAP" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "Запис збережено як PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "Не вдалося зберегти у форматі графа DOT" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "Запис збережено як граф DOT" #: src/window.rs:415 msgid "translator-credits" msgstr "Юрій Чорноіван , 2024" #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "файли PCAP" #: src/window.rs:517 msgid "Open Log" msgstr "Відкрити журнал" #: src/window.rs:542 msgid "Save Log" msgstr "Зберегти журнал" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "Зберегти журнал як файл графу DOT" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "До журналу записано {n_messages} повідомлення" msgstr[1] "До журналу записано {n_messages} повідомлення" msgstr[2] "До журналу записано {n_messages} повідомлень" msgstr[3] "До журналу записано {n_messages} повідомлення" #~ msgid "Reset All" #~ msgstr "Скинути все" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/po/zh_CN.po000066400000000000000000000364761506133026500225220ustar00rootroot00000000000000# Chinese (China) translation for bustle. # Copyright (C) 2024 bustle's COPYRIGHT HOLDER # This file is distributed under the same license as the bustle package. # lumingzh , 2024-2025. # msgid "" msgstr "" "Project-Id-Version: bustle main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/bustle/issues\n" "POT-Creation-Date: 2025-07-03 15:55+0000\n" "PO-Revision-Date: 2025-07-04 18:50+0800\n" "Last-Translator: lumingzh \n" "Language-Team: Chinese (China) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 48.0\n" #: data/org.freedesktop.Bustle.desktop.in.in:3 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:9 msgid "Bustle" msgstr "Bustle" #: data/org.freedesktop.Bustle.desktop.in.in:4 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:10 msgid "Visualize D-Bus activity" msgstr "可视化 D-Bus 活动" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Bustle.desktop.in.in:10 msgid "debug;profile;d-bus;dbus;sequence;monitor;" msgstr "debug;profile;d-bus;dbus;sequence;monitor;调试;配置文件;序列;监视器;" #: data/org.freedesktop.Bustle.desktop.in.in:20 msgid "New Window" msgstr "新建窗口" #: data/org.freedesktop.Bustle.gschema.xml.in:6 msgid "Window width" msgstr "窗口宽度" #: data/org.freedesktop.Bustle.gschema.xml.in:10 msgid "Window height" msgstr "窗口高度" #: data/org.freedesktop.Bustle.gschema.xml.in:14 msgid "Window maximized state" msgstr "窗口最大化状态" #. Translators: These are the application description paragraphs in the AppData file. #: data/org.freedesktop.Bustle.metainfo.xml.in.in:13 msgid "" "Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, " "method calls and their corresponding returns, with time stamps for each " "individual event and the duration of each method call. This can help you " "check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based " "application is not performing as well as you like. It also provides " "statistics like signal frequencies and average method call times." msgstr "" "Bustle 绘制 D-Bus 活动的序列图表。它显示了信号发射、方法调用和它们相应的返" "回,带有时间戳的每个独立事件和每个方法调用的时长。这可以帮助您检查不需要的 D-" "Bus 交流,以及您基于 D-Bus 的应用程序为什么未按照预期良好运行的精确位置。它也" "提供了诸如信号频率和平均方法调用时间等的统计信息。" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:26 msgid "Explore sequence diagrams of D-Bus activity" msgstr "探索 D-Bus 活动的序列图表" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:30 msgid "See statistics summarizing the log" msgstr "查看统计摘要日志" #: data/org.freedesktop.Bustle.metainfo.xml.in.in:34 msgid "Relax with this soothing greyscale welcome page" msgstr "通过这个镇静的灰度欢迎页面放松" #. developer_name tag deprecated with Appstream 1.0 #: data/org.freedesktop.Bustle.metainfo.xml.in.in:159 src/window.rs:416 msgid "The Bustle developers" msgstr "Bustle 开发者" #: data/resources/ui/address_dialog.ui:4 msgid "Record Address" msgstr "记录地址" #: data/resources/ui/address_dialog.ui:5 msgid "" "For more information about addresses, see the D-Bus specification." msgstr "" "获取关于地址的更多信息,请参阅 D-Bus 规范。" #: data/resources/ui/address_dialog.ui:10 msgid "_Cancel" msgstr "取消(_C)" #. This is a verb #: data/resources/ui/address_dialog.ui:11 data/resources/ui/window.ui:90 #: data/resources/ui/window.ui:198 msgid "_Record" msgstr "记录(_R)" #: data/resources/ui/address_dialog.ui:20 msgid "Server Address" msgstr "服务器地址" #: data/resources/ui/details_view.ui:19 msgid "Associated Message" msgstr "相关的消息" #: data/resources/ui/details_view.ui:30 msgid "Copy as busctl" msgstr "复制为 busctl" #: data/resources/ui/details_view.ui:39 msgid "Message" msgstr "消息" #: data/resources/ui/details_view.ui:42 msgid "Type" msgstr "类型" #: data/resources/ui/details_view.ui:51 msgid "Path" msgstr "路径" #: data/resources/ui/details_view.ui:60 msgid "Interface" msgstr "接口" #: data/resources/ui/details_view.ui:69 #: data/resources/ui/frequencies_page.ui:44 data/resources/ui/sizes_page.ui:43 msgid "Member" msgstr "成员" #: data/resources/ui/details_view.ui:78 msgid "Error Type" msgstr "错误类型" #: data/resources/ui/details_view.ui:90 msgid "Details" msgstr "详情" #: data/resources/ui/details_view.ui:93 msgid "Sender" msgstr "发送者" #: data/resources/ui/details_view.ui:102 msgid "Destination" msgstr "目标位置" #: data/resources/ui/details_view.ui:111 msgid "Component" msgstr "组件" #: data/resources/ui/details_view.ui:126 msgid "Body Size" msgstr "消息体大小" #: data/resources/ui/details_view.ui:136 msgid "Response Time" msgstr "响应时间" #: data/resources/ui/details_view.ui:146 msgid "Signature" msgstr "特征" #: data/resources/ui/details_view.ui:155 msgid "Flags" msgstr "标志" #: data/resources/ui/details_view.ui:166 msgid "Body" msgstr "消息体" #: data/resources/ui/details_view.ui:169 msgid "Open Format Documentation" msgstr "打开格式化文档" #: data/resources/ui/diagram_header.ui:20 msgid "Expand" msgstr "展开" #: data/resources/ui/durations_page.ui:11 data/resources/ui/statistics.ui:43 msgid "Durations" msgstr "时长" #: data/resources/ui/durations_page.ui:12 msgid "The total and mean time every method call took" msgstr "每个方法调用花费的总计和平均时间" #: data/resources/ui/durations_page.ui:16 msgid "Method" msgstr "方法" #: data/resources/ui/durations_page.ui:45 msgid "Total" msgstr "总计" #: data/resources/ui/durations_page.ui:76 msgid "Calls" msgstr "调用" #: data/resources/ui/durations_page.ui:110 data/resources/ui/sizes_page.ui:102 msgid "Mean" msgstr "平均" #: data/resources/ui/filter_pane.ui:6 msgid "Select All Tags" msgstr "选择所有标签" #: data/resources/ui/filter_pane.ui:10 msgid "Unselect All Tags" msgstr "取消全选标签" #: data/resources/ui/filter_pane.ui:16 msgid "Select All Names" msgstr "选择所有名称" #: data/resources/ui/filter_pane.ui:20 msgid "Unselect All Names" msgstr "取消全选名称" #: data/resources/ui/filter_pane.ui:73 msgid "Actions" msgstr "动作" #: data/resources/ui/frequencies_page.ui:11 data/resources/ui/statistics.ui:33 msgid "Frequencies" msgstr "频率" #: data/resources/ui/frequencies_page.ui:12 msgid "The frequencies of method calls and signals" msgstr "方法调用和信号的频率" #: data/resources/ui/frequencies_page.ui:16 data/resources/ui/sizes_page.ui:16 msgid "Message Type" msgstr "消息类型" #: data/resources/ui/frequencies_page.ui:72 msgid "Frequency" msgstr "频率" #: data/resources/ui/shortcuts.ui:6 msgctxt "shortcut window" msgid "General" msgstr "常规" #: data/resources/ui/shortcuts.ui:9 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "显示快捷键" #: data/resources/ui/shortcuts.ui:15 msgctxt "shortcut window" msgid "New Window" msgstr "新建窗口" #: data/resources/ui/shortcuts.ui:21 msgctxt "shortcut window" msgid "Close Window" msgstr "关闭窗口" #: data/resources/ui/shortcuts.ui:27 msgctxt "shortcut window" msgid "Quit" msgstr "退出" #: data/resources/ui/shortcuts.ui:35 msgctxt "shortcut window" msgid "Recording" msgstr "记录" #: data/resources/ui/shortcuts.ui:38 msgctxt "shortcut window" msgid "Record Session Bus" msgstr "记录会话总线" #: data/resources/ui/shortcuts.ui:44 msgctxt "shortcut window" msgid "Record System Bus" msgstr "记录系统总线" #: data/resources/ui/shortcuts.ui:50 msgctxt "shortcut window" msgid "Record Address" msgstr "记录地址" #: data/resources/ui/shortcuts.ui:56 msgctxt "shortcut window" msgid "Open Log File" msgstr "打开日志文件" #: data/resources/ui/shortcuts.ui:64 msgctxt "shortcut window" msgid "Diagram" msgstr "图表" #: data/resources/ui/shortcuts.ui:67 msgctxt "shortcut window" msgid "Save as PCAP File" msgstr "保存为 PCAP 文件" #: data/resources/ui/shortcuts.ui:73 msgctxt "shortcut window" msgid "Save as DOT Graph File" msgstr "保存为 DOT 图表文件" #: data/resources/ui/shortcuts.ui:79 msgctxt "shortcut window" msgid "Filter Services" msgstr "筛选器服务" #: data/resources/ui/shortcuts.ui:85 msgctxt "shortcut window" msgid "Statistics" msgstr "统计" #: data/resources/ui/sizes_page.ui:11 data/resources/ui/statistics.ui:53 msgid "Sizes" msgstr "大小" #: data/resources/ui/sizes_page.ui:12 msgid "The smallest, mean and largest size of every message" msgstr "每条消息的最小、平均和最大大小" #: data/resources/ui/sizes_page.ui:72 msgid "Smallest" msgstr "最小" #: data/resources/ui/sizes_page.ui:132 msgid "Largest" msgstr "最大" #: data/resources/ui/statistics.ui:4 msgid "Statistics" msgstr "统计" #: data/resources/ui/window.ui:6 msgid "New _Window" msgstr "新建窗口(_W)" #: data/resources/ui/window.ui:10 msgid "_Open" msgstr "打开(_O)" #: data/resources/ui/window.ui:15 msgid "Open Pair of Logs" msgstr "打开相配的日志" #: data/resources/ui/window.ui:22 msgid "Save As…" msgstr "另存为…" #: data/resources/ui/window.ui:25 msgid "PCAP" msgstr "PCAP" #. Translators: Dot is a type of file, do not translate. #: data/resources/ui/window.ui:29 src/window.rs:569 msgid "DOT Graph" msgstr "DOT 图表" #: data/resources/ui/window.ui:35 msgid "_Statistics" msgstr "统计(_S)" #: data/resources/ui/window.ui:42 msgid "_Keyboard Shortcuts" msgstr "键盘快捷键(_K)" #: data/resources/ui/window.ui:46 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "_About Bustle" msgstr "关于 Bustle(_A)" #. Bus as in D-Bus #: data/resources/ui/window.ui:54 msgid "Record Session Bus" msgstr "记录会话总线" #. Bus as in D-Bus #: data/resources/ui/window.ui:58 msgid "Record System Bus" msgstr "记录系统总线" #. Bus as in D-Bus #: data/resources/ui/window.ui:62 msgid "Record Address…" msgstr "记录地址…" #: data/resources/ui/window.ui:103 data/resources/ui/window.ui:231 msgid "Open Log File" msgstr "打开日志文件" #: data/resources/ui/window.ui:111 data/resources/ui/window.ui:166 #: data/resources/ui/window.ui:247 msgid "Main Menu" msgstr "主菜单" #: data/resources/ui/window.ui:118 msgctxt "Translators: Do not translate Bustle, its the app's name" msgid "Welcome to Bustle" msgstr "欢迎使用 Bustle" #. Translators: Do not localize D-Bus. It is a proper name #: data/resources/ui/window.ui:119 msgid "Start recording D-Bus activity, you can also run the command" msgstr "开始记录 D-Bus 活动,您也可以运行命令" #: data/resources/ui/window.ui:139 msgid "Copy" msgstr "复制" #: data/resources/ui/window.ui:173 msgid "Loading" msgstr "正在加载" #. This is a verb #: data/resources/ui/window.ui:214 msgid "_Stop" msgstr "停止(_S)" #: data/resources/ui/window.ui:237 msgid "Filter Services" msgstr "筛选器服务" #: data/resources/ui/window.ui:253 msgid "Details View" msgstr "详情视图" #: data/resources/ui/window.ui:264 msgid "Please Hold" msgstr "请稍候" #: data/resources/ui/window.ui:265 msgid "Waiting for D-Bus Traffic…" msgstr "正在等待 D-Bus 交流…" #: src/application.rs:37 msgid "Open a new window" msgstr "打开新窗口" #: src/details_view.rs:265 src/window.rs:670 msgid "Copied to clipboard" msgstr "已复制到剪贴板" #: src/details_view.rs:269 src/details_view.rs:273 msgid "Could not generate busctl command" msgstr "无法生成 busctl 命令" #: src/message.rs:208 msgid "No Reply Expected" msgstr "无预期回复" #: src/message.rs:209 msgid "No Auto Start" msgstr "无自动启动" #: src/message.rs:211 msgid "Allow Interactive Authentication" msgstr "允许交互式认证" #: src/message_tag.rs:124 msgid "Accessibility" msgstr "无障碍" #: src/message_tag.rs:125 msgid "Bluetooth" msgstr "蓝牙" #: src/message_tag.rs:126 msgid "Flatpak" msgstr "Flatpak" #: src/message_tag.rs:127 msgid "Geolocalization" msgstr "地理定位" #: src/message_tag.rs:128 msgid "GTK" msgstr "GTK" #: src/message_tag.rs:129 msgid "GVFS" msgstr "GVFS" #: src/message_tag.rs:130 msgid "Input" msgstr "输入" #. TRANSLATORS Noun, as in user login #: src/message_tag.rs:132 msgid "Login" msgstr "登录" #: src/message_tag.rs:133 msgid "Network" msgstr "网络" #: src/message_tag.rs:134 msgid "Policy Control" msgstr "策略控制" #: src/message_tag.rs:135 msgid "Portals" msgstr "门户" #: src/message_tag.rs:136 msgid "Search Provider" msgstr "搜索提供者" #: src/message_tag.rs:137 msgid "Secrets" msgstr "保密" #: src/message_tag.rs:138 msgid "Session" msgstr "会话" #. TRANSLATORS As in the GNOME Shell #: src/message_tag.rs:140 msgid "Shell" msgstr "Shell" #: src/message_tag.rs:141 msgid "systemd" msgstr "systemd" #. TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ #: src/message_tag.rs:143 msgid "Tracker" msgstr "Tracker" #: src/message_tag.rs:144 msgid "Power" msgstr "电源" #: src/message_tag.rs:145 msgid "Unknown" msgstr "未知" #: src/message_type.rs:34 msgid "Method Call" msgstr "方法调用" #: src/message_type.rs:35 msgid "Method Return" msgstr "方法返回" #: src/message_type.rs:36 msgid "Error" msgstr "错误" #: src/message_type.rs:37 msgid "Signal" msgstr "信号" #: src/window.rs:135 msgid "Recording session bus…" msgstr "正在记录会话总线…" #: src/window.rs:140 msgid "Failed to record session bus" msgstr "记录会话总线失败" #: src/window.rs:147 msgid "Recording system bus…" msgstr "正在记录系统总线…" #: src/window.rs:152 msgid "Failed to record system bus" msgstr "记录系统总线失败" #. Translators: Do NOT translate the contents between '{' and '}', this #. is a variable name. #: src/window.rs:169 msgid "Recording {address}…" msgstr "正在记录 {address}…" #: src/window.rs:178 msgid "Failed to record address" msgstr "记录地址失败" #: src/window.rs:194 msgid "Failed to open file" msgstr "打开文件失败" #: src/window.rs:206 msgid "Failed to save as PCAP" msgstr "保存为 PCAP 失败" #: src/window.rs:209 msgid "Recording saved as PCAP" msgstr "记录已保存为 PCAP" #: src/window.rs:220 msgid "Failed to save as DOT graph" msgstr "保存为 DOT 图表失败" #: src/window.rs:223 msgid "Recording saved as DOT graph" msgstr "记录已保存为 DOT 图表" #: src/window.rs:415 msgid "translator-credits" msgstr "lumingzh , 2024-2025." #. Translators: PCAP is a type of file, do not translate. #: src/window.rs:509 src/window.rs:534 msgid "PCAP Files" msgstr "PCAP 文件" #: src/window.rs:517 msgid "Open Log" msgstr "打开日志" #: src/window.rs:542 msgid "Save Log" msgstr "保存日志" #: src/window.rs:576 msgid "Save Log as DOT Graph File" msgstr "将日志保存为 DOT 图表文件" #. Translators: Do NOT translate the contents between '{' and '}', this is a #. variable name. #: src/window.rs:644 msgid "Logged {n_messages} message" msgid_plural "Logged {n_messages} messages" msgstr[0] "已记录 {n_messages} 条消息" #~ msgid "Reset All" #~ msgstr "全部重置" bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/rustfmt.toml000066400000000000000000000003121506133026500231200ustar00rootroot00000000000000imports_granularity = "Crate" format_code_in_doc_comments = true group_imports = "StdExternalCrate" newline_style = "Unix" normalize_comments = true normalize_doc_attributes = true wrap_comments = true bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/000077500000000000000000000000001506133026500213125ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/address_dialog.rs000066400000000000000000000043121506133026500246240ustar00rootroot00000000000000use adw::{prelude::*, subclass::prelude::*}; use gtk::glib; pub struct Cancelled; mod imp { use super::*; #[derive(Default, gtk::CompositeTemplate)] #[template(resource = "/org/freedesktop/Bustle/ui/address_dialog.ui")] pub struct AddressDialog { #[template_child] pub(super) entry_row: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for AddressDialog { const NAME: &'static str = "BustleAddressDialog"; type Type = super::AddressDialog; type ParentType = adw::AlertDialog; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } impl ObjectImpl for AddressDialog { fn constructed(&self) { self.parent_constructed(); self.obj().update_record_response_enabled(); self.entry_row.grab_focus(); } } impl WidgetImpl for AddressDialog {} impl AdwDialogImpl for AddressDialog {} impl AdwAlertDialogImpl for AddressDialog {} } glib::wrapper! { pub struct AddressDialog(ObjectSubclass) @extends gtk::Widget, adw::Dialog, adw::AlertDialog, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } #[gtk::template_callbacks] impl AddressDialog { pub async fn choose(parent: &impl IsA) -> Result { let this = glib::Object::new::(); let entry_row = this.imp().entry_row.get(); match this.choose_future(parent).await.as_str() { "cancel" => Err(Cancelled), "record" => Ok(entry_row .text() .parse() .expect("address must have been validated")), response_id => unreachable!("unexpected response id `{}`", response_id), } } #[template_callback] fn update_record_response_enabled(&self) { self.set_response_enabled( "record", self.imp().entry_row.text().parse::().is_ok(), ); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/application.rs000066400000000000000000000130251506133026500241640ustar00rootroot00000000000000use adw::subclass::prelude::*; use gettextrs::gettext; use gtk::{ gio, glib::{self, clone}, prelude::*, }; use crate::{ config::{APP_ID, PKGDATADIR, PROFILE, VERSION}, window::Window, }; mod imp { use super::*; #[derive(Debug, Default)] pub struct Application; #[glib::object_subclass] impl ObjectSubclass for Application { const NAME: &'static str = "BustleApplication"; type Type = super::Application; type ParentType = adw::Application; } impl ObjectImpl for Application { fn constructed(&self) { tracing::debug!("GtkApplication::constructed"); self.parent_constructed(); self.obj().add_main_option( "new-window", glib::Char::from(b'w'), glib::OptionFlags::NONE, glib::OptionArg::None, &gettext("Open a new window"), None, ); } } impl ApplicationImpl for Application { fn handle_local_options( &self, options: &glib::VariantDict, ) -> std::ops::ControlFlow { let app = self.obj(); if options.contains("new-window") { if let Err(err) = app.register(None::<&gio::Cancellable>) { tracing::error!("Failed to register the application: {err}"); } if app.is_remote() { app.activate_action("new-window", None); return std::ops::ControlFlow::Break(glib::ExitCode::SUCCESS); } } tracing::debug!("GtkApplication::handle_local_options"); self.parent_handle_local_options(options) } fn activate(&self) { tracing::debug!("GtkApplication::activate"); self.parent_activate(); let app = self.obj(); if let Some(window) = app.active_window() { window.present(); return; } let window = Window::with_group(&app); window.present(); } fn startup(&self) { tracing::debug!("GtkApplication::startup"); self.parent_startup(); let app = self.obj(); // Set icons for shell gtk::Window::set_default_icon_name(APP_ID); app.setup_gactions(); app.setup_accels(); } fn open(&self, files: &[gio::File], _hint: &str) { let app = self.obj(); for file in files { let window = Window::with_group(&app); glib::spawn_future_local(clone!( #[strong] window, #[strong] file, async move { if let Err(err) = window.load_log(&file).await { tracing::error!("Failed to load log {err:?}") } } )); window.present(); } } } impl GtkApplicationImpl for Application {} impl AdwApplicationImpl for Application {} } glib::wrapper! { pub struct Application(ObjectSubclass) @extends gio::Application, gtk::Application, adw::Application, @implements gio::ActionMap, gio::ActionGroup; } impl Application { fn setup_gactions(&self) { // Quit let action_quit = gio::ActionEntry::builder("quit") .activate(move |app: &Self, _, _| { // This is needed to trigger the delete event and saving the window state if let Some(window) = app.active_window() { window.close(); } app.quit(); }) .build(); let new_window_action = gio::ActionEntryBuilder::new("new-window") .activate(|app, _, _| { let window = Window::with_group(app); window.present(); }) .build(); self.add_action_entries([action_quit, new_window_action]); } // Sets up keyboard shortcuts fn setup_accels(&self) { self.set_accels_for_action("app.quit", &["q"]); self.set_accels_for_action("app.new-window", &["n"]); self.set_accels_for_action("window.close", &["w"]); self.set_accels_for_action("win.statistics", &["F9"]); self.set_accels_for_action("win.filter-services", &["f"]); self.set_accels_for_action("win.record-session-bus", &["e"]); self.set_accels_for_action("win.record-system-bus", &["y"]); self.set_accels_for_action("win.record-address", &["a"]); self.set_accels_for_action("win.open-log", &["o"]); self.set_accels_for_action("win.save", &["s"]); self.set_accels_for_action("win.save-dot", &["s"]); } pub fn run(&self) -> glib::ExitCode { tracing::info!("Bustle ({})", APP_ID); tracing::info!("Version: {} ({})", VERSION, PROFILE); tracing::info!("Datadir: {}", PKGDATADIR); ApplicationExtManual::run(self) } } impl Default for Application { fn default() -> Self { glib::Object::builder() .property("application-id", APP_ID) .property("resource-base-path", "/org/freedesktop/Bustle/") .property("flags", gio::ApplicationFlags::HANDLES_OPEN) .build() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/bus_name_item.rs000066400000000000000000000053701506133026500244740ustar00rootroot00000000000000use gtk::{glib, subclass::prelude::*}; use indexmap::IndexSet; use zbus::names::{BusName, WellKnownName}; use crate::message::ReceiveIndex; /// The point at which to look up for well-known names. #[derive(Clone, Copy)] pub enum LookupPoint { /// Union of all well-known names at all indices. All, /// Well-known names at the given index. Index(ReceiveIndex), /// Well-known names at the last index. Last, } impl From for LookupPoint { fn from(receive_index: ReceiveIndex) -> Self { Self::Index(receive_index) } } mod imp { use std::{ cell::{OnceCell, RefCell}, collections::BTreeMap, }; use super::*; #[derive(Default)] pub struct BusNameItem { pub(super) name: OnceCell>, pub(super) wk_name_log: RefCell>>>, } #[glib::object_subclass] impl ObjectSubclass for BusNameItem { const NAME: &'static str = "BustleBusNameItem"; type Type = super::BusNameItem; } impl ObjectImpl for BusNameItem {} } glib::wrapper! { pub struct BusNameItem(ObjectSubclass); } impl BusNameItem { pub fn name(&self) -> &BusName<'static> { self.imp().name.get().unwrap() } /// Returns a copy of the well-known names that were known at the given /// lookup point. pub fn wk_names(&self, lookup_point: LookupPoint) -> IndexSet> { let wk_name_log = self.imp().wk_name_log.borrow(); match lookup_point { LookupPoint::All => wk_name_log.values().flatten().cloned().collect(), LookupPoint::Index(receive_index) => wk_name_log .range(..=receive_index) .next_back() .map(|(_, wk_names)| wk_names.clone()) .unwrap_or_default(), LookupPoint::Last => wk_name_log .last_key_value() .map(|(_, wk_names)| wk_names.clone()) .unwrap_or_default(), } } /// This must only be called on `BusNameList` pub fn new(name: BusName<'static>) -> Self { let this = glib::Object::new::(); this.imp().name.set(name).unwrap(); this } /// This must only be called on `BusNameList` pub fn insert_wk_name_log( &self, receive_index: ReceiveIndex, wk_names: IndexSet>, ) { let prev_entry = self .imp() .wk_name_log .borrow_mut() .insert(receive_index, wk_names.clone()); debug_assert_eq!( prev_entry, None, "duplicate entry `{wk_names:?}` for the same receive index `{receive_index:?}`" ); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/bus_name_list.rs000066400000000000000000000405161506133026500245120ustar00rootroot00000000000000use std::collections::HashSet; use anyhow::{Context, Result, bail}; use gtk::{gio, glib, prelude::*, subclass::prelude::*}; use indexmap::{IndexSet, map::Entry}; use zbus::{ fdo::DBusProxy, names::{BusName, UniqueName, WellKnownName}, proxy::Defaults, zvariant::Optional, }; use crate::{ bus_name_item::{BusNameItem, LookupPoint}, message::{Message, ReceiveIndex}, message_type::MessageType, }; mod imp { use std::cell::RefCell; use indexmap::IndexMap; use super::*; #[derive(Default)] pub struct BusNameList { pub(super) inner: RefCell, BusNameItem>>, } #[glib::object_subclass] impl ObjectSubclass for BusNameList { const NAME: &'static str = "BustleBusNameList"; type Type = super::BusNameList; type Interfaces = (gio::ListModel,); } impl ObjectImpl for BusNameList {} impl ListModelImpl for BusNameList { fn item_type(&self) -> glib::Type { BusNameItem::static_type() } fn n_items(&self) -> u32 { self.inner.borrow().len() as u32 } fn item(&self, position: u32) -> Option { self.inner .borrow() .get_index(position as usize) .map(|(_, v)| v.upcast_ref::()) .cloned() } } } glib::wrapper! { pub struct BusNameList(ObjectSubclass) @implements gio::ListModel; } impl BusNameList { pub fn handle_message(&self, message: &Message) -> Result<()> { match message.message_type() { MessageType::MethodCall => self.handle_method_call_message(message), MessageType::MethodReturn | MessageType::Error => { self.handle_method_return_message(message) } MessageType::Signal => self.handle_signal_message(message), } } /// Returns the `BusNameItem` with the given `bus_name` as item's `bus_name` pub fn get(&self, bus_name: &BusName<'_>) -> Option { self.imp().inner.borrow().get(bus_name).cloned() } fn handle_method_call_message(&self, message: &Message) -> Result<()> { debug_assert!(message.message_type().is_method_call()); let sender = message.sender().context("Call message has no sender")?; self.insert_bus_name(sender.to_owned().into()); let destination = message .destination() .context("Call message has no destination")?; self.insert_bus_name(destination.to_owned()); Ok(()) } fn handle_method_return_message(&self, message: &Message) -> Result<()> { debug_assert!(message.message_type().is_method_return()); let call_message = message .associated_message() .context("Return message has no associated call message")?; let call_header = call_message.header(); if call_header.interface() == DBusProxy::INTERFACE.as_ref() && call_header.member().is_some_and(|m| m == "GetNameOwner") { self.handle_get_name_owner_message(&call_message, message) .context("Failed to handle get name owner message")?; return Ok(()); } let imp = self.imp(); match call_message .destination() .context("Call message has no destination")? { BusName::Unique(unique_name) => { // Already inserted on MethodCall handling debug_assert!( imp.inner .borrow() .contains_key(&BusName::from(unique_name.as_ref())), "{unique_name:?} was not found" ); } BusName::WellKnown(dest_wk_name) => { if !Message::is_fallback_destination(&BusName::from(dest_wk_name.as_ref())) { // TODO All of these could be optimized by having a specialized `insert_wk_name` // function, so instead of emitting items-changed thrice, we only emit it once. // FIXME This disrupts ordering of names, see `IndexMap.move_index` let removed = imp .inner .borrow_mut() .shift_remove_full(&BusName::from(dest_wk_name.to_owned())); if let Some((old_index, old_name, old_bus_name_item)) = removed { debug_assert!(matches!(old_name, BusName::WellKnown(_))); debug_assert_eq!( old_bus_name_item.wk_names(LookupPoint::All), IndexSet::new() ); self.items_changed(old_index as u32, 1, 0); // There was a well-known name in bus names map (which we removed), and now // that we already know its unique name through this return message, we can // now add that unique name to the map as a key, and move the well-known // name to the items's well-known names. let sender = message.sender().context("Return message has no sender")?; self.insert_wk_name( sender.to_owned().into(), dest_wk_name.to_owned(), message.receive_index(), ); // After that, we need to go back in time when the well-known name was first // discovered, and add the well-known name to the entry at that index. // First, get the index and the bus name item we just inserted. let inner = imp.inner.borrow(); let (new_index, _, new_bus_name_item) = inner.get_full(&BusName::from(sender)).unwrap(); // Then add to the log entry the well-known name when it was first // discovered, which is the call message's receive // index let mut wk_names = new_bus_name_item.wk_names(call_message.receive_index().into()); wk_names.insert(dest_wk_name.to_owned()); new_bus_name_item .insert_wk_name_log(call_message.receive_index(), wk_names); // Finally, notify that we have modified an item's well-known names drop(inner); self.items_changed(new_index as u32, 1, 1); } else { // It was already handled by other return messages. debug_assert!(imp.inner.borrow().values().any(|bus_name_item| { bus_name_item .wk_names(message.receive_index().into()) .contains(&dest_wk_name) })); } } } } match message .destination() .context("Return message has no destination")? { BusName::Unique(unique_name) => { // Already inserted on MethodCall handling debug_assert!( imp.inner .borrow() .contains_key(&BusName::from(unique_name.as_ref())), "{unique_name:?} was not found" ); } BusName::WellKnown(dest_wk_name) => { let sender = call_message .sender() .context("Call message has no sender")?; self.insert_wk_name( sender.to_owned().into(), dest_wk_name.to_owned(), message.receive_index(), ); } } Ok(()) } fn handle_get_name_owner_message( &self, call_message: &Message, return_message: &Message, ) -> Result<()> { debug_assert!(call_message.message_type().is_method_call()); debug_assert!(return_message.message_type().is_method_return()); if return_message.message_type().is_error() { return Ok(()); } debug_assert_eq!( call_message.header().member().map(|m| m.as_str()), Some("GetNameOwner") ); let call_message_body = call_message.body(); let return_message_body = return_message.body(); let (name,) = call_message_body .deserialize::<(BusName<'_>,)>() .context("Failed to get call message body")?; let (owner,) = return_message_body .deserialize::<(UniqueName<'_>,)>() .context("Failed to get return message body")?; match name { BusName::Unique(unique_name) => { debug_assert_eq!(owner, unique_name); } BusName::WellKnown(wk_name) => { self.insert_wk_name( owner.to_owned().into(), wk_name.to_owned(), return_message.receive_index(), ); } } Ok(()) } fn handle_signal_message(&self, message: &Message) -> Result<()> { debug_assert!(message.message_type().is_signal()); let header = message.header(); if header.interface() == DBusProxy::INTERFACE.as_ref() && header.member().is_some_and(|m| m == "NameOwnerChanged") { self.handle_name_owner_changed_message(message) .context("Failed to handle NOC message")?; return Ok(()); } if let Some(sender) = message.sender() { self.insert_bus_name(sender.to_owned().into()); } if let Some(destination) = message.destination() { self.insert_bus_name(destination.to_owned()); } Ok(()) } fn handle_name_owner_changed_message(&self, message: &Message) -> Result<()> { debug_assert_eq!(message.message_type(), MessageType::Signal); debug_assert_eq!( message.header().member().cloned().as_deref(), Some("NameOwnerChanged") ); let body = message.body(); let (name, old_owner, new_owner) = body .deserialize::<( BusName<'_>, Optional>, Optional>, )>() .context("Failed to get NOC message body")?; if old_owner.is_none() && new_owner.is_none() { bail!("Invalid NOC message; both old and new owner are none"); } let wk_name = match name { BusName::Unique(ref unique_name) => { if let Some(old_owner) = old_owner.as_ref() { debug_assert_eq!(unique_name, old_owner); } if let Some(new_owner) = new_owner.as_ref() { debug_assert_eq!(unique_name, new_owner); } return Ok(()); } BusName::WellKnown(wk_name) => wk_name, }; // FIXME This should be specialized because, while it is harmless as // we remove first, there is a warning since we are trying to add a // log entry to the same receive index. Also, we emit items-changed twice if let Some(old_owner) = old_owner.as_ref() { self.remove_wk_name( old_owner.to_owned().into(), wk_name.to_owned(), message.receive_index(), ); } if let Some(new_owner) = new_owner.as_ref() { self.insert_wk_name( new_owner.to_owned().into(), wk_name.to_owned(), message.receive_index(), ); } Ok(()) } /// Insert `bus_name` with empty `wk_names` fn insert_bus_name(&self, bus_name: BusName<'static>) { let index = match self.imp().inner.borrow_mut().entry(bus_name) { Entry::Occupied(_) => None, Entry::Vacant(entry) => { let index = entry.index(); let bus_name_item = BusNameItem::new(entry.key().to_owned()); entry.insert(bus_name_item); Some(index) } }; if let Some(index) = index { self.items_changed(index as u32, 0, 1); } } /// Insert `wk_name` to `bus_name`'s `wk_names` or create `bus_name` entry /// first fn insert_wk_name( &self, bus_name: BusName<'static>, wk_name: WellKnownName<'static>, receive_index: ReceiveIndex, ) { let imp = self.imp(); let (index, removed, added) = match imp.inner.borrow_mut().entry(bus_name) { Entry::Occupied(entry) => { let bus_name_item = entry.get(); // Get last snapshot and create a new snapshot with the new well-known name let mut wk_names = bus_name_item.wk_names(receive_index.into()); // TODO We can return here early and dont emit items-changed if the given // well-known name exist already in the current snapshot, but we // want to ensure first that the messages we handle come in a // chronological order. wk_names.insert(wk_name); bus_name_item.insert_wk_name_log(receive_index, wk_names); (entry.index(), 1, 1) } Entry::Vacant(entry) => { let bus_name_item = BusNameItem::new(entry.key().to_owned()); let wk_names = IndexSet::from([wk_name]); bus_name_item.insert_wk_name_log(receive_index, wk_names); let index = entry.index(); entry.insert(bus_name_item); (index, 0, 1) } }; self.items_changed(index as u32, removed, added); if cfg!(debug_assertions) { // Ensure that all well-known names has a unique owner let mut unique = HashSet::new(); for (bus_name, bus_name_item) in imp.inner.borrow().iter() { assert_eq!(bus_name, bus_name_item.name()); for wk_name in bus_name_item.wk_names(receive_index.into()) { let was_inserted = unique.insert(wk_name.clone()); assert!(was_inserted, "duplicate well-known name: {wk_name}"); } } } } /// Remove `wk_name` from `bus_name`'s `wk_names` only if `bus_name` exists fn remove_wk_name( &self, bus_name: BusName<'static>, wk_name: WellKnownName<'static>, receive_index: ReceiveIndex, ) { let imp = self.imp(); let index = match imp.inner.borrow_mut().entry(bus_name) { Entry::Occupied(entry) => { let bus_name_item = entry.get(); // Get last snapshot and create a new snapshot without the well-known name let mut wk_names = bus_name_item.wk_names(receive_index.into()); // TODO We can return here early and dont emit items-changed if the given // well-known name does not exist anyway in the current // snapshot, but we want to ensure first that the messages we // handle come in a chronological order. wk_names.shift_remove(&wk_name); bus_name_item.insert_wk_name_log(receive_index, wk_names); Some(entry.index()) } Entry::Vacant(_) => None, }; if let Some(index) = index { self.items_changed(index as u32, 1, 1); } if cfg!(debug_assertions) { // Ensure that all well-known names has a unique owner let mut unique = HashSet::new(); for (bus_name, bus_name_item) in imp.inner.borrow().iter() { assert_eq!(bus_name, bus_name_item.name()); for wk_name in bus_name_item.wk_names(receive_index.into()) { let was_inserted = unique.insert(wk_name.clone()); assert!(was_inserted, "duplicate well-known name: {wk_name}"); } } } } } impl Default for BusNameList { fn default() -> Self { glib::Object::new() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/busctl_cmd.rs000066400000000000000000000101421506133026500237750ustar00rootroot00000000000000use anyhow::Context; use zbus::{ names::{BusName, InterfaceName, MemberName, UniqueName}, zvariant::{self, ObjectPath, Signature}, }; use crate::ConnectionType; pub enum BusctlSubCommand { Call, Wait, } impl BusctlSubCommand { fn as_str(&self) -> &str { match self { Self::Call => "call", Self::Wait => "wait", } } } pub struct BusctlCommand<'a> { pub sub_command: BusctlSubCommand, pub connection_type: ConnectionType, pub sender: Option>, pub destination: Option>, pub path: Option<&'a ObjectPath<'a>>, pub interface: Option<&'a InterfaceName<'a>>, pub body_structure: Option>, pub member: Option<&'a MemberName<'a>>, pub signature: &'a Signature, } impl<'a> BusctlCommand<'a> { pub fn into_command_string(self) -> anyhow::Result { let mut cmd = vec!["busctl"]; let session_flag = match &self.connection_type { ConnectionType::Session => "--user", ConnectionType::System => "", ConnectionType::Address(addr) => &format!("--address={addr}"), }; if !session_flag.is_empty() { cmd.push(session_flag); } cmd.push(self.sub_command.as_str()); match self.sub_command { BusctlSubCommand::Call => { let destination = self .destination .as_ref() .context("Message does not have a destination")?; cmd.push(destination); } BusctlSubCommand::Wait => { let sender = self .sender .as_ref() .context("Message does not have a sender")?; cmd.push(sender); } } let path = self.path.context("Message does not have a path")?; cmd.push(path); let interface = self .interface .context("Message does not have a interface")?; cmd.push(interface); let member = self.member.context("Message does not have a member")?; cmd.push(member); if matches!(self.sub_command, BusctlSubCommand::Call) && *self.signature != zvariant::Signature::Unit { if let Some(structure) = self.body_structure { let mut body = String::new(); crate::message_fmt::structure_display_fmt(&mut body, &structure)?; let signature = self.signature.to_string_no_parens(); if !body.is_empty() { cmd.push(&signature); cmd.push(&body); } Ok(cmd.join(" ")) } else { anyhow::bail!("Message does not have a body"); } } else { Ok(cmd.join(" ")) } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_man_busctl_example() { // From busctl(1) examples. let expected = "busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartUnit ss cups.service replace"; let body_structure = zvariant::Structure::from(( zvariant::Str::from_static("cups.service"), zvariant::Str::from_static("replace"), )); let path = ObjectPath::from_static_str("/org/freedesktop/systemd1").unwrap(); let iface = InterfaceName::from_static_str("org.freedesktop.systemd1.Manager").unwrap(); let member = MemberName::from_static_str("StartUnit").unwrap(); let cmd = BusctlCommand { sub_command: BusctlSubCommand::Call, connection_type: ConnectionType::System, sender: None, destination: Some(BusName::from_static_str("org.freedesktop.systemd1").unwrap()), path: Some(&path), interface: Some(&iface), member: Some(&member), signature: &body_structure.signature().clone(), body_structure: Some(body_structure), }; assert_eq!(cmd.into_command_string().unwrap(), expected); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/color_widget.rs000066400000000000000000000041271506133026500243450ustar00rootroot00000000000000use gtk::{gdk, glib, graphene, prelude::*, subclass::prelude::*}; const SIZE: i32 = 16; mod imp { use std::cell::Cell; use super::*; #[derive(Debug, glib::Properties)] #[properties(wrapper_type = super::ColorWidget)] pub struct ColorWidget { #[property(get, set = Self::set_rgba, explicit_notify)] pub(super) rgba: Cell, } impl Default for ColorWidget { fn default() -> Self { Self { rgba: Cell::new(gdk::RGBA::TRANSPARENT), } } } #[glib::object_subclass] impl ObjectSubclass for ColorWidget { const NAME: &'static str = "BustleColorWidget"; type Type = super::ColorWidget; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_css_name("colorwidget"); } } #[glib::derived_properties] impl ObjectImpl for ColorWidget { fn constructed(&self) { self.parent_constructed(); self.obj().set_overflow(gtk::Overflow::Hidden); } } impl WidgetImpl for ColorWidget { fn measure(&self, _orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) { (SIZE, SIZE, -1, -1) } fn snapshot(&self, snapshot: >k::Snapshot) { let widget = self.obj(); let color = widget.rgba(); let width = widget.width() as f32; let height = widget.height() as f32; snapshot.append_color(&color, &graphene::Rect::new(0.0, 0.0, width, height)); self.parent_snapshot(snapshot); } } impl ColorWidget { fn set_rgba(&self, rgba: gdk::RGBA) { let obj = self.obj(); if self.rgba.get() == rgba { return; } self.rgba.set(rgba); obj.queue_draw(); obj.notify_rgba(); } } } glib::wrapper! { pub struct ColorWidget(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/colors.rs000066400000000000000000000106621506133026500231660ustar00rootroot00000000000000#![allow(unused)] use gtk::gdk; pub const BLUE_1: gdk::RGBA = gdk::RGBA::new(0.6, 0.756_862_76, 0.945_098_04, 1.0); // #99c1f1 pub const BLUE_2: gdk::RGBA = gdk::RGBA::new(0.384_313_73, 0.627_451, 0.917_647_06, 1.0); // #62a0ea pub const BLUE_3: gdk::RGBA = gdk::RGBA::new(0.207_843_14, 0.517_647_1, 0.894_117_65, 1.0); // #3584e4 pub const BLUE_4: gdk::RGBA = gdk::RGBA::new(0.109_803_92, 0.443_137_26, 0.847_058_83, 1.0); // #1c71d8 pub const BLUE_5: gdk::RGBA = gdk::RGBA::new(0.101_960_786, 0.372_549_03, 0.705_882_4, 1.0); // #1a5fb4 pub const GREEN_1: gdk::RGBA = gdk::RGBA::new(0.560_784_34, 0.941_176_5, 0.643_137_3, 1.0); // #8ff0a4 pub const GREEN_2: gdk::RGBA = gdk::RGBA::new(0.341_176_48, 0.890_196_1, 0.537_254_9, 1.0); // #57e389 pub const GREEN_3: gdk::RGBA = gdk::RGBA::new(0.2, 0.819_607_85, 0.478_431_37, 1.0); // #33d17a pub const GREEN_4: gdk::RGBA = gdk::RGBA::new(0.180_392_16, 0.760_784_3, 0.494_117_65, 1.0); // #2ec27e pub const GREEN_5: gdk::RGBA = gdk::RGBA::new(0.149_019_61, 0.635_294_14, 0.411_764_7, 1.0); // #26a269 pub const YELLOW_1: gdk::RGBA = gdk::RGBA::new(0.976_470_6, 0.941_176_5, 0.419_607_85, 1.0); // #f9f06b pub const YELLOW_2: gdk::RGBA = gdk::RGBA::new(0.972_549, 0.894_117_65, 0.360_784_32, 1.0); // #f8e45c pub const YELLOW_3: gdk::RGBA = gdk::RGBA::new(0.964_705_9, 0.827_451, 0.176_470_6, 1.0); // #f6d32d pub const YELLOW_4: gdk::RGBA = gdk::RGBA::new(0.960_784_3, 0.760_784_3, 0.066_666_67, 1.0); // #f5c211 pub const YELLOW_5: gdk::RGBA = gdk::RGBA::new(0.898_039_2, 0.647_058_84, 0.039_215_688, 1.0); // #e5a50a pub const ORANGE_1: gdk::RGBA = gdk::RGBA::new(1.0, 0.745_098_05, 0.435_294_12, 1.0); // #ffbe6f pub const ORANGE_2: gdk::RGBA = gdk::RGBA::new(1.0, 0.639_215_7, 0.282_352_95, 1.0); // #ffa348 pub const ORANGE_3: gdk::RGBA = gdk::RGBA::new(1.0, 0.470_588_24, 0.0, 1.0); // #ff7800 pub const ORANGE_4: gdk::RGBA = gdk::RGBA::new(0.901_960_8, 0.380_392_16, 0.0, 1.0); // #e66100 pub const ORANGE_5: gdk::RGBA = gdk::RGBA::new(0.776_470_6, 0.274_509_82, 0.0, 1.0); // #c64600 pub const RED_1: gdk::RGBA = gdk::RGBA::new(0.964_705_9, 0.380_392_16, 0.317_647_07, 1.0); // #f66151 pub const RED_2: gdk::RGBA = gdk::RGBA::new(0.929_411_77, 0.2, 0.231_372_55, 1.0); // #ed333b pub const RED_3: gdk::RGBA = gdk::RGBA::new(0.878_431_4, 0.105_882_354, 0.141_176_48, 1.0); // #e01b24 pub const RED_4: gdk::RGBA = gdk::RGBA::new(0.752_941_2, 0.109_803_92, 0.156_862_75, 1.0); // #c01c28 pub const RED_5: gdk::RGBA = gdk::RGBA::new(0.647_058_84, 0.113_725_49, 0.176_470_6, 1.0); // #a51d2d pub const PURPLE_1: gdk::RGBA = gdk::RGBA::new(0.862_745_1, 0.541_176_5, 0.866_666_7, 1.0); // #dc8add pub const PURPLE_2: gdk::RGBA = gdk::RGBA::new(0.752_941_2, 0.380_392_16, 0.796_078_44, 1.0); // #c061cb pub const PURPLE_3: gdk::RGBA = gdk::RGBA::new(0.568_627_5, 0.254_901_98, 0.674_509_8, 1.0); // #9141ac pub const PURPLE_4: gdk::RGBA = gdk::RGBA::new(0.505_882_4, 0.239_215_69, 0.611_764_7, 1.0); // #813d9c pub const PURPLE_5: gdk::RGBA = gdk::RGBA::new(0.380_392_16, 0.207_843_14, 0.513_725_5, 1.0); // #613583 pub const BROWN_1: gdk::RGBA = gdk::RGBA::new(0.803_921_6, 0.670_588_25, 0.560_784_34, 1.0); // #cdab8f pub const BROWN_2: gdk::RGBA = gdk::RGBA::new(0.709_803_94, 0.513_725_5, 0.352_941_2, 1.0); // #b5835a pub const BROWN_3: gdk::RGBA = gdk::RGBA::new(0.596_078_46, 0.415_686_28, 0.266_666_68, 1.0); // #986a44 pub const BROWN_4: gdk::RGBA = gdk::RGBA::new(0.525_490_2, 0.368_627_46, 0.235_294_12, 1.0); // #865e3c pub const BROWN_5: gdk::RGBA = gdk::RGBA::new(0.388_235_3, 0.270_588_25, 0.172_549_02, 1.0); // #63452c pub const LIGHT_1: gdk::RGBA = gdk::RGBA::WHITE; // #ffffff pub const LIGHT_2: gdk::RGBA = gdk::RGBA::new(0.964_705_9, 0.960_784_3, 0.956_862_75, 1.0); // #f6f5f4 pub const LIGHT_3: gdk::RGBA = gdk::RGBA::new(0.870_588_24, 0.866_666_7, 0.854_901_97, 1.0); // #deddda pub const LIGHT_4: gdk::RGBA = gdk::RGBA::new(0.752_941_2, 0.749_019_6, 0.737_254_9, 1.0); // #c0bfbc pub const LIGHT_5: gdk::RGBA = gdk::RGBA::new(0.603_921_6, 0.6, 0.588_235_3, 1.0); // #9a9996 pub const DARK_1: gdk::RGBA = gdk::RGBA::new(0.466_666_67, 0.462_745_1, 0.482_352_94, 1.0); // #77767b pub const DARK_2: gdk::RGBA = gdk::RGBA::new(0.368_627_46, 0.360_784_32, 0.392_156_87, 1.0); // #5e5c64 pub const DARK_3: gdk::RGBA = gdk::RGBA::new(0.239_215_69, 0.219_607_84, 0.274_509_82, 1.0); // #3d3846 pub const DARK_4: gdk::RGBA = gdk::RGBA::new(0.141_176_48, 0.121_568_63, 0.192_156_87, 1.0); // #241f31 pub const DARK_5: gdk::RGBA = gdk::RGBA::BLACK; // #000000 bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/config.rs.in000066400000000000000000000005061506133026500235330ustar00rootroot00000000000000pub const APP_ID: &str = @APP_ID@; pub const GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@; pub const LOCALEDIR: &str = @LOCALEDIR@; pub const PKGDATADIR: &str = @PKGDATADIR@; pub const PROFILE: &str = @PROFILE@; pub const RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/resources.gresource"); pub const VERSION: &str = @VERSION@; bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/details_view.rs000066400000000000000000000226441506133026500243470ustar00rootroot00000000000000use adw::prelude::*; use gettextrs::gettext; use gtk::{ gio, glib::{self, closure_local}, subclass::prelude::*, }; use zbus::zvariant; use crate::{message::Message, message_type::MessageType}; mod imp { use std::{cell::RefCell, sync::LazyLock}; use glib::subclass::Signal; use super::*; use crate::color_widget::ColorWidget; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::DetailsView)] #[template(resource = "/org/freedesktop/Bustle/ui/details_view.ui")] pub struct DetailsView { #[property(get, set = Self::set_message, explicit_notify, nullable)] pub(super) message: RefCell>, #[template_child] pub(super) scrolled_window: TemplateChild, #[template_child] pub(super) associated_message_group: TemplateChild, #[template_child] pub(super) copy_as_busctl_group: TemplateChild, #[template_child] pub(super) type_row: TemplateChild, #[template_child] pub(super) sender_row: TemplateChild, #[template_child] pub(super) destination_row: TemplateChild, #[template_child] pub(super) component_row: TemplateChild, #[template_child] pub(super) component_color: TemplateChild, #[template_child] pub(super) path_row: TemplateChild, #[template_child] pub(super) member_row: TemplateChild, #[template_child] pub(super) interface_row: TemplateChild, #[template_child] pub(super) error_row: TemplateChild, #[template_child] pub(super) flags_row: TemplateChild, #[template_child] pub(super) size_row: TemplateChild, #[template_child] pub(super) signature_row: TemplateChild, #[template_child] pub(super) response_time_row: TemplateChild, #[template_child] pub(super) arguments_text_buffer: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for DetailsView { const NAME: &'static str = "BustleDetailsView"; type Type = super::DetailsView; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); klass.set_layout_manager_type::(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for DetailsView { fn constructed(&self) { self.parent_constructed(); self.obj().update_rows(); } fn dispose(&self) { self.dispose_template(); } fn signals() -> &'static [glib::subclass::Signal] { static SIGNALS: LazyLock> = LazyLock::new(|| { vec![ Signal::builder("show-message-request") .param_types([Message::static_type()]) .build(), ] }); SIGNALS.as_ref() } } impl WidgetImpl for DetailsView {} impl DetailsView { fn set_message(&self, message: Option) { if message == self.message.replace(message.clone()) { return; } let obj = self.obj(); self.message.replace(message.clone()); obj.update_rows(); obj.notify_message(); } } } glib::wrapper! { pub struct DetailsView(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl DetailsView { pub fn connect_show_message_request(&self, f: F) -> glib::SignalHandlerId where F: Fn(&Self, &Message) + 'static, { self.connect_closure( "show-message-request", false, closure_local!(|obj: &Self, message: &Message| { f(obj, message); }), ) } fn update_rows(&self) { let imp = self.imp(); let message = imp.message.borrow(); let message = message.as_ref(); let header = message.map(|message| message.header()); imp.copy_as_busctl_group .set_visible(message.is_some_and(|message| { matches!( message.message_type(), MessageType::MethodCall | MessageType::Signal ) })); imp.associated_message_group .set_visible(message.is_some_and(|message| message.associated_message().is_some())); imp.type_row .set_subtitle(&message.map(|m| m.message_type().i18n()).unwrap_or_default()); imp.sender_row .set_subtitle(&message.map(|m| m.sender_display()).unwrap_or_default()); imp.destination_row .set_subtitle(&message.map(|m| m.destination_display()).unwrap_or_default()); let message_tag = message.map(|m| m.message_tag()).unwrap_or_default(); imp.component_color.set_rgba(message_tag.color()); imp.component_row.set_subtitle(&message_tag.name()); imp.path_row .set_subtitle(&message.map(|m| m.path_display()).unwrap_or_default()); imp.interface_row .set_subtitle(&message.map(|m| m.interface_display()).unwrap_or_default()); imp.member_row .set_subtitle(&message.map(|m| m.member_display()).unwrap_or_default()); imp.error_row.set_visible( message .map(|m| m.message_type().is_error()) .unwrap_or(false), ); imp.error_row.set_subtitle( &header .as_ref() .and_then(|m| m.error_name().map(|e| e.to_string())) .unwrap_or_default(), ); let flags = message.map(|i| i.flags_display()).unwrap_or_default(); imp.flags_row.set_visible(!flags.is_empty()); imp.flags_row.set_subtitle(&flags); imp.size_row.set_subtitle( &header .as_ref() .map(|h| glib::format_size(h.primary().body_len() as u64)) .unwrap_or_default(), ); let signature = header .as_ref() .map(|h| h.signature()) .map(|s| s.to_string()) .unwrap_or_default(); imp.signature_row.set_visible(!signature.is_empty()); imp.signature_row.set_subtitle(&signature); let response_time = message.and_then(|m| m.response_time()); imp.response_time_row.set_visible( response_time.is_some() && message.is_some_and(|m| m.message_type().is_method_return()), ); imp.response_time_row.set_subtitle( &response_time .map(|ts| format!("{:.2} ms", ts.as_millis_f64())) .unwrap_or_default(), ); imp.arguments_text_buffer.set_text( &message .and_then(|m| { m.body() .deserialize::>() .ok() .map(|s| s.to_string()) }) .unwrap_or_default(), ); } } #[gtk::template_callbacks] impl DetailsView { #[template_callback] fn open_doc_button_clicked(&self) { let uri = "https://docs.gtk.org/glib/gvariant-text-format.html"; let launcher = gtk::UriLauncher::new(uri); let window = self.root().and_downcast::().unwrap(); launcher.launch(Some(&window), gio::Cancellable::NONE, move |res| { if let Err(err) = res { tracing::error!("Failed to launch uri {uri}: {err}"); } }); } #[template_callback] fn associated_message_activated(&self) { // It is safe to unwrap as the row is only visible when there is a message // that has an associated message. let associated_message = self.message().unwrap().associated_message().unwrap(); self.emit_by_name::<()>("show-message-request", &[&associated_message]); } #[template_callback] fn copy_as_busctl_activated(&self) { let window = self.root().and_downcast::().unwrap(); let connection_type = window.connection_type(); let toast = match self .imp() .message .borrow() .as_ref() .map(|message| message.print_as_busctl(connection_type)) { Some(Ok(incantation)) => { self.clipboard().set_text(&incantation); tracing::debug!("Copied '{incantation}' to clipboard"); gettext("Copied to clipboard") } Some(Err(err)) => { tracing::error!("Could not generate busctl cmd: {err}"); gettext("Could not generate busctl command") } None => { tracing::error!("Could not generate busctl cmd: there is no message"); gettext("Could not generate busctl command") } }; window.add_message_toast(&toast); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/000077500000000000000000000000001506133026500227165ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/header.rs000066400000000000000000000270451506133026500245240ustar00rootroot00000000000000use adw::prelude::*; use gtk::{ glib::{self, clone}, graphene::Point, pango, subclass::prelude::*, }; use zbus::names::BusName; use crate::{ bus_name_item::{BusNameItem, LookupPoint}, filtered_bus_name_model::FilteredBusNameModel, }; const TEXT_PADDING: i32 = 3; const EXPAND_ANIMATION_DURATION_MS: u32 = 200; // NOTE Keep in sync with diagram_header.ui const PIXEL_SIZE: i32 = 16; const MARGIN: i32 = 3; const IMAGE_SIZE: i32 = PIXEL_SIZE + 2 * MARGIN; mod imp { use std::cell::{Cell, OnceCell, RefCell}; use super::*; #[derive(Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::Header)] #[template(resource = "/org/freedesktop/Bustle/ui/diagram_header.ui")] pub struct Header { #[property(get, set = Self::set_column_width, explicit_notify)] pub(super) column_width: Cell, #[property(get, set = Self::set_first_column_x, explicit_notify)] pub(super) first_column_x: Cell, #[template_child] pub(super) arrow: TemplateChild, pub(super) default_height: Cell, pub(super) expanded_height: Cell, pub(super) current_height: Cell, pub(super) is_expanded: Cell, pub(super) expand_animation: OnceCell, pub(super) default_layout: OnceCell, pub(super) layouts: RefCell>, pub(super) model: OnceCell, } #[glib::object_subclass] impl ObjectSubclass for Header { const NAME: &'static str = "BustleDiagramHeader"; type Type = super::Header; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for Header { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); obj.set_direction(gtk::TextDirection::Ltr); let animation_target = adw::CallbackAnimationTarget::new(clone!( #[weak] obj, move |value| { obj.imp().current_height.set(value.round() as i32); obj.queue_resize(); } )); let animation = adw::TimedAnimation::builder() .widget(&*obj) .duration(EXPAND_ANIMATION_DURATION_MS) .target(&animation_target) .build(); animation.connect_done(clone!( #[weak] obj, move |_| { let imp = obj.imp(); // TODO Use Cell::update when stabilized imp.is_expanded.set(!imp.is_expanded.get()); } )); self.expand_animation.set(animation).unwrap(); let default_layout = obj.create_pango_layout(None); default_layout.set_alignment(pango::Alignment::Center); default_layout.set_ellipsize(pango::EllipsizeMode::End); self.default_layout.set(default_layout).unwrap(); obj.settings().connect_gtk_xft_dpi_notify(clone!( #[weak] obj, move |_| { obj.update_heights(); obj.queue_resize(); obj.queue_draw(); } )); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for Header { fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) { if orientation == gtk::Orientation::Vertical { let height = self.current_height.get(); (IMAGE_SIZE.max(height), IMAGE_SIZE.max(height), -1, -1) } else { debug_assert_eq!(orientation, gtk::Orientation::Horizontal); (-1, -1, -1, -1) } } fn size_allocate(&self, width: i32, height: i32, baseline: i32) { self.arrow.size_allocate( >k::Allocation::new(0, 0, IMAGE_SIZE.max(width), IMAGE_SIZE.max(height)), baseline, ); } fn snapshot(&self, snapshot: >k::Snapshot) { let obj = self.obj(); let color = obj.color(); let column_width = obj.column_width(); let mut cursor_x = obj.first_column_x() - column_width / 2.0; for layout in self.layouts.borrow().iter() { snapshot.save(); snapshot.translate(&Point::new(cursor_x, TEXT_PADDING as f32)); snapshot.append_layout(layout, &color); snapshot.restore(); cursor_x += column_width; } self.parent_snapshot(snapshot); } } impl Header { fn set_column_width(&self, column_width: f32) { if column_width == self.column_width.get() { return; } self.column_width.set(column_width); for layout in self.layouts.borrow().iter() { layout.set_width(pango::units_from_double(column_width as f64)); } let obj = self.obj(); obj.queue_draw(); obj.notify_column_width(); } fn set_first_column_x(&self, first_column_x: f32) { if first_column_x == self.first_column_x.get() { return; } self.first_column_x.set(first_column_x); let obj = self.obj(); obj.queue_draw(); obj.notify_first_column_x(); } } } glib::wrapper! { pub struct Header(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl Header { pub fn set_model(&self, model: FilteredBusNameModel) { let imp = self.imp(); model.connect_items_changed(clone!( #[weak(rename_to = obj)] self, move |model, position, removed, added| { obj.on_model_items_changed(model, position, removed, added); } )); imp.model.set(model).unwrap(); } fn on_model_items_changed( &self, model: &FilteredBusNameModel, position: u32, removed: u32, added: u32, ) { let imp = self.imp(); let default_layout = imp .default_layout .get() .expect("default layout was not set"); let column_width = self.column_width(); let new_layouts = (0..added).map(|i| { let bus_name_item = model .item(position + i) .unwrap() .downcast::() .unwrap(); let name = bus_name_item.name(); let name_display = match *name { BusName::Unique(ref unique_name) => unique_name.as_str(), BusName::WellKnown(ref wk_name) => { wk_name.split('.').next_back().unwrap_or_default() } }; let mut lines = vec![format!( r#"{}"#, name_display )]; lines.extend( bus_name_item // FIXME We don't always want this as some well-known names are not valid at all // indices We want to somehow represent which well-known // names are valid as we scroll on the view .wk_names(LookupPoint::All) .iter() .filter_map(|other_name| { other_name .split('.') .next_back() .map(|last| format!(r#"{last}"#)) }), ); let text = lines.join("\n"); let layout = default_layout.copy(); layout.set_width(pango::units_from_double(column_width as f64)); layout.set_markup(&text); layout }); imp.layouts.borrow_mut().splice( position as usize..(removed + position) as usize, new_layouts, ); debug_assert_eq!(imp.layouts.borrow().len(), model.n_items() as usize); self.update_heights(); self.queue_resize(); self.queue_draw(); } fn update_heights(&self) { let imp = self.imp(); let layouts = imp.layouts.borrow(); let Some(first_layout) = layouts.first() else { imp.current_height.set(0); imp.default_height.set(0); imp.expanded_height.set(0); return; }; let first_line = first_layout .downcast_ref::() .unwrap() .line(0) .expect("there must be at least one line"); let (_, logical_extents) = first_line.pixel_extents(); let default_height = logical_extents.height() + TEXT_PADDING; imp.default_height.set(default_height); let max_line_count = layouts .iter() .map(|layout| layout.line_count()) .max() .unwrap(); debug_assert!(max_line_count > 0); // Since somehow a line's height extent can be greater than the entire layout's // height extent, workaround it by just using default height if the maximum // amount of line is 1. let expanded_height = if max_line_count == 1 { default_height } else { layouts .iter() .map(|layout| { let (_, logical_extents) = layout.pixel_extents(); logical_extents.height() }) .max() .unwrap() + TEXT_PADDING * 2 }; imp.expanded_height.set(expanded_height); if imp.is_expanded.get() { imp.current_height.set(expanded_height); } else { imp.current_height.set(default_height); } } } #[gtk::template_callbacks] impl Header { #[template_callback] fn gesture_click_released(&self, _n_press: i32, x: f64, y: f64, gesture: >k::GestureClick) { gesture.set_state(gtk::EventSequenceState::Claimed); if gesture .widget() .is_some_and(|widget| !widget.contains(x, y)) { return; } let imp = self.imp(); let animation = imp .expand_animation .get() .expect("animation was not set on constructed"); if animation.state() == adw::AnimationState::Playing { return; } if imp.is_expanded.get() { animation.set_value_from(imp.expanded_height.get() as f64); animation.set_value_to(imp.default_height.get() as f64); imp.arrow.remove_css_class("expanded"); imp.arrow.remove_css_class("accent"); imp.arrow.add_css_class("dimmed"); } else { animation.set_value_from(imp.default_height.get() as f64); animation.set_value_to(imp.expanded_height.get() as f64); imp.arrow.remove_css_class("dimmed"); imp.arrow.add_css_class("accent"); imp.arrow.add_css_class("expanded"); } animation.play(); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/mod.rs000066400000000000000000000162521506133026500240510ustar00rootroot00000000000000mod header; mod row; mod row_tag; mod view; use anyhow::Result; use gtk::{ gdk, glib::{self, clone}, prelude::*, subclass::prelude::*, }; use crate::{ diagram::{header::Header, view::View}, filtered_message_model::FilteredMessageModel, message::Message, }; const COLUMN_WIDTH_SP: f64 = 90.0; // This must be synced with DiagramRow label char widths const FIRST_COLUMN_INITIAL_X_SP: f64 = 600.0; mod imp { use std::{ cell::{Cell, OnceCell}, marker::PhantomData, }; use super::*; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::Diagram)] #[template(resource = "/org/freedesktop/Bustle/ui/diagram.ui")] pub struct Diagram { #[property(get, set, construct_only)] pub(super) model: OnceCell, #[property(get = Self::selected_message)] pub(super) selected_message: PhantomData>, #[template_child] pub(super) header: TemplateChild

, #[template_child] pub(super) separator: TemplateChild, // Unused, but needed for disposal #[template_child] pub(super) scrolled_window: TemplateChild, #[template_child] pub(super) view: TemplateChild, pub(super) should_stick: Cell, pub(super) is_sticky: Cell, pub(super) is_auto_scrolling: Cell, } #[glib::object_subclass] impl ObjectSubclass for Diagram { const NAME: &'static str = "BustleDiagram"; type Type = super::Diagram; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_layout_manager_type::(); klass.bind_template(); klass.bind_template_instance_callbacks(); klass.add_binding(gdk::Key::Escape, gdk::ModifierType::empty(), |obj| { obj.imp().view.unselect(); glib::Propagation::Stop }) } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for Diagram { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let layout_manager = self .obj() .layout_manager() .and_downcast::() .unwrap(); layout_manager.set_orientation(gtk::Orientation::Vertical); let model = obj.model(); self.view.set_model(&model); self.header.set_model(model.filtered_bus_names().clone()); let hadj = self.view.hadjustment().unwrap(); hadj.connect_value_changed(clone!( #[weak] obj, move |_| { obj.update_first_column_x(); } )); let vadj = self.view.vadjustment().unwrap(); vadj.connect_value_changed(clone!( #[weak] obj, move |vadj| { let imp = obj.imp(); if imp.should_stick.get() { let is_at_bottom = vadj.value() + vadj.page_size() == vadj.upper(); if imp.is_auto_scrolling.get() { if is_at_bottom { imp.is_auto_scrolling.set(false); imp.is_sticky.set(true); } else { obj.scroll_to_end(); } } else { imp.is_sticky.set(is_at_bottom); } } } )); vadj.connect_upper_notify(clone!( #[weak] obj, move |_| { let imp = obj.imp(); if imp.should_stick.get() && imp.is_sticky.get() { obj.scroll_to_end(); } } )); vadj.connect_page_size_notify(clone!( #[weak] obj, move |_| { let imp = obj.imp(); if imp.should_stick.get() && imp.is_sticky.get() { obj.scroll_to_end(); } } )); obj.settings().connect_gtk_xft_dpi_notify(clone!( #[weak] obj, move |_| { obj.update_column_width_and_first_column_initial_x(); obj.update_first_column_x(); } )); obj.update_column_width_and_first_column_initial_x(); obj.update_first_column_x(); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for Diagram {} impl Diagram { fn selected_message(&self) -> Option { self.view.selected_message() } } } glib::wrapper! { pub struct Diagram(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl Diagram { /// Set whether to stick the view to the newest messages. pub fn set_should_stick(&self, should_stick: bool) { self.imp().should_stick.set(should_stick); } pub fn scroll_to(&self, message: &Message, flags: gtk::ListScrollFlags) -> Result<()> { self.imp().view.scroll_to(message, flags) } /// Returns the x coordinate where the first column should be drawn. fn first_column_initial_x(&self) -> f32 { adw::LengthUnit::Sp.to_px(FIRST_COLUMN_INITIAL_X_SP, Some(&self.settings())) as f32 } fn scroll_to_end(&self) { let imp = self.imp(); imp.is_auto_scrolling.set(true); imp.scrolled_window .emit_scroll_child(gtk::ScrollType::End, false); } /// This must be called when text scale factor changes fn update_column_width_and_first_column_initial_x(&self) { let imp = self.imp(); let column_width = adw::LengthUnit::Sp.to_px(COLUMN_WIDTH_SP, Some(&self.settings())) as f32; imp.header.set_column_width(column_width); imp.view.set_column_width(column_width); imp.view .set_first_column_initial_x(self.first_column_initial_x()); } /// This must be called when either: /// a. the view's horizontal adjustment changes /// b. the text scale factor changes fn update_first_column_x(&self) { let imp = self.imp(); let first_column_x = self.first_column_initial_x() - self .imp() .view .hadjustment() .map_or(0.0, |hadj| hadj.value() as f32); imp.header.set_first_column_x(first_column_x); imp.view.set_first_column_x(first_column_x); } } #[gtk::template_callbacks] impl Diagram { #[template_callback] fn view_selected_message_notify(&self) { self.notify_selected_message(); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/row.rs000066400000000000000000000120311506133026500240700ustar00rootroot00000000000000use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::{message::Message, timestamp::Timestamp}; mod imp { use std::cell::RefCell; use super::*; use crate::diagram::row_tag::RowTag; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::Row)] #[template(resource = "/org/freedesktop/Bustle/ui/diagram_row.ui")] pub struct Row { #[property(get, set = Self::set_message, explicit_notify, nullable)] pub(super) message: RefCell>, #[property(get, set = Self::set_head_message, explicit_notify, nullable)] pub(super) head_message: RefCell>, #[template_child] pub(super) timestamp_label: TemplateChild, #[template_child] pub(super) tag: TemplateChild, #[template_child] pub(super) vbox: TemplateChild, // Unused, but needed for disposal #[template_child] pub(super) title_label: TemplateChild, #[template_child] pub(super) subtitle_label: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for Row { const NAME: &'static str = "BustleDiagramRow"; type Type = super::Row; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_layout_manager_type::(); klass.bind_template(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for Row { fn constructed(&self) { self.parent_constructed(); let layout_manager = self .obj() .layout_manager() .and_downcast::() .unwrap(); layout_manager.set_spacing(12); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for Row {} impl Row { fn set_message(&self, message: Option) { if message == self.message.replace(message.clone()) { return; } let obj = self.obj(); let message_tag = message.map(|m| m.message_tag()).unwrap_or_default(); self.tag.set_message_tag(message_tag); obj.update_timestamp_label(); obj.update_title_subtitle_labels(); obj.notify_message(); let manager = adw::StyleManager::default(); manager.connect_dark_notify(glib::clone!( #[weak] obj, move |_manager| { obj.update_title_subtitle_labels(); } )); } fn set_head_message(&self, head_message: Option) { if head_message == self.head_message.replace(head_message.clone()) { return; } let obj = self.obj(); obj.update_timestamp_label(); obj.notify_head_message(); } } } glib::wrapper! { pub struct Row(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl Row { fn update_timestamp_label(&self) { let imp = self.imp(); if let Some(message) = self.message() { let elapsed_ms = { let head_message = self .head_message() .expect("there must always be a head message if there is a message"); if head_message == message { Timestamp::default() } else { message.timestamp() - head_message.timestamp() } }; imp.timestamp_label .set_label(&format!("{} ms", elapsed_ms.as_millis_f64().round())); } else { imp.timestamp_label.set_label(""); } } fn update_title_subtitle_labels(&self) { let imp = self.imp(); if let Some(message) = self.message() { let title = message.path_display(); if message.message_type().is_method_return() { // We need to use `set_markup` even we don't use any markup because it somehow // fix issues where random part of the label is bold. Possibly, // a `GtkInscription` issue? imp.title_label.set_markup(&title); imp.subtitle_label .set_markup(&format!("{}", &message.member_markup(true))); } else { imp.title_label .set_markup(&format!("{}", glib::markup_escape_text(&title))); imp.subtitle_label.set_markup(&message.member_markup(true)); } } else { imp.title_label.set_text(""); imp.subtitle_label.set_text(""); } } } impl Default for Row { fn default() -> Self { glib::Object::new() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/row_tag.rs000066400000000000000000000046141506133026500247330ustar00rootroot00000000000000use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::message_tag::MessageTag; mod imp { use std::cell::Cell; use super::*; use crate::color_widget::ColorWidget; #[derive(Debug, glib::Properties, Default, gtk::CompositeTemplate)] #[properties(wrapper_type = super::RowTag)] #[template(resource = "/org/freedesktop/Bustle/ui/diagram_row_tag.ui")] pub struct RowTag { #[property(get, set = Self::set_message_tag, explicit_notify, default)] pub(super) message_tag: Cell, #[template_child] pub(super) label: TemplateChild, #[template_child] pub(super) color: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for RowTag { const NAME: &'static str = "BustleDiagramRowTag"; type Type = super::RowTag; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.set_layout_manager_type::(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for RowTag { fn constructed(&self) { self.parent_constructed(); let layout_manager = self .obj() .layout_manager() .and_downcast::() .unwrap(); layout_manager.set_spacing(6); self.obj().update_color_and_label(); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for RowTag {} impl RowTag { fn set_message_tag(&self, tag: MessageTag) { let obj = self.obj(); if self.message_tag.get() == tag { return; } self.message_tag.set(tag); obj.update_color_and_label(); obj.notify_message_tag(); } } } glib::wrapper! { pub struct RowTag(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl RowTag { fn update_color_and_label(&self) { let imp = self.imp(); let tag = self.message_tag(); imp.color.set_rgba(tag.color()); imp.label.set_text(Some(&tag.name())); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/diagram/view.rs000066400000000000000000000720271506133026500242460ustar00rootroot00000000000000use anyhow::{Context, Result}; use gtk::{ gdk, glib::{self, clone}, graphene::{Point, Rect}, gsk, pango, prelude::*, subclass::prelude::*, }; use indexmap::IndexMap; use zbus::names::BusName; use crate::{ diagram::row::Row, filtered_message_model::FilteredMessageModel, message::{Message, ReceiveIndex}, message_type::MessageType, }; const COLUMN_LINE_WIDTH: f32 = 1.0; const ARROW_LINE_WIDTH: f32 = 2.0; /// Determines on what side the arc will curve enum ArcSide { Left, Right, } /// Determines the type of arrow tip enum ArrowTipType { Full, TopHalf, BottomHalf, } mod imp { use std::{ cell::{Cell, OnceCell, RefCell}, marker::PhantomData, }; use gtk::glib::WeakRef; use super::*; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::View)] #[template(resource = "/org/freedesktop/Bustle/ui/diagram_view.ui")] pub struct View { #[property(get = Self::selected_message)] pub(super) selected_message: PhantomData>, #[property(get, set = Self::set_column_width, explicit_notify)] pub(super) column_width: Cell, #[property(get, set = Self::set_first_column_x, explicit_notify)] pub(super) first_column_x: Cell, #[property(get, set = Self::set_first_column_initial_x, explicit_notify)] pub(super) first_column_initial_x: Cell, #[property(get = Self::hscroll_policy, set = Self::set_hscroll_policy, override_interface = gtk::Scrollable)] pub(super) hscroll_policy: PhantomData, #[property(get = Self::hadjustment, set = Self::set_hadjustment, override_interface = gtk::Scrollable)] pub(super) hadjustment: PhantomData>, #[property(get = Self::vscroll_policy, set = Self::set_vscroll_policy, override_interface = gtk::Scrollable)] pub(super) vscroll_policy: PhantomData, #[property(get = Self::vadjustment, set = Self::set_vadjustment, override_interface = gtk::Scrollable)] pub(super) vadjustment: PhantomData>, #[template_child] pub(super) list_view: TemplateChild, #[template_child] pub(super) selection_model: TemplateChild, pub(super) rows: RefCell>>, pub(super) row_width_request: Cell, pub(super) distance_between_rows_center: Cell>, pub(super) response_time_layout: OnceCell, } #[glib::object_subclass] impl ObjectSubclass for View { const NAME: &'static str = "BustleDiagramView"; type Type = super::View; type ParentType = gtk::Widget; type Interfaces = (gtk::Scrollable,); fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for View { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); obj.set_direction(gtk::TextDirection::Ltr); let layout = obj.create_pango_layout(None); self.response_time_layout.set(layout).unwrap(); obj.settings().connect_gtk_xft_dpi_notify(clone!( #[weak] obj, move |_| { obj.imp().distance_between_rows_center.set(None); } )); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for View { fn snapshot(&self, snapshot: >k::Snapshot) { let obj = self.obj(); if obj.model().n_items() == 0 { tracing::debug!("Filter list model is empty, not drawing"); self.parent_snapshot(snapshot); return; }; obj.draw_columns(snapshot); for row in self.rows.borrow().iter() { let Some(row) = row.upgrade() else { continue; }; if !row.is_drawable() { continue; } let Some(message) = row.message() else { continue; }; // Translate from row center coordinates to self coordinates let row_center_y = row .compute_point( &*obj, &Point::new(row.width() as f32 / 2.0, row.height() as f32 / 2.0), ) .unwrap() .y(); match message.message_type() { MessageType::MethodCall => { if let Err(err) = obj.draw_method_call(snapshot, &message, row_center_y) { tracing::warn!(%message, "Can't draw method call: {:?}", err); } } MessageType::MethodReturn | MessageType::Error => { let Some(call_message) = message.associated_message() else { tracing::warn!(%message, "Can't draw return message; it has no call message"); continue; }; if let Err(err) = obj.draw_method_return( snapshot, &call_message, &message, &row, row_center_y, ) { tracing::warn!(%call_message, %message, "Can't draw method return: {:?}", err); } } MessageType::Signal => { if let Err(err) = obj.draw_signal(snapshot, &message, row_center_y) { tracing::warn!(%message, "Can't draw signal: {:?}", err); } } } } self.parent_snapshot(snapshot); } fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) { self.list_view.measure(orientation, for_size) } fn size_allocate(&self, width: i32, height: i32, baseline: i32) { self.list_view .size_allocate(>k::Allocation::new(0, 0, width, height), baseline) } } impl ScrollableImpl for View {} impl View { fn selected_message(&self) -> Option { self.selection_model .selected_item() .map(|selected_item| selected_item.downcast().unwrap()) } fn set_column_width(&self, column_width: f32) { if column_width == self.column_width.get() { return; } self.column_width.set(column_width); let obj = self.obj(); obj.update_row_width_request(); obj.queue_draw(); obj.notify_column_width(); } fn set_first_column_x(&self, first_column_x: f32) { if first_column_x == self.first_column_x.get() { return; } self.first_column_x.set(first_column_x); let obj = self.obj(); obj.queue_draw(); obj.notify_first_column_x(); } fn set_first_column_initial_x(&self, first_column_initial_x: f32) { if first_column_initial_x == self.first_column_initial_x.get() { return; } self.first_column_initial_x.set(first_column_initial_x); let obj = self.obj(); obj.update_row_width_request(); obj.notify_first_column_x(); } fn hscroll_policy(&self) -> gtk::ScrollablePolicy { self.list_view.hscroll_policy() } fn set_hscroll_policy(&self, policy: gtk::ScrollablePolicy) { self.list_view.set_hscroll_policy(policy); } fn hadjustment(&self) -> Option { self.list_view.hadjustment() } fn set_hadjustment(&self, adj: Option<>k::Adjustment>) { self.list_view.set_hadjustment(adj); } fn vscroll_policy(&self) -> gtk::ScrollablePolicy { self.list_view.vscroll_policy() } fn set_vscroll_policy(&self, policy: gtk::ScrollablePolicy) { self.list_view.set_vscroll_policy(policy); } fn vadjustment(&self) -> Option { self.list_view.vadjustment() } fn set_vadjustment(&self, adj: Option<>k::Adjustment>) { self.list_view.set_vadjustment(adj); } } } glib::wrapper! { pub struct View(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible, gtk::Scrollable; } impl View { pub fn set_model(&self, model: &FilteredMessageModel) { let imp = self.imp(); model.filtered_bus_names().connect_items_changed(clone!( #[weak(rename_to = obj)] self, move |_, _, _, _| { obj.update_row_width_request(); } )); imp.selection_model.set_model(Some(model)); self.update_row_width_request(); } pub fn model(&self) -> FilteredMessageModel { self.imp() .selection_model .model() .expect("model must be set") .downcast() .unwrap() } pub fn unselect(&self) { self.imp() .selection_model .set_selected(gtk::INVALID_LIST_POSITION); } pub fn scroll_to(&self, message: &Message, flags: gtk::ListScrollFlags) -> Result<()> { let index = self .model() .get_index_of(message) .context("Message does not exist on the model")?; self.imp().list_view.scroll_to(index as u32, flags, None); Ok(()) } /// Returns the x coordinate where the given name must be placed fn x_for_name(&self, name: &BusName<'_>, receive_index: ReceiveIndex) -> Result { let model = self.model(); let bus_names = model.filtered_bus_names(); let bus_name_index = bus_names.get_index_of(name).or_else(|| { tracing::trace!("`{}` was not found in keys; looking for other names", name); match name { BusName::Unique(_) => None, BusName::WellKnown(wk_name) => { bus_names.get_index_of_wk_name(wk_name, receive_index.into()) } } }); if tracing::enabled!(tracing::Level::DEBUG) && bus_name_index.is_none() { // This makes it easier to read the names when debugging let bus_names = bus_names .iter() .map(|bus_name_item| { let name = bus_name_item.name(); let wk_names = bus_name_item.wk_names(receive_index.into()); ( name.to_string(), wk_names .iter() .map(|name| name.to_string()) .collect::>(), ) }) .collect::>(); tracing::debug!(?bus_names, "Index of `{}` in bus names was not found", name); } let bus_name_index = bus_name_index.context("Name not found in bus names")?; Ok(self.first_column_x() + self.column_width() * bus_name_index as f32) } /// Computes the distance from row center to an adjacent row's center, /// then caches and return it. This assumes that distance between row /// centers is consistent across rows, and thus heights. /// /// Returns None if there is no visible adjacent row /// /// This must be invalidated when the text scale factor changes fn distance_between_rows_center(&self, row: &Row, row_center_y: f32) -> Option { // FIXME Don't have any better idea to compute distance between rows centers let imp = self.imp(); if let Some(distance) = imp.distance_between_rows_center.get() { return Some(distance); } let parent = row.parent().unwrap(); // Get GtkListItemWidget let first_prev_adjacent_parent = parent.prev_sibling().filter(|w| w.is_drawable()); let second_prev_adjacent_parent = first_prev_adjacent_parent .as_ref() .and_then(|w| w.prev_sibling()) .filter(|w| w.is_drawable()); let first_next_adjacent_parent = parent.next_sibling().filter(|w| w.is_drawable()); let second_next_adjacent_parent = first_next_adjacent_parent .as_ref() .and_then(|w| w.next_sibling()) .filter(|w| w.is_drawable()); // Get the adjacent row which is farthest to the edge. For example, // we don't want to use an adjacent row clipped to the edge since GTK // doesn't correctly compute the row's center coordinates in that case. let adjacent_parent = match ( second_prev_adjacent_parent, first_prev_adjacent_parent, first_next_adjacent_parent, second_next_adjacent_parent, ) { (Some(_), Some(w), Some(_), Some(_)) | (Some(_), Some(w), Some(_), None) | (None, Some(_), Some(w), Some(_)) | (None, None, Some(w), Some(_)) | (Some(_), Some(w), None, None) | (None, Some(w), Some(_), None) | (None, None, Some(w), None) | (None, Some(w), None, None) => w, (None, None, None, None) => return None, (_, _, _, _) => unreachable!(), }; let adjacent_row = adjacent_parent .first_child() // Get our DiagramRow .unwrap() .downcast::() .unwrap(); debug_assert_eq!(adjacent_row.height(), row.height()); // Translate the adjacent row center coordinates into self's coordinates let adjacent_row_center_y = adjacent_row .compute_point( self, &Point::new( adjacent_row.width() as f32 / 2.0, adjacent_row.height() as f32 / 2.0, ), ) .unwrap() .y(); let ret = (adjacent_row_center_y - row_center_y).abs(); debug_assert!(ret >= row.height() as f32); imp.distance_between_rows_center.set(Some(ret)); Some(ret) } fn bounds(&self) -> Rect { Rect::new(0.0, 0.0, self.width() as f32, self.height() as f32) } fn arrow_stroke(&self) -> gsk::Stroke { gsk::Stroke::builder(ARROW_LINE_WIDTH) .line_join(gsk::LineJoin::Round) .line_cap(gsk::LineCap::Round) .build() } fn error_arrow_color(&self) -> gdk::RGBA { if adw::StyleManager::default().is_dark() { crate::colors::RED_1 } else { crate::colors::RED_3 } } fn method_arc_color(&self) -> gdk::RGBA { if adw::StyleManager::default().is_dark() { crate::colors::GREEN_3 } else { crate::colors::GREEN_5 } } fn draw_columns(&self, snapshot: >k::Snapshot) { let color = self.color(); let height = self.height(); let first_column_x = self.first_column_x(); let column_width = self.column_width(); let model = self.model(); let bus_names = model.filtered_bus_names(); let path_builder = gsk::PathBuilder::new(); let mut cursor_x = first_column_x; for _ in bus_names.iter() { let x = cursor_x.round(); path_builder.move_to(x, 0.0); path_builder.line_to(x, height as f32); cursor_x += column_width; } snapshot.push_stroke( &path_builder.to_path(), &gsk::Stroke::new(COLUMN_LINE_WIDTH), ); snapshot.append_color(&color.with_alpha(color.alpha() * 0.3), &self.bounds()); snapshot.pop(); } fn draw_method_call( &self, snapshot: >k::Snapshot, message: &Message, row_center_y: f32, ) -> Result<()> { let sender = message.sender().context("Call message has no sender")?; let destination = message .destination() .context("Call message has no destination")?; let start_x = self.x_for_name(&BusName::from(sender), message.receive_index())?; let end_x = self.x_for_name(&destination, message.receive_index())?; let path_builder = gsk::PathBuilder::new(); path_builder.add_arrow( &Point::new(start_x, row_center_y), &Point::new(end_x, row_center_y), ArrowTipType::TopHalf, ); snapshot.push_stroke(&path_builder.to_path(), &self.arrow_stroke()); snapshot.append_color(&self.color(), &self.bounds()); snapshot.pop(); Ok(()) } fn draw_method_return( &self, snapshot: >k::Snapshot, call_message: &Message, return_message: &Message, return_row: &Row, return_row_center_y: f32, ) -> Result<()> { const RESPONSE_TIME_LAYOUT_OFFSET_X: f32 = 20.0; let model = self.model(); let return_message_sender = return_message .sender() .context("Return message has no sender")?; debug_assert!( call_message .destination() .is_some_and(|call_message_destination| call_message_destination == return_message_sender || match call_message_destination { BusName::Unique(_) => false, BusName::WellKnown(ref wk_name) => model .filtered_bus_names() .get(&BusName::from(return_message_sender.as_ref())) .unwrap() .wk_names(return_message.receive_index().into()) .contains(wk_name), }) ); let call_message_sender = call_message .sender() .context("Call message has no sender")?; debug_assert!( return_message .destination() .is_some_and(|return_message_destination| return_message_destination == call_message_sender || match return_message_destination { BusName::Unique(_) => false, BusName::WellKnown(ref wk_name) => model .filtered_bus_names() .get(&BusName::from(call_message_sender.as_ref())) .unwrap() .wk_names(return_message.receive_index().into()) .contains(wk_name), }) ); let start_x = self.x_for_name( &BusName::from(return_message_sender), return_message.receive_index(), )?; let end_x = self.x_for_name( &BusName::from(call_message_sender), return_message.receive_index(), )?; let arrow_path_builder = gsk::PathBuilder::new(); arrow_path_builder.add_arrow( &Point::new(start_x, return_row_center_y), &Point::new(end_x, return_row_center_y), ArrowTipType::BottomHalf, ); let arrow_color = if return_message.message_type() == MessageType::Error { self.error_arrow_color() } else { self.color() }; snapshot.push_stroke(&arrow_path_builder.to_path(), &self.arrow_stroke()); snapshot.append_color(&arrow_color, &self.bounds()); snapshot.pop(); let call_row_center_y = { let return_message_index = model.get_index_of(return_message).unwrap(); let call_message_index = model.get_index_of(call_message).unwrap(); // We can't just use row heights as we also need to account for the paddings. let other_row_offset_y = self .distance_between_rows_center(return_row, return_row_center_y) .unwrap() * (return_message_index as f32 - call_message_index as f32); return_row_center_y - other_row_offset_y }; // TODO draw this arc as long as the rows in the middle of call and return // row is visible let arc_path_builder = gsk::PathBuilder::new(); arc_path_builder.add_arc( &Point::new(start_x, return_row_center_y), &Point::new(start_x, call_row_center_y), if (end_x - start_x).is_sign_positive() { ArcSide::Left } else { ArcSide::Right }, ); let arc_stroke = gsk::Stroke::new(ARROW_LINE_WIDTH); arc_stroke.set_dash(&[3.0, 3.0]); snapshot.push_stroke(&arc_path_builder.to_path(), &arc_stroke); snapshot.append_color(&self.method_arc_color(), &self.bounds()); snapshot.pop(); let response_time_layout = self .imp() .response_time_layout .get() .expect("response time layout must be set on construcetd"); response_time_layout.set_markup(&format!( r#"{} ms"#, (return_message.timestamp() - call_message.timestamp()) .as_millis_f64() .round() )); let (ink_extents, _) = response_time_layout.pixel_extents(); snapshot.save(); if (end_x - start_x).is_sign_positive() { snapshot.translate(&Point::new( start_x - RESPONSE_TIME_LAYOUT_OFFSET_X - ink_extents.width() as f32, return_row_center_y - ink_extents.height() as f32, )); } else { snapshot.translate(&Point::new( start_x + RESPONSE_TIME_LAYOUT_OFFSET_X, return_row_center_y - ink_extents.height() as f32, )); } snapshot.append_layout(response_time_layout, &self.color()); snapshot.restore(); Ok(()) } fn draw_signal( &self, snapshot: >k::Snapshot, message: &Message, row_center_y: f32, ) -> Result<()> { const CIRCLE_RADIUS: f32 = 5.0; const ARROW_END_OFFSET: f32 = 20.0; let sender = message.sender().context("Signal message has no sender")?; let start_x = self.x_for_name(&BusName::from(sender), message.receive_index())?; let path_builder = gsk::PathBuilder::new(); path_builder.add_circle(&Point::new(start_x, row_center_y), CIRCLE_RADIUS); if let Some(destination) = message.destination() { // This is a targeted signal let end_x = self.x_for_name(&destination, message.receive_index())?; path_builder.add_arrow( &Point::new( start_x + negate_if((end_x - start_x).is_sign_negative(), CIRCLE_RADIUS), row_center_y, ), &Point::new(end_x, row_center_y), ArrowTipType::Full, ); } else { let model = self.model(); let bus_names = model.filtered_bus_names(); let first_column_x = self.first_column_x(); let column_width = self.column_width(); // FIXME Is this correct that the arrows extend on all columns/names? // Left arrow path_builder.add_arrow( &Point::new(start_x - CIRCLE_RADIUS, row_center_y), &Point::new(first_column_x - ARROW_END_OFFSET, row_center_y), ArrowTipType::Full, ); // Right arrow path_builder.add_arrow( &Point::new(start_x + CIRCLE_RADIUS, row_center_y), &Point::new( first_column_x + column_width * (bus_names.n_items() - 1) as f32 + ARROW_END_OFFSET, row_center_y, ), ArrowTipType::Full, ); } snapshot.push_stroke(&path_builder.to_path(), &self.arrow_stroke()); snapshot.append_color(&self.color(), &self.bounds()); snapshot.pop(); Ok(()) } // This must be called when either: // a. the model is replaced // b. the filtered bus names items changed // c. first_column_initial_x or column_width changed fn update_row_width_request(&self) { let imp = self.imp(); let n_bus_names = self.model().filtered_bus_names().n_items(); let new_value = if n_bus_names == 0 { -1 } else { let column_width = self.column_width(); (self.first_column_initial_x() + column_width * n_bus_names as f32 - column_width / 2.0) .round() as i32 }; imp.row_width_request.set(new_value); for row in imp.rows.borrow().iter() { if let Some(row) = row.upgrade() { row.set_width_request(new_value); } } } } #[gtk::template_callbacks] impl View { #[template_callback] fn factory_setup(&self, list_item: &glib::Object) { let imp = self.imp(); let list_item = list_item.downcast_ref::().unwrap(); let row = Row::default(); row.set_width_request(imp.row_width_request.get()); list_item.set_child(Some(&row)); // Remove dead weak references imp.rows.borrow_mut().retain(|i| i.upgrade().is_some()); debug_assert_eq!(imp.rows.borrow().iter().filter(|i| **i == row).count(), 0); imp.rows.borrow_mut().push(row.downgrade()); } #[template_callback] fn factory_teardown(&self, list_item: &glib::Object) { let imp = self.imp(); let list_item = list_item.downcast_ref::().unwrap(); if let Some(row) = list_item.child() { let row = row.downcast_ref::().unwrap(); debug_assert_eq!(imp.rows.borrow().iter().filter(|i| *i == row).count(), 1); imp.rows .borrow_mut() .retain(|i| i.upgrade().is_some_and(|i| &i != row)); } } #[template_callback] fn factory_bind(&self, list_item: &glib::Object) { let list_item = list_item.downcast_ref::().unwrap(); let row = list_item.child().unwrap().downcast::().unwrap(); row.set_width_request(self.imp().row_width_request.get()); row.set_head_message( self.model() .item(0) .map(|model| model.downcast::().unwrap()), ); row.set_message( list_item .item() .map(|item| item.downcast::().unwrap()), ); } #[template_callback] fn factory_unbind(&self, list_item: &glib::Object) { let list_item = list_item.downcast_ref::().unwrap(); let row = list_item.child().unwrap().downcast::().unwrap(); row.set_width_request(-1); row.set_message(None::); row.set_head_message(None::); } #[template_callback] fn selection_model_selected_item_notify(&self) { self.notify_selected_message(); } } fn negate_if(negate: bool, num: f32) -> f32 { if negate { -num } else { num } } trait PathBuilderExt { fn add_arrow(&self, start: &Point, end: &Point, tip_type: ArrowTipType); fn add_arc(&self, start: &Point, end: &Point, arc_side: ArcSide); } impl PathBuilderExt for gsk::PathBuilder { fn add_arrow(&self, start: &Point, end: &Point, tip_type: ArrowTipType) { const TIP_OFFSET_X: f32 = 10.0; const TIP_OFFSET_Y: f32 = 5.0; self.move_to(start.x(), start.y()); self.line_to(end.x(), end.y()); let tip_offset_x = negate_if((end.x() - start.x()).is_sign_positive(), TIP_OFFSET_X); match tip_type { ArrowTipType::Full => { self.rel_move_to(tip_offset_x, -TIP_OFFSET_Y); self.line_to(end.x(), end.y()); self.rel_line_to(tip_offset_x, TIP_OFFSET_Y); } ArrowTipType::TopHalf => { self.rel_line_to(tip_offset_x, -TIP_OFFSET_Y); } ArrowTipType::BottomHalf => { self.rel_line_to(tip_offset_x, TIP_OFFSET_Y); } } } fn add_arc(&self, start: &Point, end: &Point, arc_side: ArcSide) { const MID_OFFSET_X: f32 = 50.0; let mid_x = (start.x() + end.x()) / 2.0; let mid_y = (start.y() + end.y()) / 2.0; self.move_to(start.x(), start.y()); self.quad_to( mid_x + negate_if(matches!(arc_side, ArcSide::Left), MID_OFFSET_X), mid_y, end.x(), end.y(), ); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/every_filter.rs000066400000000000000000000063221506133026500243620ustar00rootroot00000000000000use gtk::{ glib::{self, clone}, prelude::*, subclass::prelude::*, }; mod imp { use std::{cell::RefCell, collections::HashMap}; use super::*; #[derive(Default)] pub struct EveryFilter { pub(super) filters: RefCell>, } #[glib::object_subclass] impl ObjectSubclass for EveryFilter { const NAME: &'static str = "BustleEveryFilter"; type Type = super::EveryFilter; type ParentType = gtk::Filter; } impl ObjectImpl for EveryFilter { fn dispose(&self) { let obj = self.obj(); obj.clear(); } } impl FilterImpl for EveryFilter { fn strictness(&self) -> gtk::FilterMatch { let mut ret = gtk::FilterMatch::All; for (filter, _) in self.filters.borrow().values() { match filter.strictness() { gtk::FilterMatch::Some => { ret = gtk::FilterMatch::Some; } gtk::FilterMatch::None => { return gtk::FilterMatch::None; } gtk::FilterMatch::All => {} _ => unreachable!(), } } ret } fn match_(&self, item: &glib::Object) -> bool { self.filters .borrow() .values() .all(|(filter, _)| filter.match_(item)) } } } glib::wrapper! { pub struct EveryFilter(ObjectSubclass) @extends gtk::Filter; } impl EveryFilter { pub fn contains(&self, id: &str) -> bool { self.imp().filters.borrow().contains_key(id) } pub fn insert(&self, id: impl Into, filter: impl IsA) -> bool { let imp = self.imp(); let signal_id = filter.connect_changed(clone!( #[weak(rename_to = obj)] self, move |_, change| { obj.changed(change); } )); let prev_value = imp .filters .borrow_mut() .insert(id.into(), (filter.as_ref().clone(), signal_id)); let had_prev_value = prev_value.is_some(); if let Some((prev_filter, prev_signal_id)) = prev_value { prev_filter.disconnect(prev_signal_id); self.changed(gtk::FilterChange::Different); } else { self.changed(gtk::FilterChange::MoreStrict); } !had_prev_value } pub fn remove(&self, id: &str) -> bool { let imp = self.imp(); let prev_value = imp.filters.borrow_mut().remove(id); let had_prev_value = prev_value.is_some(); if let Some((_, signal_id)) = prev_value { self.disconnect(signal_id); self.changed(gtk::FilterChange::LessStrict); } had_prev_value } pub fn clear(&self) { let imp = self.imp(); for (_, (filter, signal_id)) in imp.filters.take() { filter.disconnect(signal_id); } self.changed(gtk::FilterChange::LessStrict); } } impl Default for EveryFilter { fn default() -> Self { glib::Object::new() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filter_pane/000077500000000000000000000000001506133026500236025ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filter_pane/bus_name_row.rs000066400000000000000000000067451506133026500266440ustar00rootroot00000000000000use gtk::{ glib::{self, clone}, prelude::*, subclass::prelude::*, }; use crate::bus_name_item::BusNameItem; mod imp { use std::{cell::OnceCell, marker::PhantomData}; use super::*; use crate::bus_name_item::LookupPoint; #[derive(Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::BusNameRow)] #[template(resource = "/org/freedesktop/Bustle/ui/filter_pane_bus_name_row.ui")] pub struct BusNameRow { #[property(get, set, construct_only)] pub(super) bus_name_item: OnceCell, #[property(get = Self::is_active)] pub(super) is_active: PhantomData, #[template_child] pub(super) title: TemplateChild, #[template_child] pub(super) subtitle: TemplateChild, #[template_child] pub(super) check_button: TemplateChild, pub(super) check_button_active_notify_handler_id: OnceCell, } #[glib::object_subclass] impl ObjectSubclass for BusNameRow { const NAME: &'static str = "BustleFilterPaneBusNameRow"; type Type = super::BusNameRow; type ParentType = gtk::ListBoxRow; fn class_init(klass: &mut Self::Class) { klass.bind_template(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for BusNameRow { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let bus_name_item = obj.bus_name_item(); self.title.set_label(bus_name_item.name()); let subtitle_text = bus_name_item .wk_names(LookupPoint::Last) .iter() .filter_map(|n| n.split('.').next_back()) .collect::>() .join(", "); self.subtitle.set_label(&subtitle_text); self.subtitle.set_visible(!subtitle_text.is_empty()); let handler_id = self.check_button.connect_active_notify(clone!( #[weak] obj, move |_| { obj.notify_is_active(); } )); self.check_button_active_notify_handler_id .set(handler_id) .unwrap(); } } impl WidgetImpl for BusNameRow {} impl ListBoxRowImpl for BusNameRow {} impl BusNameRow { fn is_active(&self) -> bool { self.check_button.is_active() } } } glib::wrapper! { pub struct BusNameRow(ObjectSubclass) @extends gtk::Widget, gtk::ListBoxRow, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible, gtk::Actionable; } impl BusNameRow { pub fn new(bus_name_item: &BusNameItem) -> Self { glib::Object::builder() .property("bus-name-item", bus_name_item) .build() } pub fn handle_activation(&self) { let was_activated = self.imp().check_button.activate(); debug_assert!(was_activated); } pub fn set_selected_no_notify(&self, is_selected: bool) { let imp = self.imp(); let handler_id = imp.check_button_active_notify_handler_id.get().unwrap(); imp.check_button.block_signal(handler_id); imp.check_button.set_active(is_selected); imp.check_button.unblock_signal(handler_id); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filter_pane/message_tag_row.rs000066400000000000000000000063241506133026500273230ustar00rootroot00000000000000use gtk::{ glib::{self, clone}, prelude::*, subclass::prelude::*, }; use crate::message_tag::MessageTag; mod imp { use std::{cell::OnceCell, marker::PhantomData}; use super::*; use crate::color_widget::ColorWidget; #[derive(Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::MessageTagRow)] #[template(resource = "/org/freedesktop/Bustle/ui/filter_pane_message_tag_row.ui")] pub struct MessageTagRow { #[property(get, set, construct_only, default)] pub(super) message_tag: OnceCell, #[property(get = Self::is_active)] pub(super) is_active: PhantomData, #[template_child] pub(super) color: TemplateChild, #[template_child] pub(super) title: TemplateChild, #[template_child] pub(super) check_button: TemplateChild, pub(super) check_button_active_notify_handler_id: OnceCell, } #[glib::object_subclass] impl ObjectSubclass for MessageTagRow { const NAME: &'static str = "BustleFilterPaneMessageTagRow"; type Type = super::MessageTagRow; type ParentType = gtk::ListBoxRow; fn class_init(klass: &mut Self::Class) { klass.bind_template(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for MessageTagRow { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let message_tag = obj.message_tag(); self.title.set_label(&message_tag.name()); self.color.set_rgba(message_tag.color()); let handler_id = self.check_button.connect_active_notify(clone!( #[weak] obj, move |_| { obj.notify_is_active(); } )); self.check_button_active_notify_handler_id .set(handler_id) .unwrap(); } } impl WidgetImpl for MessageTagRow {} impl ListBoxRowImpl for MessageTagRow {} impl MessageTagRow { fn is_active(&self) -> bool { self.check_button.is_active() } } } glib::wrapper! { pub struct MessageTagRow(ObjectSubclass) @extends gtk::Widget, gtk::ListBoxRow, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible, gtk::Actionable; } impl MessageTagRow { pub fn new(bus_name_item: &MessageTag) -> Self { glib::Object::builder() .property("message-tag", bus_name_item) .build() } pub fn handle_activation(&self) { let was_activated = self.imp().check_button.activate(); debug_assert!(was_activated); } pub fn set_selected_no_notify(&self, is_selected: bool) { let imp = self.imp(); let handler_id = imp.check_button_active_notify_handler_id.get().unwrap(); imp.check_button.block_signal(handler_id); imp.check_button.set_active(is_selected); imp.check_button.unblock_signal(handler_id); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filter_pane/mod.rs000066400000000000000000000211611506133026500247300ustar00rootroot00000000000000mod bus_name_row; mod message_tag_row; use gtk::{ glib::{self, clone, translate::FromGlib}, prelude::*, subclass::prelude::*, }; use crate::{ bus_name_item::BusNameItem, bus_name_list::BusNameList, filter_pane::{bus_name_row::BusNameRow, message_tag_row::MessageTagRow}, filtered_message_model::FilteredMessageModel, message_tag::MessageTag, }; mod imp { use std::cell::{OnceCell, RefCell}; use super::*; #[derive(Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::FilterPane)] #[template(resource = "/org/freedesktop/Bustle/ui/filter_pane.ui")] pub struct FilterPane { #[property(get, set = Self::set_model)] pub(super) model: OnceCell, #[template_child] pub(super) toolbar_view: TemplateChild, /* Unused, but needed for * disposal */ #[template_child] pub(super) message_tag_list_box: TemplateChild, #[template_child] pub(super) bus_name_list_box: TemplateChild, pub(super) message_tag_model: RefCell>, pub(super) bus_name_model: RefCell>, } #[glib::object_subclass] impl ObjectSubclass for FilterPane { const NAME: &'static str = "BustleFilterPane"; type Type = super::FilterPane; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); klass.set_layout_manager_type::(); klass.install_action( "filter-pane.unselect-all-message-tags", None, |obj, _, _| { obj.message_tag_rows_set_selected_no_notify(false); if let Some(model) = obj.imp().message_tag_model.borrow().as_ref() { for item in model.iter::() { let enum_list_item = item.unwrap(); let message_tag = unsafe { MessageTag::from_glib(enum_list_item.value()) }; obj.model().add_message_tag_filter(message_tag); } } }, ); klass.install_action("filter-pane.unselect-all-bus-names", None, |obj, _, _| { obj.bus_name_rows_set_selected_no_notify(false); if let Some(model) = obj.imp().bus_name_model.borrow().as_ref() { for item in model.iter::() { let bus_name_item = item.unwrap(); obj.model() .add_bus_name_filter(bus_name_item.name()) .unwrap(); } } }); klass.install_action("filter-pane.select-all-message-tags", None, |obj, _, _| { obj.message_tag_rows_set_selected_no_notify(true); obj.model().remove_all_message_tag_filters(); }); klass.install_action("filter-pane.select-all-bus-names", None, |obj, _, _| { obj.bus_name_rows_set_selected_no_notify(true); obj.model().remove_all_bus_name_filters(); }); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for FilterPane { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let message_tag_model = adw::EnumListModel::new(MessageTag::static_type()); self.message_tag_list_box.bind_model( Some(&message_tag_model), clone!( #[weak] obj, #[upgrade_or_panic] move |item| { let enum_list_item = item.downcast_ref::().unwrap(); let message_tag = unsafe { MessageTag::from_glib(enum_list_item.value()) }; obj.create_message_tag_row(message_tag).upcast() } ), ); self.message_tag_model.replace(Some(message_tag_model)); } fn dispose(&self) { self.dispose_template(); } } impl WidgetImpl for FilterPane {} impl FilterPane { fn set_model(&self, model: FilteredMessageModel) { let obj = self.obj(); self.bus_name_model.replace(None); model .filtered_bus_names() .connect_bus_name_list_notify(clone!( #[weak] obj, move |filtered_bus_names| { let imp = obj.imp(); obj.message_tag_rows_set_selected_no_notify(true); obj.bus_name_rows_set_selected_no_notify(true); let bus_name_model = filtered_bus_names.bus_name_list(); imp.bus_name_list_box.bind_model( bus_name_model.as_ref(), clone!( #[weak] obj, #[upgrade_or_panic] move |item| { let bus_name_item = item.downcast_ref().unwrap(); obj.create_bus_name_row(bus_name_item).upcast() } ), ); imp.bus_name_model.replace(bus_name_model); } )); self.model.set(model).unwrap(); } } } glib::wrapper! { pub struct FilterPane(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl FilterPane { fn create_message_tag_row(&self, message_tag: MessageTag) -> MessageTagRow { let row = MessageTagRow::new(&message_tag); row.connect_is_active_notify(clone!( #[weak(rename_to = obj)] self, move |row| { let model = obj.model(); let message_tag = row.message_tag(); if row.is_active() { model.remove_message_tag_filter(message_tag); } else { model.add_message_tag_filter(message_tag); } } )); row } fn create_bus_name_row(&self, bus_name_item: &BusNameItem) -> BusNameRow { let row = BusNameRow::new(bus_name_item); row.connect_is_active_notify(clone!( #[weak(rename_to = obj)] self, move |row| { let model = obj.model(); let bus_name_item = row.bus_name_item(); let name = bus_name_item.name(); if row.is_active() { model.remove_bus_name_filter(name); } else { model.add_bus_name_filter(name).unwrap(); } } )); row } fn message_tag_rows_set_selected_no_notify(&self, is_selected: bool) { let imp = self.imp(); let mut curr = imp.message_tag_list_box.first_child(); while let Some(child) = &curr { let message_tag_row = child.downcast_ref::().unwrap(); message_tag_row.set_selected_no_notify(is_selected); curr = child.next_sibling(); } } fn bus_name_rows_set_selected_no_notify(&self, is_selected: bool) { let imp = self.imp(); let mut curr = imp.bus_name_list_box.first_child(); while let Some(child) = &curr { let bus_name_row = child.downcast_ref::().unwrap(); bus_name_row.set_selected_no_notify(is_selected); curr = child.next_sibling(); } } } #[gtk::template_callbacks] impl FilterPane { #[template_callback] fn message_tag_list_box_row_activated(&self, row: >k::ListBoxRow) { let message_tag_row = row.downcast_ref::().unwrap(); message_tag_row.handle_activation(); } #[template_callback] fn bus_name_list_box_row_activated(&self, row: >k::ListBoxRow) { let bus_name_row = row.downcast_ref::().unwrap(); bus_name_row.handle_activation(); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filtered_bus_name_model.rs000066400000000000000000000075401506133026500265150ustar00rootroot00000000000000use gtk::{ gio, glib::{self, clone}, prelude::*, subclass::prelude::*, }; use zbus::names::{BusName, WellKnownName}; use crate::{ bus_name_item::{BusNameItem, LookupPoint}, bus_name_list::BusNameList, }; mod imp { use std::marker::PhantomData; use super::*; #[derive(Default, glib::Properties)] #[properties(wrapper_type = super::FilteredBusNameModel)] pub struct FilteredBusNameModel { #[property(get = Self::bus_name_list, set = Self::set_bus_name_list, explicit_notify, nullable)] pub(super) bus_name_list: PhantomData>, pub(super) inner: gtk::FilterListModel, } #[glib::object_subclass] impl ObjectSubclass for FilteredBusNameModel { const NAME: &'static str = "BustleFilteredBusNameModel"; type Type = super::FilteredBusNameModel; type Interfaces = (gio::ListModel,); } #[glib::derived_properties] impl ObjectImpl for FilteredBusNameModel { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); self.inner.connect_items_changed(clone!( #[weak] obj, move |_, position, removed, added| { obj.items_changed(position, removed, added); } )); } } impl ListModelImpl for FilteredBusNameModel { fn item_type(&self) -> glib::Type { BusNameItem::static_type() } fn n_items(&self) -> u32 { self.inner.n_items() } fn item(&self, position: u32) -> Option { self.inner.item(position) } } impl FilteredBusNameModel { fn set_bus_name_list(&self, bus_name_list: Option<&BusNameList>) { let obj = self.obj(); if bus_name_list == obj.bus_name_list().as_ref() { return; } self.inner.set_model(bus_name_list); obj.notify_bus_name_list(); } fn bus_name_list(&self) -> Option { self.inner.model().map(|model| model.downcast().unwrap()) } } } glib::wrapper! { pub struct FilteredBusNameModel(ObjectSubclass) @implements gio::ListModel; } impl FilteredBusNameModel { /// This must only be called within `FilteredMessageModel` pub fn set_filter(&self, filter: Option<&impl IsA>) { self.imp().inner.set_filter(filter); } pub fn iter(&self) -> impl Iterator + '_ { ListModelExtManual::iter(self).map(|item| item.unwrap()) } // TODO the following could be optimize if we don't rely on GTK's // `FilterListModel` /// Returns the `BusNameItem` with the given `bus_name` as item's `name` pub fn get(&self, bus_name: &BusName<'_>) -> Option { self.iter() .find(|bus_name_item| *bus_name_item.name() == *bus_name) } /// Returns the index of `BusNameItem` with the given `bus_name` as item's /// `name` pub fn get_index_of(&self, bus_name: &BusName<'_>) -> Option { self.iter() .position(|bus_name_item| *bus_name_item.name() == *bus_name) } /// Returns the index of `BusNameItem` containing the `wk_name` at the given /// lookup point. pub fn get_index_of_wk_name( &self, wk_name: &WellKnownName<'_>, lookup_point: LookupPoint, ) -> Option { debug_assert_eq!( self.get(&BusName::from(wk_name.as_ref())), None, "`get` or `get_index_of` must be used first" ); self.iter() .position(|bus_name_item| bus_name_item.wk_names(lookup_point).contains(wk_name)) } } impl Default for FilteredBusNameModel { fn default() -> Self { glib::Object::new() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/filtered_message_model.rs000066400000000000000000000245301506133026500263460ustar00rootroot00000000000000use anyhow::{Context, Result}; use gtk::{ gio, glib::{self, clone}, prelude::*, subclass::prelude::*, }; use zbus::{fdo::DBusProxy, names::BusName, proxy::Defaults}; use crate::{ bus_name_item::{BusNameItem, LookupPoint}, every_filter::EveryFilter, filtered_bus_name_model::FilteredBusNameModel, message::Message, message_list::MessageList, message_tag::MessageTag, multi_set::MultiSet, }; mod imp { use std::cell::RefCell; use indexmap::IndexSet; use super::*; #[derive(Default)] pub struct FilteredMessageModel { pub(super) inner: gtk::FilterListModel, pub(super) inner_index: RefCell>, pub(super) filtered_bus_names: FilteredBusNameModel, pub(super) used_bus_names: RefCell>>, pub(super) message_tag_filter: EveryFilter, pub(super) bus_name_filter: EveryFilter, } #[glib::object_subclass] impl ObjectSubclass for FilteredMessageModel { const NAME: &'static str = "BustleFilteredMessageModel"; type Type = super::FilteredMessageModel; type Interfaces = (gio::ListModel,); } impl ObjectImpl for FilteredMessageModel { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let bus_names_filter = gtk::CustomFilter::new(clone!( #[weak] obj, #[upgrade_or_panic] move |bus_name_item| { let bus_name_item = bus_name_item.downcast_ref::().unwrap(); obj.bus_names_filter_func(bus_name_item) } )); self.inner.connect_items_changed(clone!( #[weak] obj, #[weak] bus_names_filter, move |_, position, removed, added| { obj.update_inner_index(position, removed, added); bus_names_filter.changed(gtk::FilterChange::Different); obj.items_changed(position, removed, added); } )); self.filtered_bus_names.set_filter(Some(&bus_names_filter)); let filter = gtk::EveryFilter::new(); filter.append(gtk::CustomFilter::new(|message| { let message = message.downcast_ref::().unwrap(); // TODO: define the exact rules and document why are these filtered out &message.destination() != DBusProxy::DESTINATION && message.sender().as_deref() != DBusProxy::DESTINATION.as_deref() })); filter.append(self.message_tag_filter.clone()); filter.append(self.bus_name_filter.clone()); self.inner.set_filter(Some(&filter)); } } impl ListModelImpl for FilteredMessageModel { fn item_type(&self) -> glib::Type { Message::static_type() } fn n_items(&self) -> u32 { self.inner.n_items() } fn item(&self, position: u32) -> Option { self.inner.item(position) } } } glib::wrapper! { pub struct FilteredMessageModel(ObjectSubclass) @implements gio::ListModel; } impl FilteredMessageModel { /// This also resets the filters pub fn set_message_list(&self, message_list: Option<&MessageList>) { self.remove_all_bus_name_filters(); self.remove_all_message_tag_filters(); let imp = self.imp(); // Must set the model for `inner` first to have used names updated imp.inner.set_model(message_list); imp.filtered_bus_names .set_bus_name_list(message_list.map(|l| l.bus_names())); } pub fn message_list(&self) -> Option { self.imp() .inner .model() .map(|model| model.downcast().unwrap()) } pub fn get_index_of(&self, message: &Message) -> Option { self.imp().inner_index.borrow().get_index_of(message) } pub fn iter(&self) -> impl ExactSizeIterator + DoubleEndedIterator + '_ { ListModelExtManual::iter(self).map(|item| item.unwrap()) } /// Returns the filtered bus names from self's message list with respect to /// self's filters pub fn filtered_bus_names(&self) -> &FilteredBusNameModel { &self.imp().filtered_bus_names } /// Adds filter that filters out messages with the given tag pub fn add_message_tag_filter(&self, message_tag: MessageTag) { let imp = self.imp(); let filter_id = message_tag.to_string(); if imp.message_tag_filter.contains(&filter_id) { return; } let custom_filter = gtk::CustomFilter::new(move |message| { let message = message.downcast_ref::().unwrap(); message.message_tag() != message_tag }); let is_unique = imp.message_tag_filter.insert(filter_id, custom_filter); debug_assert!(is_unique); } /// Removes the filter that filters out messages with the given tag pub fn remove_message_tag_filter(&self, message_tag: MessageTag) { let imp = self.imp(); let filter_id = message_tag.to_string(); let was_removed = imp.message_tag_filter.remove(&filter_id); debug_assert!(was_removed); } pub fn remove_all_message_tag_filters(&self) { let imp = self.imp(); imp.message_tag_filter.clear(); } /// Adds filter that filters out messages relevant to `BusNameItem` with /// `bus_name` equals given `name` /// /// Returns an error if self has no `message_list` pub fn add_bus_name_filter(&self, name: &BusName<'_>) -> Result<()> { let imp = self.imp(); if imp.bus_name_filter.contains(name) { return Ok(()); } let bus_name_item = self .message_list() .context("Message list was not set")? .bus_names() .get(name) .unwrap(); let custom_filter = gtk::CustomFilter::new(move |message| { let message = message.downcast_ref::().unwrap(); let name = bus_name_item.name(); message.sender().is_none_or(|sender| *name != sender) && !message.destination().is_some_and(|destination| { *name == destination || match destination { BusName::Unique(_) => false, BusName::WellKnown(wk_name) => bus_name_item .wk_names(message.receive_index().into()) .contains(&wk_name), } }) }); let is_unique = imp.bus_name_filter.insert(name.to_string(), custom_filter); debug_assert!(is_unique); Ok(()) } /// Removes the filter that is relevant to `BusNameItem` with `bus_name` /// equals given `name` /// /// Returns true if the filter existed and removed pub fn remove_bus_name_filter(&self, name: &BusName<'static>) { let imp = self.imp(); let was_removed = imp.bus_name_filter.remove(name); debug_assert!(was_removed); } pub fn remove_all_bus_name_filters(&self) { let imp = self.imp(); imp.bus_name_filter.clear(); } fn bus_names_filter_func(&self, bus_name_item: &BusNameItem) -> bool { let used_names = self.imp().used_bus_names.borrow(); used_names.contains(bus_name_item.name()) || bus_name_item .wk_names(LookupPoint::All) .iter() .any(|wk_name| used_names.contains(&BusName::from(wk_name.as_ref()))) } fn update_inner_index(&self, position: u32, removed: u32, added: u32) { let imp = self.imp(); let mut inner_index = imp.inner_index.borrow_mut(); let mut used_bus_names = imp.used_bus_names.borrow_mut(); let added_items = (0..added) .map(|i| { imp.inner .item(position + i) .unwrap() .downcast::() .unwrap() }) .collect::>(); let used_sender_names = added_items .iter() .filter_map(|message| message.sender().map(|s| BusName::from(s.to_owned()))); let used_destination_names = added_items .iter() .filter_map(|message| message.destination().map(|d| d.to_owned())); used_bus_names.extend(used_sender_names.chain(used_destination_names)); let removed_items = inner_index .splice( position as usize..(removed + position) as usize, added_items, ) .collect::>(); let removed_sender_names = removed_items .iter() .filter_map(|message| message.sender().map(|s| BusName::from(s.to_owned()))); let removed_destination_names = removed_items .iter() .filter_map(|message| message.destination().map(|d| d.to_owned())); removed_sender_names .chain(removed_destination_names) .for_each(|bus_name| { used_bus_names.remove(bus_name); }); // Ensure that `inner_index` items are exactly the same as the items in `inner` debug_assert_eq!(inner_index.len(), imp.inner.n_items() as usize); debug_assert!( self.iter() .zip(inner_index.iter()) .all(|(inner_item, inner_index_item)| inner_item == *inner_index_item) ); // Ensure that all bus names in `inner_index` are in `used_bus_names`. debug_assert!(self.iter().all(|message| { [message.sender().map(BusName::from), message.destination()] .iter() .flatten() .all(|bus_name| used_bus_names.contains(bus_name)) })); // Ensure that only bus names in `inner_index` are in `used_bus_names`. Note: // This assertion is very expensive. debug_assert!(used_bus_names.iter_unique().all(|bus_name| { self.iter().any(|message| { message.sender().is_some_and(|s| *bus_name == s) || message.destination().is_some_and(|d| *bus_name == d) }) })); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/i18n.rs000066400000000000000000000110521506133026500224360ustar00rootroot00000000000000// Copied from Fractal GPLv3 // See https://gitlab.gnome.org/GNOME/fractal/-/blob/c0bc4078bb2cdd511c89fdf41a51275db90bb7ab/src/i18n.rs use gettextrs::{gettext, ngettext}; /// Like `gettext`, but replaces named variables using the given key-value /// tuples. /// /// The expected format to replace is `{name}`, where `name` is the first string /// in a key-value tuple. pub fn gettext_f(msgid: &str, args: &[(&str, &str)]) -> String { let s = gettext(msgid); freplace(s, args) } /// Like `ngettext`, but replaces named variables using the given key-value /// tuples. /// /// The expected format to replace is `{name}`, where `name` is the first string /// in a key-value tuple. pub fn ngettext_f(msgid: &str, msgid_plural: &str, n: u32, args: &[(&str, &str)]) -> String { let s = ngettext(msgid, msgid_plural, n); freplace(s, args) } /// Replace variables in the given string using the given key-value tuples. /// /// The expected format to replace is `{name}`, where `name` is the first string /// in a key-value tuple. fn freplace(s: String, args: &[(&str, &str)]) -> String { // This function is useless if there are no arguments debug_assert!( !args.is_empty(), "at least one key-value pair must be given" ); // We could check here if all keys were used, but some translations might // not use all variables, so we don't do that. let mut s = s; for (key, val) in args { s = s.replace(&format!("{{{key}}}"), val); } debug_assert!(!s.contains('{'), "all format variables must be replaced"); if tracing::enabled!(tracing::Level::WARN) && s.contains('{') { tracing::warn!( "all format variables must be replaced, but some were not: {}", s ); } s } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic = "at least one key-value pair must be given"] fn freplace_no_args() { gettext_f("no args", &[]); } #[test] #[should_panic = "all format variables must be replaced"] fn freplace_missing_key() { gettext_f("missing {one}", &[("two", "2")]); } #[test] fn gettext_f_simple() { assert_eq!(gettext_f("no replace", &[("one", "1")]), "no replace"); assert_eq!(gettext_f("{one} param", &[("one", "1")]), "1 param"); assert_eq!( gettext_f("middle {one} param", &[("one", "1")]), "middle 1 param" ); assert_eq!(gettext_f("end {one}", &[("one", "1")]), "end 1"); } #[test] fn gettext_f_multiple() { assert_eq!( gettext_f("multiple {one} and {two}", &[("one", "1"), ("two", "2")]), "multiple 1 and 2" ); assert_eq!( gettext_f("multiple {two} and {one}", &[("one", "1"), ("two", "2")]), "multiple 2 and 1" ); assert_eq!( gettext_f("multiple {one} and {one}", &[("one", "1"), ("two", "2")]), "multiple 1 and 1" ); } #[test] fn ngettext_f_multiple() { assert_eq!( ngettext_f( "singular {one} and {two}", "plural {one} and {two}", 1, &[("one", "1"), ("two", "two")], ), "singular 1 and two" ); assert_eq!( ngettext_f( "singular {one} and {two}", "plural {one} and {two}", 2, &[("one", "1"), ("two", "two")], ), "plural 1 and two" ); } #[test] fn ngettext_f_unused_on_singular() { assert_eq!( ngettext_f( "singular {one}", "plural {one} and {two}", 1, &[("one", "1"), ("two", "2")], ), "singular 1" ); assert_eq!( ngettext_f( "singular {one}", "plural {one} and {two}", 2, &[("one", "1"), ("two", "2")], ), "plural 1 and 2" ); } #[test] fn ngettext_f_unused_on_plural() { assert_eq!( ngettext_f( "singular {one} and {two}", "plural {one}", 1, &[("one", "1"), ("two", "2")], ), "singular 1 and 2" ); assert_eq!( ngettext_f( "singular {one} and {two}", "plural {one}", 2, &[("one", "1"), ("two", "2")], ), "plural 1" ); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/main.rs000066400000000000000000000026731506133026500226140ustar00rootroot00000000000000#![warn(rust_2018_idioms)] mod address_dialog; mod application; #[rustfmt::skip] mod config; mod bus_name_item; mod bus_name_list; mod busctl_cmd; mod color_widget; mod colors; mod details_view; mod diagram; mod every_filter; mod filter_pane; mod filtered_bus_name_model; mod filtered_message_model; mod i18n; mod message; mod message_fmt; mod message_list; mod message_tag; mod message_type; mod monitor; mod multi_set; mod pcap_reader; mod statistics; mod timestamp; mod window; use std::sync::LazyLock; use gettextrs::LocaleCategory; use gtk::{gio, glib}; pub static RUNTIME: LazyLock = LazyLock::new(|| tokio::runtime::Runtime::new().unwrap()); use self::{ application::Application, config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE}, }; #[derive(Debug, Clone)] pub enum ConnectionType { Session, System, Address(String), } fn main() -> glib::ExitCode { // Initialize logger tracing_subscriber::fmt::init(); // Prepare i18n gettextrs::setlocale(LocaleCategory::LcAll, ""); gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain"); gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain"); glib::set_application_name("Bustle"); let res = gio::Resource::load(RESOURCES_FILE).expect("Could not load gresource file"); gio::resources_register(&res); let app = Application::default(); app.run() } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/meson.build000066400000000000000000000026541506133026500234630ustar00rootroot00000000000000global_conf = configuration_data() global_conf.set_quoted('APP_ID', application_id) global_conf.set_quoted('PKGDATADIR', pkgdatadir) global_conf.set_quoted('PROFILE', profile) global_conf.set_quoted('VERSION', version + version_suffix) global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) global_conf.set_quoted('LOCALEDIR', localedir) config = configure_file( input: 'config.rs.in', output: 'config.rs', configuration: global_conf ) # Copy the config.rs output to the source directory. run_command( 'cp', meson.project_build_root() / 'src' / 'config.rs', meson.project_source_root() / 'src' / 'config.rs', check: true ) cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ] cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ] if get_option('profile') == 'default' cargo_options += [ '--release' ] rust_target = 'release' message('Building in release mode') else rust_target = 'debug' message('Building in debug mode') endif cargo_env = [ 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home' ] cargo_build = custom_target( 'cargo-build', build_by_default: true, build_always_stale: true, output: meson.project_name(), console: true, install: true, install_dir: bindir, depends: resources, command: [ 'env', cargo_env, cargo, 'build', cargo_options, '&&', 'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@', ] ) bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/message.rs000066400000000000000000000324301506133026500233060ustar00rootroot00000000000000use gettextrs::gettext; use gtk::{glib, prelude::*, subclass::prelude::*}; use zbus::{ names::{BusName, UniqueName, WellKnownName}, zvariant, }; use crate::{ ConnectionType, message_tag::MessageTag, message_type::MessageType, monitor::Event, timestamp::Timestamp, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ReceiveIndex(u32); mod imp { use std::cell::{Cell, OnceCell}; use super::*; #[derive(Debug, Default, glib::Properties)] #[properties(wrapper_type = super::Message)] pub struct Message { #[property(get, set, construct_only, default)] pub(super) message_type: Cell, #[property(get, set, construct_only)] pub(super) timestamp: OnceCell, #[property(get, set, default)] pub(super) message_tag: Cell, pub(super) inner: OnceCell, pub(super) receive_index: OnceCell, pub(super) associated_message: OnceCell>, } #[glib::object_subclass] impl ObjectSubclass for Message { const NAME: &'static str = "BustleMessage"; type Type = super::Message; } #[glib::derived_properties] impl ObjectImpl for Message {} } glib::wrapper! { pub struct Message(ObjectSubclass); } impl Message { pub fn from_event(event: Event) -> Self { let this = glib::Object::builder::() .property( "message-type", MessageType::from(event.message.message_type()), ) .property("timestamp", event.timestamp) .build(); this.imp().inner.set(event.message).unwrap(); this } pub fn to_event(&self) -> Event { Event { message: self.imp().inner.get().unwrap().clone(), timestamp: self.timestamp(), } } fn inner(&self) -> &zbus::Message { self.imp().inner.get().unwrap() } /// Return the length of the whole message pub fn len(&self) -> usize { self.inner().data().len() } /// Return the message body pub fn body(&self) -> zbus::message::Body { self.inner().body() } /// Return the message header pub fn header(&self) -> zbus::message::Header<'_> { self.inner().header() } /// Return the time it took to receive a response or None if it is a signal /// or no message is associated to the current one pub fn response_time(&self) -> Option { let associated_message = self.associated_message()?; match self.message_type() { MessageType::Signal => None, MessageType::MethodReturn | MessageType::Error => { Some(self.timestamp() - associated_message.timestamp()) } MessageType::MethodCall => Some(associated_message.timestamp() - self.timestamp()), } } pub fn set_receive_index(&self, raw_receive_index: u32) { self.imp() .receive_index .set(ReceiveIndex(raw_receive_index)) .expect("receive index must only be set once"); } pub fn receive_index(&self) -> ReceiveIndex { *self.imp().receive_index.get().unwrap() } pub fn set_associated_message(&self, message: &Self) { if self .imp() .associated_message .set(message.downgrade()) .is_err() { // Method call can somehow be associated with multiple replies, so simply ignore // the subsequent ones. if self .associated_message() .is_some_and(|curr| curr.is_return_of(self) && message.is_return_of(self)) { tracing::debug!("Associated message was already set to the same message"); } else { unreachable!("associated message must only be set once"); } } } pub fn associated_message(&self) -> Option { self.imp() .associated_message .get() .and_then(|m| m.upgrade()) } pub fn sender_display(&self) -> String { if self.message_type().is_method_return() { self.associated_message() .as_ref() .and_then(|m| m.destination()) .map(|d| d.to_string()) } else { self.sender().map(|m| m.to_string()) } .unwrap_or_else(|| "(no sender)".to_owned()) } pub fn member_display(&self) -> String { if self.message_type().is_method_return() { self.associated_message() .as_ref() .map(|m| m.header()) .and_then(|h| h.member().map(|m| m.to_string())) } else { self.header().member().map(|m| m.to_string()) } .unwrap_or_else(|| "(no member)".to_owned()) } pub fn interface_display(&self) -> String { if self.message_type().is_method_return() { self.associated_message() .as_ref() .map(|m| m.header()) .and_then(|h| h.interface().map(|m| m.to_string())) } else { self.header().interface().map(|s| s.to_string()) } .unwrap_or_else(|| "(no interface)".to_owned()) } pub fn destination_display(&self) -> String { if self.message_type().is_method_return() { self.associated_message() .as_ref() .and_then(|m| m.sender()) .map(|s| s.to_string()) } else { self.destination().map(|m| m.to_string()) } .unwrap_or_else(|| "(no destination)".to_owned()) } pub fn path_display(&self) -> String { if self.message_type().is_method_return() { self.associated_message() .as_ref() .and_then(|m| m.header().path().map(|p| p.to_string())) } else { self.header().path().map(|p| p.to_string()) } .unwrap_or_else(|| "(no path)".to_owned()) } pub fn flags_display(&self) -> String { self.header() .primary() .flags() .iter() .map(|flag| match flag { zbus::message::Flags::NoReplyExpected => gettext("No Reply Expected"), zbus::message::Flags::NoAutoStart => gettext("No Auto Start"), zbus::message::Flags::AllowInteractiveAuth => { gettext("Allow Interactive Authentication") } }) .collect::>() .join(" | ") } pub fn member_markup(&self, render_error: bool) -> String { let interface = self.interface_display(); let member = self.member_display(); if self.message_type() == MessageType::Error && render_error { // This is the .error class at // https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/css-variables.html#error-colors. let foreground = if adw::StyleManager::default().is_dark() { "#ff938c" } else { "#c30000" }; format!( "{}.{}", glib::markup_escape_text(&interface), glib::markup_escape_text(&member) ) } else { format!( "{}.{}", glib::markup_escape_text(&interface), glib::markup_escape_text(&member) ) } } pub fn sender(&self) -> Option> { self.header().sender().cloned() } pub fn destination(&self) -> Option> { self.header().destination().cloned().or_else(|| { match self.message_type() { MessageType::MethodCall => Some("method.call.destination"), MessageType::MethodReturn => Some("method.return.destination"), MessageType::Error => Some("method.error.destination"), _ => None, } .map(|raw_name| { let ret = BusName::WellKnown(WellKnownName::from_static_str(raw_name).unwrap()); debug_assert!(Self::is_fallback_destination(&ret)); ret }) }) } /// Whether `name` is used as a fallback destination internally pub fn is_fallback_destination(name: &BusName<'_>) -> bool { match name { BusName::Unique(_) => false, BusName::WellKnown(wk_name) => matches!( wk_name.as_str(), "method.call.destination" | "method.return.destination" | "method.error.destination" ), } } /// Whether this is the return/error message of `other` pub fn is_return_of(&self, other: &Self) -> bool { debug_assert!(self.message_type().is_method_return()); other.message_type().is_method_call() && self.header().reply_serial() == Some(other.header().primary().serial_num()) && self.destination() == other.sender().map(From::from) } pub fn print_as_busctl( &self, connection_type: Option, ) -> anyhow::Result { use crate::busctl_cmd::{BusctlCommand, BusctlSubCommand}; let sub_command = match self.message_type() { MessageType::Signal => BusctlSubCommand::Wait, MessageType::MethodCall => BusctlSubCommand::Call, MessageType::MethodReturn | MessageType::Error => unimplemented!(), }; let body = self.body(); // TODO Do we need this guard? Add a test for a method without an arg. let body_structure = if self.header().signature() != &zvariant::Signature::Unit { match body.deserialize::>() { Ok(structure) => Some(structure), Err(err) => anyhow::bail!("Could not deserialize structure: {err}"), } } else { None }; let header = self.header(); let args = BusctlCommand { // NOTE: When reading a cap file there is no way to tell on which // bus we are. By default we assume it is a session connection connection_type: connection_type.unwrap_or(ConnectionType::Session), sub_command, destination: self.destination(), sender: self.sender(), path: header.path(), interface: header.interface(), member: header.member(), signature: header.signature(), body_structure, }; args.into_command_string() } } impl std::fmt::Display for Message { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Message") .field("path", &self.path_display()) .field("destination", &self.destination_display()) .field("sender", &self.sender_display()) .field("member", &self.member_display()) .field("interface", &self.interface_display()) .field("type", &self.message_type()) .field("associated_message", &self.associated_message()) .finish() } } #[cfg(test)] mod tests { use base64::prelude::*; use super::*; #[test] fn test_print_portal_call_as_busctl() { // This is a message captured with wireshark. The window handled comes // from test_shell_escape. let expected_cmd = "busctl --user call org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop org.freedesktop.portal.OpenURI OpenURI ssa{sv} 'wayland:1zb*ehkl'\\''MwqCXbcN-T=8EGr&7'\\!'zB'\\''_0' 'https://github.com/bilelmoussaoui/ashpd' 3 ask b false handle_token s ashpd_VoXzDnLLr8 writeable b false"; let message_base64 = b"bAEAAbgAAAAoAAAApwAAAAEBbwAfAAAAL29yZy9mcmVlZGVza3RvcC9wb3J0YWwvZGVza3RvcAACAXMAHgAAAG9yZy5mcmVlZGVza3RvcC5wb3J0YWwuT3BlblVSSQAAAwFzAAcAAABPcGVuVVJJAAYBcwAeAAAAb3JnLmZyZWVkZXNrdG9wLnBvcnRhbC5EZXNrdG9wAAAIAWcAB3NzYXtzdn0AAAAABwFzAAYAAAA6MS42NTcAACgAAAB3YXlsYW5kOjF6YiplaGtsJ013cUNYYmNOLVQ9OEVHciY3IXpCJ18wAAAAACcAAABodHRwczovL2dpdGh1Yi5jb20vYmlsZWxtb3Vzc2FvdWkvYXNocGQAWAAAAAwAAABoYW5kbGVfdG9rZW4AAXMAEAAAAGFzaHBkX1ZvWHpEbkxMcjgAAAAAAAAAAAkAAAB3cml0ZWFibGUAAWIAAAAAAAAAAAMAAABhc2sAAWIAAAAAAAA="; let msg = message(message_base64).unwrap(); let cmd = msg.print_as_busctl(Some(ConnectionType::Session)).unwrap(); assert_eq!(cmd, expected_cmd); } #[test] fn test_print_ping_as_busctl() { // Tests a call without an argument let expected_cmd = "busctl --user call org.freedesktop.secrets /org/freedesktop/DBus org.freedesktop.DBus.Peer Ping"; let message_base64 = b"bAEEAQAAAAACAAAAiAAAAAEBbwAVAAAAL29yZy9mcmVlZGVza3RvcC9EQnVzAAAAAwFzAAQAAABQaW5nAAAAAAIBcwAZAAAAb3JnLmZyZWVkZXNrdG9wLkRCdXMuUGVlcgAAAAAAAAAGAXMAFwAAAG9yZy5mcmVlZGVza3RvcC5zZWNyZXRzAAcBcwAHAAAAOjEuMjYyMgA="; let msg = message(message_base64).unwrap(); let cmd = msg.print_as_busctl(Some(ConnectionType::Session)).unwrap(); assert_eq!(cmd, expected_cmd); } fn message(message_base64: &[u8]) -> anyhow::Result { let message_bytes = BASE64_STANDARD.decode(message_base64)?; let event = Event::from_bytes(message_bytes, Timestamp::now())?; Ok(Message::from_event(event)) } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/message_fmt.rs000066400000000000000000000206201506133026500241520ustar00rootroot00000000000000use std::{borrow::Cow, collections::btree_map::BTreeMap, fmt::Write}; use zbus::zvariant; pub fn structure_display_fmt( f: &mut W, structure: &zvariant::Structure<'_>, ) -> std::fmt::Result { let fields = structure.fields(); let len = fields.len(); match len { 0 => {} 1 => { value_display_fmt(f, &fields[0])?; } _ => { for (i, field) in fields.iter().enumerate() { value_display_fmt(f, field)?; if i + 1 < fields.len() { f.write_char(' ')?; } } } } Ok(()) } /// Implemented based on: /// - https://gitlab.gnome.org/GNOME/glib/-/blob/e1d47f0b0d0893ac9171e24cc7bf635495376546/glib/gvariant.c#L2213 /// - https://github.com/dbus2/zbus/blob/22182f00da8d7d29508aa9db58fd90fd583bd56f/zvariant/src/value.rs#L479 pub(crate) fn value_display_fmt( f: &mut W, value: &zvariant::Value<'_>, ) -> std::fmt::Result { match value { zvariant::Value::U8(num) => { write!(f, "0x{num:02x}") } zvariant::Value::Bool(boolean) => { write!(f, "{boolean}") } zvariant::Value::I16(num) => { write!(f, "{num}") } zvariant::Value::U16(num) => { write!(f, "{num}") } zvariant::Value::I32(num) => { // Never annotate this type because it is the default for numbers write!(f, "{num}") } zvariant::Value::U32(num) => { write!(f, "{num}") } zvariant::Value::I64(num) => { write!(f, "{num}") } zvariant::Value::U64(num) => { write!(f, "{num}") } zvariant::Value::F64(num) => { if num.fract() == 0. { // Add a dot to make it clear that this is a float write!(f, "{num}.") } else { write!(f, "{num}") } } zvariant::Value::Str(string) => { write!( f, "{}", &shell_escape::escape(Cow::Borrowed(string.as_str())) ) } zvariant::Value::Signature(val) => { write!(f, "{}", val.to_string()) } zvariant::Value::ObjectPath(val) => { write!(f, "{}", val.as_str()) } zvariant::Value::Value(child) => { f.write_str(&child.value_signature().to_string())?; f.write_char(' ')?; value_display_fmt(f, child)?; Ok(()) } zvariant::Value::Array(array) => array_display_fmt(f, array), zvariant::Value::Dict(dict) => dict_display_fmt(f, dict), zvariant::Value::Structure(structure) => structure_display_fmt(f, structure), #[cfg(unix)] zvariant::Value::Fd(handle) => { write!(f, "{handle}") } } } pub(crate) fn dict_display_fmt( f: &mut W, dict: &zvariant::Dict<'_, '_>, ) -> std::fmt::Result { let mut bmap = BTreeMap::new(); for (k, v) in dict.iter() { bmap.insert(k, v); } write!(f, "{len} ", len = bmap.len())?; if !bmap.is_empty() { // Annotate only the first entry as the rest will be of the same type. for (i, (key, value)) in bmap.iter().enumerate() { value_display_fmt(f, key)?; f.write_char(' ')?; value_display_fmt(f, value)?; if i + 1 < bmap.len() { f.write_char(' ')?; } } } Ok(()) } pub(crate) fn array_display_fmt( f: &mut W, array: &zvariant::Array<'_>, ) -> std::fmt::Result { // Print as string if it is a bytestring (i.e., first nul character is the last // byte) write!(f, "{len} ", len = array.len())?; if let [leading @ .., zvariant::Value::U8(b'\0')] = array.as_ref() { if !leading.contains(&zvariant::Value::U8(b'\0')) { let bytes = leading .iter() .map(|v| { v.downcast_ref::() .expect("item must have a signature of a byte") }) .collect::>(); let string = String::from_utf8_lossy(&bytes); write!(f, "b{:?}", string.as_ref())?; return Ok(()); } } if !array.is_empty() { // Annotate only the first item as the rest will be of the same type. for (i, item) in array.iter().enumerate() { value_display_fmt(f, item)?; if i + 1 < array.len() { f.write_char(' ')?; } } } Ok(()) } #[cfg(test)] mod tests { use std::borrow::Cow; use zvariant::{OwnedValue, Value}; use super::*; #[derive(zvariant::Type, zvariant::OwnedValue)] struct Struct { field1: Vec, field2: String, } #[test] fn test_shell_escape() { let handle = "wayland:1zb*ehkl'MwqCXbcN-T=8EGr&7!zB'_0"; let expected = "'wayland:1zb*ehkl'\\''MwqCXbcN-T=8EGr&7'\\!'zB'\\''_0'"; let escaped = shell_escape::escape(Cow::Borrowed(handle)); assert_eq!(escaped, expected); } #[test] fn test_struct_display_fmt() { let mut s = String::new(); let expected = "2 0xda 0xff 'hello world'"; let structure = Struct { field1: vec![0xda, 255], field2: "hello world".to_string(), }; let v = OwnedValue::try_from(structure).unwrap(); value_display_fmt(&mut s, &v).unwrap(); assert_eq!("(ays)", v.value_signature().to_string()); assert_eq!(s, expected); } #[test] fn test_dict_display_fmt() { #[rustfmt::skip] // From: // // busctl --user call org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service \ // SearchItems a{ss} 1 e-source-uid c4df06dec5cc2eb0e992654d1aa42cd58cf05117 let mut s = String::new(); let expected = "1 e-source-uid c4df06dec5cc2eb0e992654d1aa42cd58cf05117"; let mut dict = zvariant::Dict::new(&zvariant::Signature::Str, &zvariant::Signature::Str); dict.append( Value::Str(zvariant::Str::from_static("e-source-uid")), Value::Str(zvariant::Str::from_static( "c4df06dec5cc2eb0e992654d1aa42cd58cf05117", )), ) .unwrap(); let v = Value::Dict(dict); value_display_fmt(&mut s, &v).unwrap(); assert_eq!("a{ss}", v.value_signature().to_string()); assert_eq!(s, expected); } #[test] fn test_value_display_fmt() { // Examples from busctl(1) man page. let mut s = String::new(); let v = zvariant::Value::Str(zvariant::Str::from_static("jawoll")); value_display_fmt(&mut s, &v).unwrap(); assert_eq!("s", v.value_signature().to_string()); assert_eq!(s, "jawoll"); let mut s = String::new(); let mut arr = zvariant::Array::new(&zvariant::Signature::Str); arr.append(zvariant::Value::Str(zvariant::Str::from_static("hello"))) .unwrap(); arr.append(zvariant::Value::Str(zvariant::Str::from_static("world"))) .unwrap(); arr.append(zvariant::Value::Str(zvariant::Str::from_static("foobar"))) .unwrap(); let v = zvariant::Value::Array(arr); value_display_fmt(&mut s, &v).unwrap(); assert_eq!("as", v.value_signature().to_string()); assert_eq!(s, "3 hello world foobar"); let mut s = String::new(); let mut dict = zvariant::Dict::new(&zvariant::Signature::Str, &zvariant::Signature::Variant); dict.append( Value::Str(zvariant::Str::from_static("One")), Value::Value(Box::new(zvariant::Value::Str(zvariant::Str::from_static( "Eins", )))), ) .unwrap(); dict.append( Value::Str(zvariant::Str::from_static("Two")), Value::Value(Box::new(zvariant::Value::U32(2))), ) .unwrap(); dict.append( Value::Str(zvariant::Str::from_static("Yes")), Value::Value(Box::new(zvariant::Value::Bool(true))), ) .unwrap(); let v = zvariant::Value::Dict(dict); value_display_fmt(&mut s, &v).unwrap(); assert_eq!("a{sv}", v.value_signature().to_string()); assert_eq!(s, "3 One s Eins Two u 2 Yes b true"); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/message_list.rs000066400000000000000000000142761506133026500243510ustar00rootroot00000000000000use std::{ borrow::Cow, collections::{HashMap, hash_map::Entry}, fs::File, io::{BufWriter, Write}, path::Path, }; use anyhow::{Context, Ok, Result}; use gtk::{gio, glib, prelude::*, subclass::prelude::*}; use pcap_file::pcap::{PcapHeader, PcapPacket, PcapWriter}; use crate::{ RUNTIME, bus_name_list::BusNameList, message::Message, message_tag::MessageTag, pcap_reader::Reader, }; mod imp { use std::cell::RefCell; use super::*; #[derive(Debug, Default)] pub struct MessageList { pub(super) inner: RefCell>, pub(super) bus_names: BusNameList, } #[glib::object_subclass] impl ObjectSubclass for MessageList { const NAME: &'static str = "BustleMessageList"; type Type = super::MessageList; type Interfaces = (gio::ListModel,); } impl ObjectImpl for MessageList {} impl ListModelImpl for MessageList { fn item_type(&self) -> glib::Type { Message::static_type() } fn n_items(&self) -> u32 { self.inner.borrow().len() as u32 } fn item(&self, position: u32) -> Option { self.inner .borrow() .get(position as usize) .map(|item| item.upcast_ref()) .cloned() } } } glib::wrapper! { pub struct MessageList(ObjectSubclass) @implements gio::ListModel; } impl MessageList { pub async fn save_to_file(&self, path: impl AsRef) -> Result<()> { let events = self .imp() .inner .borrow() .iter() .map(|message| message.to_event()) .collect::>(); let path = path.as_ref().to_owned(); RUNTIME .spawn_blocking(move || { let header = PcapHeader { datalink: pcap_file::DataLink::DBUS, ..Default::default() }; let file = File::create(path).context("Failed to create file")?; let mut writer = PcapWriter::with_header(BufWriter::new(file), header) .context("Failed to create writer")?; for event in events { let message_bytes = event.message.data(); writer .write_packet(&PcapPacket { timestamp: event.timestamp.into(), orig_len: message_bytes.len() as u32, data: Cow::Borrowed(message_bytes), }) .context("Failed to write packet")?; } Ok(()) }) .await .context("Failed to spawn blocking task")? .context("Failed to save to file") } pub async fn save_as_dot(&self, dest: &gio::File) -> Result<()> { let mut buffer = Vec::new(); let mut combinations = HashMap::new(); writeln!(&mut buffer, "digraph bustle {{")?; for message in self.imp().inner.borrow().iter() { let sender = message.sender_display(); let destination = message.destination_display(); match combinations.entry((sender, destination)) { Entry::Occupied(_) => continue, Entry::Vacant(entry) => { let (sender, destination) = entry.key(); writeln!(&mut buffer, "\t\"{sender}\" -> \"{destination}\";")?; entry.insert(()); } } } writeln!(&mut buffer, "}}")?; dest.replace_contents_future( buffer, None, false, gio::FileCreateFlags::REPLACE_DESTINATION, ) .await .map_err(|e| e.1)?; Ok(()) } pub async fn load_from_file(path: impl AsRef) -> Result { let path = path.as_ref().to_owned(); let events = RUNTIME .spawn_blocking(move || { let reader = Reader::new(&path).context("Failed to create reader")?; tracing::debug!(?path, "Loaded PCAP file"); let mut events = Vec::new(); for event in reader { let event = event.context("Failed to get packet")?; events.push(event); } Ok(events) }) .await .context("Failed to join handle")? .context("Failed to load from file")?; let this = Self::default(); for event in events { this.push_inner(Message::from_event(event)); } Ok(this) } pub fn push(&self, message: Message) { self.push_inner(message); self.items_changed(self.n_items() - 1, 0, 1); } pub fn bus_names(&self) -> &BusNameList { &self.imp().bus_names } fn push_inner(&self, message: Message) { let position = self.n_items(); message.set_receive_index(position); let imp = self.imp(); if message.message_type().is_method_return() { // Reverse so we first look at the most recent call. This speeds up the search // substantially in the common case where the return is close to the call. if let Some(associated_message) = imp .inner .borrow() .iter() .rev() .find(|other_message| message.is_return_of(other_message)) { message.set_associated_message(associated_message); associated_message.set_associated_message(&message); } } // Only handle message when we have its associated message if let Err(err) = imp.bus_names.handle_message(&message) { tracing::warn!(%message, "Failed to handle message: {:?}", err); } // Only try to guess the component once we have an associated message let message_tag = MessageTag::guess(&message); message.set_message_tag(message_tag); imp.inner.borrow_mut().push(message); } } impl Default for MessageList { fn default() -> Self { glib::Object::new() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/message_tag.rs000066400000000000000000000122631506133026500241430ustar00rootroot00000000000000use std::fmt; use gettextrs::gettext; use gtk::{gdk, glib}; use crate::message::Message; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, glib::Enum)] #[enum_type(name = "BustleMessageTag")] pub enum MessageTag { Accessibility, Bluetooth, Flatpak, Geoclue, Gtk, Gvfs, IBus, Logind, NetworkManager, PolicyKit, Portals, SearchProvider, Secrets, SessionManager, Shell, Systemd, Tracker, UPower, #[default] Unknown, } impl fmt::Display for MessageTag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") } } impl MessageTag { pub fn guess(message: &Message) -> Self { let object_path = message.path_display(); let interface = message.interface_display(); if object_path.contains("/org/a11y/bus") || interface.contains("org.a11y.Bus") { Self::Accessibility } else if object_path.contains("/org/bluez") { Self::Bluetooth } else if object_path.contains("/org/freedesktop/Flatpak") || interface.contains("org.freedesktop.Flatpak") { Self::Flatpak } else if object_path.contains("/org/freedesktop/GeoClue") { Self::Geoclue } else if interface.contains("org.gtk.Actions") || object_path.contains("/org/gtk/Settings") { Self::Gtk } else if object_path.contains("/org/gtk/vfs") || object_path.contains("/org/gtk/Private/RemoteVolumeMonitor") { Self::Gvfs } else if object_path.contains("/org/freedesktop/IBus") { Self::IBus } else if object_path.contains("/org/freedesktop/login") { Self::Logind } else if object_path.contains("/org/freedesktop/NetworkManager") || object_path.contains("/fi/w1/wpa_supplicant") { Self::NetworkManager } else if object_path.contains("/org/freedesktop/PolicyKit") { Self::PolicyKit } else if object_path.contains("/org/freedesktop/portal") || object_path.contains("/org/freedesktop/impl/portal") { Self::Portals } else if interface.contains("org.gnome.Shell.SearchProvider") { Self::SearchProvider } else if interface.contains("org.freedesktop.Secret") || object_path.contains("/org/freedesktop/secrets") { Self::Secrets } else if object_path.contains("/org/gnome/SessionManager") { Self::SessionManager } else if object_path.contains("/org/gnome/Shell") || object_path.contains("/org/gnome/Mutter") { Self::Shell } else if object_path.contains("/org/freedesktop/systemd") { Self::Systemd } else if object_path.contains("/org/freedesktop/Tracker") { Self::Tracker } else if object_path.contains("/org/freedesktop/UPower") { Self::UPower } else { Self::Unknown } } pub fn color(self) -> gdk::RGBA { use crate::colors; match self { Self::Accessibility => colors::PURPLE_5, Self::Bluetooth => colors::RED_3, Self::Flatpak => colors::RED_5, Self::Geoclue => colors::PURPLE_3, Self::Gtk => colors::YELLOW_5, Self::Gvfs => colors::BLUE_5, Self::IBus => colors::YELLOW_4, Self::Logind => colors::BROWN_3, Self::NetworkManager => colors::YELLOW_1, Self::PolicyKit => colors::GREEN_4, Self::Portals => colors::RED_1, Self::SearchProvider => colors::BLUE_2, Self::Secrets => colors::GREEN_2, Self::SessionManager => colors::ORANGE_3, Self::Shell => gdk::RGBA::BLACK, Self::Systemd => colors::RED_2, Self::Tracker => colors::BROWN_1, Self::UPower => colors::ORANGE_5, Self::Unknown => gdk::RGBA::TRANSPARENT, } } pub fn name(self) -> String { match self { Self::Accessibility => gettext("Accessibility"), Self::Bluetooth => gettext("Bluetooth"), Self::Flatpak => gettext("Flatpak"), Self::Geoclue => gettext("Geolocalization"), Self::Gtk => gettext("GTK"), Self::Gvfs => gettext("GVFS"), Self::IBus => gettext("Input"), // TRANSLATORS Noun, as in user login Self::Logind => gettext("Login"), Self::NetworkManager => gettext("Network"), Self::PolicyKit => gettext("Policy Control"), Self::Portals => gettext("Portals"), Self::SearchProvider => gettext("Search Provider"), Self::Secrets => gettext("Secrets"), Self::SessionManager => gettext("Session"), // TRANSLATORS As in the GNOME Shell Self::Shell => gettext("Shell"), Self::Systemd => gettext("systemd"), // TRANSLATORS As in the Tracker software. https://tracker.gnome.org/ Self::Tracker => gettext("Tracker"), Self::UPower => gettext("Power"), Self::Unknown => gettext("Unknown"), } } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/message_type.rs000066400000000000000000000032631506133026500243510ustar00rootroot00000000000000use gettextrs::gettext; use gtk::glib; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, glib::Enum)] #[enum_type(name = "BustleMessageType")] pub enum MessageType { MethodCall, MethodReturn, #[default] Error, Signal, } impl MessageType { /// Returns true if self is a method return or error pub fn is_method_return(self) -> bool { matches!(self, Self::MethodReturn | Self::Error) } pub fn is_error(self) -> bool { matches!(self, Self::Error) } pub fn is_signal(self) -> bool { matches!(self, Self::Signal) } pub fn is_method_call(self) -> bool { matches!(self, Self::MethodCall) } pub fn i18n(self) -> String { match self { Self::MethodCall => gettext("Method Call"), Self::MethodReturn => gettext("Method Return"), Self::Error => gettext("Error"), Self::Signal => gettext("Signal"), } } } impl From for MessageType { fn from(value: zbus::message::Type) -> Self { match value { zbus::message::Type::MethodCall => Self::MethodCall, zbus::message::Type::MethodReturn => Self::MethodReturn, zbus::message::Type::Error => Self::Error, zbus::message::Type::Signal => Self::Signal, } } } impl From for zbus::message::Type { fn from(value: MessageType) -> Self { match value { MessageType::MethodCall => Self::MethodCall, MessageType::MethodReturn => Self::MethodReturn, MessageType::Error => Self::Error, MessageType::Signal => Self::Signal, } } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/monitor.rs000066400000000000000000000253301506133026500233520ustar00rootroot00000000000000use std::{ffi::OsStr, fmt, path::Path, process::Stdio, sync::LazyLock}; use anyhow::{Context, Result, anyhow}; use futures_util::StreamExt; use gtk::{gio, glib}; use pcap_file::pcap::PcapPacket; use tokio::{ io::{AsyncReadExt, BufReader}, process::{Child, Command}, sync::{mpsc, oneshot}, task::JoinHandle, }; use zbus::zvariant; use crate::{ConnectionType, RUNTIME, timestamp::Timestamp}; static IS_RUNNING_IN_FLATPAK: LazyLock = LazyLock::new(|| Path::new("/.flatpak-info").exists()); pub struct Event { pub message: zbus::Message, pub timestamp: Timestamp, } impl Event { pub fn from_packet(packet: PcapPacket<'_>) -> Result { Self::from_bytes(packet.data.into_owned(), packet.timestamp.into()) } pub fn from_bytes(bytes: Vec, timestamp: Timestamp) -> Result { let ctx = zvariant::serialized::Context::new_dbus(zvariant::LE, 0); let data = zvariant::serialized::Data::new(bytes, ctx); let message = unsafe { zbus::Message::from_bytes(data).context("Failed to parse message")? }; Ok(Self { message, timestamp }) } } #[derive(Debug)] pub struct Cancelled(String); impl fmt::Display for Cancelled { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } impl Cancelled { pub fn new(message: &str) -> Self { Self(format!("Cancelled monitoring: {message}")) } } #[derive(Debug)] pub struct Monitor(Inner, ConnectionType); #[allow(clippy::large_enum_variant)] #[derive(Debug)] enum Inner { DbusMonitor { command: Command, child: Option, tokio_handle: Option>, handle: Option>, }, // FIXME Use in session recording when Issue #28 is resolved #[allow(dead_code)] Zbus { tokio_handle: Option>, handle: Option>, }, } impl Drop for Monitor { fn drop(&mut self) { match &mut self.0 { Inner::DbusMonitor { child, tokio_handle, handle, .. } => { if let Some(mut child) = child.take() { glib::spawn_future(async move { if let Err(err) = child.kill().await { tracing::warn!("Failed to kill child process: {:?}", err) } else { tracing::debug!("Killed child process") } }); } if let Some(tokio_handle) = tokio_handle.take() { tokio_handle.abort(); } if let Some(handle) = handle.take() { handle.abort(); } } Inner::Zbus { tokio_handle, handle, .. } => { if let Some(tokio_handle) = tokio_handle.take() { tokio_handle.abort(); } if let Some(handle) = handle.take() { handle.abort(); } } } } } impl Monitor { fn new_dbus_monitor(command: Command, conn_type: ConnectionType) -> Self { Self( Inner::DbusMonitor { command, child: None, tokio_handle: None, handle: None, }, conn_type, ) } pub fn system() -> Self { let mut command = root_host_command("dbus-monitor"); command .stdout(Stdio::piped()) .arg("--binary") .arg("--system"); Self::new_dbus_monitor(command, ConnectionType::System) } pub fn session() -> Self { let mut command = Command::new("dbus-monitor"); command .stdout(Stdio::piped()) .arg("--binary") .arg("--session"); Self::new_dbus_monitor(command, ConnectionType::Session) } pub fn address(address: zbus::Address) -> Self { let mut command = root_host_command("dbus-monitor"); let address = address.to_string(); command .stdout(Stdio::piped()) .arg("--binary") .arg("--address") .arg(&address); Self::new_dbus_monitor(command, ConnectionType::Address(address)) } pub fn connection_type(&self) -> &ConnectionType { &self.1 } pub async fn start(&mut self, message_cb: impl Fn(Event) + 'static) -> Result<()> { match &mut self.0 { Inner::DbusMonitor { command, child, tokio_handle, handle, } => { let enter_guard = RUNTIME.enter(); let mut spawned_child = command.spawn().context("Failed to spawn command")?; drop(enter_guard); let stdout = spawned_child.stdout.take().expect("Child must have stdout"); let (tx, mut rx) = mpsc::channel(10); let (first_read_tx, first_read_rx) = oneshot::channel(); let mut first_read_tx = Some(first_read_tx); tokio_handle.replace(RUNTIME.spawn(async move { let mut reader = BufReader::new(stdout); loop { // Initialize space for the header let mut message_buf = vec![0u8; 16]; // Read the header let ret = reader.read_exact(&mut message_buf).await; if let Some(first_msg_tx) = first_read_tx.take() { let _ = first_msg_tx.send(()); } if let Err(err) = ret { tracing::warn!("Failed to read header bytes: {:?}", err); break; } let timestamp = Timestamp::now(); let bytes_needed = match gio::DBusMessage::bytes_needed(&message_buf) { Ok(n_needed) => n_needed, Err(err) => { tracing::warn!("Failed to get bytes needed: {:?}", err); break; } }; assert!(bytes_needed >= 16); // Make space for the body message_buf.resize(bytes_needed as usize, 0); // Read the body if let Err(err) = reader.read_exact(&mut message_buf[16..]).await { tracing::warn!("Failed to read body bytes: {:?}", err); break; } match Event::from_bytes(message_buf, timestamp) { Ok(event) => { let _ = tx.send(event).await; } Err(err) => { tracing::warn!("Failed to create event from bytes: {:?}", err); } } } })); // Wait for the first read before we check for child status to ensure that the // child already exited on error. first_read_rx.await.expect("rx unexpectedly closed"); if let Some(exit_status) = spawned_child.try_wait().context("Failed to wait child")? { debug_assert!(!exit_status.success(), "child must only exit on error"); let err = anyhow!( "Child exited with status `{:?}` and code `{:?}`", exit_status, exit_status.code() ); match exit_status.code() { Some(126) => { return Err(err.context(Cancelled::new( "User dismissed polkit authorization dialog", ))); } _ => return Err(err), } } else { child.replace(spawned_child); } handle.replace(glib::spawn_future_local(async move { while let Some(event) = rx.recv().await { message_cb(event); } })); } Inner::Zbus { tokio_handle, handle, } => { let cnx = RUNTIME .spawn(async { let cnx = zbus::Connection::session().await?; let proxy = zbus::fdo::MonitoringProxy::new(&cnx).await?; proxy.become_monitor(&[], 0).await?; zbus::Result::Ok(cnx) }) .await .context("Failed to spawn on runtime")? .context("Failed to setup monitoring")?; let (tx, mut rx) = mpsc::channel(10); tokio_handle.replace(RUNTIME.spawn(async move { let mut stream = zbus::MessageStream::from(cnx); while let Some(res) = stream.next().await { match res { Ok(message) => { let _ = tx .send(Event { message, timestamp: Timestamp::now(), }) .await; } Err(err) => tracing::warn!("Failed to receive message: {:?}", err), } } })); handle.replace(glib::spawn_future_local(async move { while let Some(event) = rx.recv().await { message_cb(event); } })); } } Ok(()) } } /// Runs a command in host as much as possible, falling back to `flatpak-spawn /// --host` if running in Flatpak. fn host_command(program: impl AsRef) -> Command { if *IS_RUNNING_IN_FLATPAK { let mut command = Command::new("flatpak-spawn"); command.arg("--host").arg(program); return command; } Command::new(program) } /// Runs a command as root using `pkexec`. fn root_host_command(program: impl AsRef) -> Command { let mut command = host_command("pkexec"); command.arg(program); command } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/multi_set.rs000066400000000000000000000063711506133026500236740ustar00rootroot00000000000000use std::{ collections::{HashMap, hash_map::Entry}, hash::Hash, }; /// Like a set, but keys can be inserted multiple times and, thus, /// removing a key only removes one occurrence. pub struct MultiSet { inner: HashMap, } impl Default for MultiSet { fn default() -> Self { Self { inner: HashMap::new(), } } } impl MultiSet where K: Eq + Hash, { /// Inserts an occurrence of a `key`. If it already exists, an occurrence is /// added. pub fn insert(&mut self, key: K) { match self.inner.entry(key) { Entry::Occupied(mut entry) => { *entry.get_mut() += 1; } Entry::Vacant(entry) => { entry.insert(1); } } } /// Removes an occurrence of a `key`. pub fn remove(&mut self, key: K) { if let Entry::Occupied(mut entry) = self.inner.entry(key) { let &count = entry.get(); debug_assert_ne!(count, 0); if count == 1 { entry.remove(); } else { *entry.get_mut() -= 1; } } } /// Returns true if `key` is exists in the set. pub fn contains(&self, key: &K) -> bool { let count = self.inner.get(key); debug_assert_ne!(count, Some(&0)); count.is_some() } /// Returns an iterator over the *distinct* keys in the set. pub fn iter_unique(&self) -> impl Iterator { self.inner.keys() } /// Returns the length of the *distinct* keys in the set. pub fn len_unique(&self) -> usize { self.inner.len() } pub fn is_empty(&self) -> bool { self.len_unique() == 0 } pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional); } } impl Extend for MultiSet where K: Eq + Hash, { fn extend>(&mut self, iter: T) { // Copied from std lib implementation // See https://docs.rs/hashbrown/0.14.3/src/hashbrown/map.rs.html#6499-6514 let iter = iter.into_iter(); let reserve = if self.is_empty() { iter.size_hint().0 } else { iter.size_hint().0.div_ceil(2) }; self.reserve(reserve); iter.for_each(move |k| { self.insert(k); }); } } #[cfg(test)] mod tests { use super::*; #[test] fn insert_and_remove() { let mut set = MultiSet::default(); assert!(!set.contains(&1)); assert!(!set.contains(&2)); assert_eq!(set.len_unique(), 0); set.insert(1); set.insert(1); assert!(set.contains(&1)); assert_eq!(set.len_unique(), 1); set.insert(2); assert!(set.contains(&1)); assert!(set.contains(&2)); assert_eq!(set.len_unique(), 2); set.remove(1); assert!(set.contains(&1)); assert!(set.contains(&2)); assert_eq!(set.len_unique(), 2); set.remove(1); assert!(!set.contains(&1)); assert!(set.contains(&2)); assert_eq!(set.len_unique(), 1); set.remove(2); assert!(!set.contains(&1)); assert!(!set.contains(&2)); assert_eq!(set.len_unique(), 0); } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/pcap_reader.rs000066400000000000000000000044301506133026500241260ustar00rootroot00000000000000use std::{io::Read, path::Path}; use anyhow::Context; use pcap_file::{pcap, pcapng}; use crate::monitor::Event; pub enum Reader { Pcap(pcap::PcapReader), PcapNg(pcapng::PcapNgReader), } impl Reader { pub fn new(path: impl AsRef) -> anyhow::Result { let is_pcapng = path.as_ref().extension().is_some_and(|ext| ext == "pcapng"); let file = std::fs::File::open(&path) .with_context(|| format!("Failed to open file at `{}`", path.as_ref().display()))?; if is_pcapng { let reader = pcapng::PcapNgReader::new(file)?; Ok(Reader::PcapNg(reader)) } else { let reader = pcap::PcapReader::new(file)?; let datalink = reader.header().datalink; if datalink != pcap_file::DataLink::DBUS { anyhow::bail!("Invalid datalink type `{datalink:?}`"); } Ok(Reader::Pcap(reader)) } } } impl Iterator for Reader { type Item = anyhow::Result; fn next(&mut self) -> Option { match self { Reader::Pcap(inner) => match inner.next_packet() { Some(Ok(packet)) => Some(Event::from_bytes( packet.data.to_vec(), packet.timestamp.into(), )), Some(Err(err)) => Some(Err(err.into())), None => None, }, Reader::PcapNg(inner) => match inner.next_block() { Some(Ok(pcapng::blocks::Block::InterfaceDescription(iface))) => { if iface.linktype != pcap_file::DataLink::DBUS { Some(Err(anyhow::anyhow!( "Invalid datalink type `{:?}`", iface.linktype ))) } else { self.next() } } Some(Ok(pcapng::blocks::Block::EnhancedPacket(packet))) => Some(Event::from_bytes( packet.data.to_vec(), packet.timestamp.into(), )), Some(Ok(_)) => self.next(), Some(Err(err)) => Some(Err(err.into())), None => None, }, } } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/000077500000000000000000000000001506133026500235045ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/duration_item.rs000066400000000000000000000024411506133026500267160ustar00rootroot00000000000000use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::timestamp::Timestamp; mod imp { use std::cell::{Cell, OnceCell}; use super::*; #[derive(Debug, Default, glib::Properties)] #[properties(wrapper_type = super::DurationItem)] pub struct DurationItem { #[property(get, set, construct_only)] pub(super) method: OnceCell, #[property(get, set, construct_only)] pub(super) total: Cell, #[property(get, set, construct_only)] pub(super) calls: Cell, #[property(get, set, construct_only)] pub(super) total_calls: Cell, } #[glib::object_subclass] impl ObjectSubclass for DurationItem { const NAME: &'static str = "BustleDurationItem"; type Type = super::DurationItem; } #[glib::derived_properties] impl ObjectImpl for DurationItem {} } glib::wrapper! { pub struct DurationItem(ObjectSubclass); } impl DurationItem { pub fn new(method: &str, calls: u32, total_calls: u32, total: Timestamp) -> Self { glib::Object::builder() .property("method", method) .property("calls", calls) .property("total-calls", total_calls) .property("total", total) .build() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/frequency_item.rs000066400000000000000000000025001506133026500270660ustar00rootroot00000000000000use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::message_type::MessageType; mod imp { use std::cell::{Cell, OnceCell}; use super::*; #[derive(Debug, Default, glib::Properties)] #[properties(wrapper_type = super::FrequencyItem)] pub struct FrequencyItem { #[property(get, set, construct_only)] pub(super) member: OnceCell, #[property(get, set, construct_only)] pub(super) count: Cell, #[property(get, set, construct_only)] pub(super) total: Cell, #[property(get, set, construct_only, default)] pub(super) message_type: Cell, } #[glib::object_subclass] impl ObjectSubclass for FrequencyItem { const NAME: &'static str = "BustleFrequencyItem"; type Type = super::FrequencyItem; } #[glib::derived_properties] impl ObjectImpl for FrequencyItem {} } glib::wrapper! { pub struct FrequencyItem(ObjectSubclass); } impl FrequencyItem { pub fn new(member: &str, count: u32, total: u32, message_type: MessageType) -> Self { glib::Object::builder() .property("member", member) .property("count", count) .property("total", total) .property("message-type", message_type) .build() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/mod.rs000066400000000000000000000004251506133026500246320ustar00rootroot00000000000000mod duration_item; mod frequency_item; mod pages; mod size_item; mod window; pub use duration_item::DurationItem; pub use frequency_item::FrequencyItem; pub use pages::{DurationsPage, FrequenciesPage, SizesPage}; pub use size_item::SizeItem; pub use window::StatisticsWindow; bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/000077500000000000000000000000001506133026500246035ustar00rootroot00000000000000bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/durations.rs000066400000000000000000000136451506133026500271720ustar00rootroot00000000000000use adw::subclass::prelude::*; use gtk::{gio, glib, prelude::*}; use crate::{filtered_message_model::FilteredMessageModel, statistics::DurationItem}; mod imp { use std::cell::RefCell; use super::*; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::DurationsPage)] #[template(resource = "/org/freedesktop/Bustle/ui/durations_page.ui")] pub struct DurationsPage { #[property(get, set = Self::set_model)] pub(super) model: RefCell>, #[template_child] pub(super) column_view: TemplateChild, #[template_child] pub(super) method_column: TemplateChild, #[template_child] pub(super) total_column: TemplateChild, #[template_child] pub(super) calls_column: TemplateChild, #[template_child] pub(super) mean_column: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for DurationsPage { const NAME: &'static str = "BustleDurationsPage"; type Type = super::DurationsPage; type ParentType = adw::Bin; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for DurationsPage { fn constructed(&self) { self.parent_constructed(); self.calls_column .set_sorter(Some(>k::NumericSorter::new(Some( &DurationItem::this_expression("calls"), )))); self.method_column .set_sorter(Some(>k::CustomSorter::new(|a, b| { let a = a.downcast_ref::().unwrap(); let b = b.downcast_ref::().unwrap(); a.method().cmp(&b.method()).into() }))); self.total_column .set_sorter(Some(>k::CustomSorter::new(|a, b| { let a = a.downcast_ref::().unwrap(); let b = b.downcast_ref::().unwrap(); a.total().cmp(&b.total()).into() }))); self.mean_column .set_sorter(Some(>k::CustomSorter::new(|a, b| { let a = a.downcast_ref::().unwrap(); let b = b.downcast_ref::().unwrap(); let mean_a = a.total() / a.calls(); let mean_b = b.total() / b.calls(); mean_a.cmp(&mean_b).into() }))); } } impl WidgetImpl for DurationsPage {} impl BinImpl for DurationsPage {} impl DurationsPage { fn set_model(&self, model: &FilteredMessageModel) { let mut durations_map = std::collections::HashMap::new(); let durations = gio::ListStore::new::(); for message in model.iter() { if !message.message_type().is_method_call() { continue; } let Some(return_message) = message.associated_message() else { continue; }; // Ignore errors when computing the time it took for a method to receive a reply // This is mostly done to be 100% compatible with Hustle // TODO: investigate if it makes sense to keep this or not if return_message.message_type().is_error() { continue; } let new_duration = return_message.timestamp() - message.timestamp(); let member_name = message.member_markup(true); durations_map .entry(member_name) .and_modify(|(calls, duration)| { *calls += 1; *duration += new_duration; }) .or_insert((1, new_duration)); } let total_calls = durations_map.iter().map(|(_, (call, _))| call).sum(); for (member, (calls, total_duration)) in durations_map.into_iter() { durations.append(&DurationItem::new( &member, calls, total_calls, total_duration, )) } self.column_view .sort_by_column(Some(&self.calls_column), gtk::SortType::Descending); let sorter = self.column_view.sorter(); let sorted_model = gtk::SortListModel::new(Some(durations), sorter); let selection_model = gtk::NoSelection::new(Some(sorted_model)); self.column_view.set_model(Some(&selection_model)); self.model.replace(Some(model.clone())); } } } glib::wrapper! { pub struct DurationsPage(ObjectSubclass) @extends gtk::Widget, adw::Bin, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } #[gtk::template_callbacks] impl DurationsPage { #[template_callback] fn calls_fraction(_: &glib::Object, entry: Option<&DurationItem>) -> f64 { entry.map_or(0.0, |e| e.calls() as f64 / e.total_calls() as f64) } #[template_callback] fn total_duration(_: &glib::Object, entry: Option<&DurationItem>) -> String { entry .map(|e| format!("{:.2} ms", e.total().as_millis_f64())) .unwrap_or_default() } #[template_callback] fn mean_duration(_: &glib::Object, entry: Option<&DurationItem>) -> String { entry .map(|e| format!("{:.2} ms", e.total().as_millis_f64() / e.calls() as f64)) .unwrap_or_default() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/frequencies.rs000066400000000000000000000106471506133026500274720ustar00rootroot00000000000000use adw::subclass::prelude::*; use gtk::{gio, glib, prelude::*}; use crate::{filtered_message_model::FilteredMessageModel, statistics::FrequencyItem}; mod imp { use std::cell::RefCell; use super::*; use crate::statistics::pages::ProgressCell; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::FrequenciesPage)] #[template(resource = "/org/freedesktop/Bustle/ui/frequencies_page.ui")] pub struct FrequenciesPage { #[property(get, set = Self::set_model)] pub(super) model: RefCell>, #[template_child] pub(super) column_view: TemplateChild, #[template_child] pub(super) message_type_column: TemplateChild, #[template_child] pub(super) member_column: TemplateChild, #[template_child] pub(super) frequency_column: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for FrequenciesPage { const NAME: &'static str = "BustleFrequenciesPage"; type Type = super::FrequenciesPage; type ParentType = adw::Bin; fn class_init(klass: &mut Self::Class) { ProgressCell::ensure_type(); klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for FrequenciesPage { fn constructed(&self) { self.parent_constructed(); self.frequency_column .set_sorter(Some(>k::NumericSorter::new(Some( &FrequencyItem::this_expression("count"), )))); self.message_type_column .set_sorter(Some(>k::CustomSorter::new(|a, b| { let a = a.downcast_ref::().unwrap(); let b = b.downcast_ref::().unwrap(); a.message_type().cmp(&b.message_type()).into() }))); self.member_column .set_sorter(Some(>k::StringSorter::new(Some( &FrequencyItem::this_expression("member"), )))); } } impl WidgetImpl for FrequenciesPage {} impl BinImpl for FrequenciesPage {} impl FrequenciesPage { fn set_model(&self, model: &FilteredMessageModel) { let mut n_items = model.n_items(); let frequencies = gio::ListStore::new::(); let mut frequencies_map = std::collections::HashMap::new(); for message in model.iter() { let message_type = message.message_type(); if message_type.is_method_return() { n_items -= 1; continue; } let member_name = message.member_markup(true); frequencies_map .entry((member_name, message_type)) .and_modify(|count| *count += 1) .or_insert(1); } for ((member, message_type), count) in frequencies_map.iter() { frequencies.append(&FrequencyItem::new(member, *count, n_items, *message_type)) } self.column_view .sort_by_column(Some(&self.frequency_column), gtk::SortType::Descending); let sorter = self.column_view.sorter(); let sorted_model = gtk::SortListModel::new(Some(frequencies), sorter); let selection_model = gtk::NoSelection::new(Some(sorted_model)); self.column_view.set_model(Some(&selection_model)); self.model.replace(Some(model.clone())); } } } glib::wrapper! { pub struct FrequenciesPage(ObjectSubclass) @extends gtk::Widget, adw::Bin, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } #[gtk::template_callbacks] impl FrequenciesPage { #[template_callback] fn fraction(_: &glib::Object, entry: Option<&FrequencyItem>) -> f64 { entry.map_or(0.0, |e| e.count() as f64 / e.total() as f64) } #[template_callback] fn message_type(_: &glib::Object, entry: Option<&FrequencyItem>) -> String { entry.map(|e| e.message_type().i18n()).unwrap_or_default() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/mod.rs000066400000000000000000000003061506133026500257270ustar00rootroot00000000000000mod durations; mod frequencies; mod progress_cell; mod sizes; pub use durations::DurationsPage; pub use frequencies::FrequenciesPage; pub use progress_cell::ProgressCell; pub use sizes::SizesPage; bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/progress_cell.rs000066400000000000000000000144431506133026500300220ustar00rootroot00000000000000// Rust rewrite of use adw::subclass::prelude::*; use gtk::{gdk, glib, graphene, prelude::*}; mod imp { use std::cell::{Cell, RefCell}; use super::*; #[derive(Debug, glib::Properties)] #[properties(wrapper_type = super::ProgressCell)] pub struct ProgressCell { #[property(get, set = Self::set_fraction, minimum = 0.0, maximum = 1.0, default = 0.0)] pub(super) fraction: Cell, #[property(get, set = Self::set_text)] pub(super) text: RefCell, pub(super) through: adw::Bin, pub(super) progress: adw::Bin, pub(super) label: gtk::Label, pub(super) alt_label: gtk::Label, } impl Default for ProgressCell { fn default() -> Self { Self { fraction: Default::default(), text: Default::default(), through: adw::Bin::builder().css_name("through").build(), progress: adw::Bin::builder() .css_name("progress") .visible(false) .build(), label: gtk::Label::builder() .halign(gtk::Align::Center) .label("0") .build(), alt_label: gtk::Label::builder() .halign(gtk::Align::Center) .label("0") .build(), } } } #[glib::object_subclass] impl ObjectSubclass for ProgressCell { const NAME: &'static str = "BustleProgressCell"; type Type = super::ProgressCell; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_css_name("progress-cell"); klass.set_accessible_role(gtk::AccessibleRole::ProgressBar); } } #[glib::derived_properties] impl ObjectImpl for ProgressCell { fn constructed(&self) { self.parent_constructed(); let widget = self.obj(); self.alt_label.add_css_class("in-progress"); self.label.add_css_class("numeric"); self.alt_label.add_css_class("numeric"); widget .bind_property("text", &self.label, "label") .sync_create() .build(); widget .bind_property("text", &self.alt_label, "label") .sync_create() .build(); self.through.set_parent(&*widget); self.label.set_parent(&*widget); self.progress.set_parent(&*widget); self.alt_label.set_parent(&*widget); widget.update_property(&[ gtk::accessible::Property::ValueMax(1.0), gtk::accessible::Property::ValueMin(0.0), gtk::accessible::Property::ValueNow(0.0), ]); } fn dispose(&self) { self.through.unparent(); self.progress.unparent(); self.label.unparent(); self.alt_label.unparent(); } } impl WidgetImpl for ProgressCell { fn size_allocate(&self, width: i32, height: i32, baseline: i32) { let widget = self.obj(); let allocation = gdk::Rectangle::new(0, 0, width, height); self.label.size_allocate(&allocation, baseline); self.alt_label.size_allocate(&allocation, baseline); self.through.size_allocate(&allocation, baseline); if self.progress.get_visible() { self.progress.size_allocate( &gdk::Rectangle::new( 0, 0, (width as f32 * widget.fraction()).max(1.) as i32, height, ), baseline, ); } } fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) { let (mut minimum, mut natural) = (0, 0); let (minimum_baseline, natural_baseline) = (-1, -1); let widgets = [ self.through.upcast_ref::(), self.progress.upcast_ref::(), self.label.upcast_ref::(), self.alt_label.upcast_ref::(), ]; for widget in widgets.into_iter() { let (child_min, child_nat, _, _) = widget.measure(orientation, for_size); if child_min > minimum { minimum = child_min; } if child_nat > natural { natural = child_nat; } } (minimum, natural, minimum_baseline, natural_baseline) } fn snapshot(&self, snapshot: >k::Snapshot) { let widget = self.obj(); widget.snapshot_child(&self.through, snapshot); widget.snapshot_child(&self.label, snapshot); widget.snapshot_child(&self.progress, snapshot); let w = widget.width(); let h = widget.height(); snapshot.push_clip(&graphene::Rect::new( 0., 0., w as f32 * widget.fraction(), h as f32, )); widget.snapshot_child(&self.alt_label, snapshot); snapshot.pop(); self.parent_snapshot(snapshot); } } impl ProgressCell { fn set_fraction(&self, mut fraction: f32) { fraction = fraction.clamp(0., 1.); let widget = self.obj(); if self.fraction.get() != fraction { self.fraction.set(fraction); self.progress.set_visible(fraction > 0.); widget.notify_fraction(); widget.update_property(&[gtk::accessible::Property::ValueNow(fraction.into())]); widget.queue_allocate(); } } fn set_text(&self, text: String) { if self.text.replace(text.clone()) != text { self.obj().notify_text(); } } } } glib::wrapper! { pub struct ProgressCell(ObjectSubclass) @extends gtk::Widget, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/pages/sizes.rs000066400000000000000000000136031506133026500263110ustar00rootroot00000000000000use std::collections::HashMap; use adw::subclass::prelude::*; use gtk::{gio, glib, prelude::*}; use crate::{ filtered_message_model::FilteredMessageModel, message_type::MessageType, statistics::SizeItem, }; mod imp { use std::cell::RefCell; use super::*; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::SizesPage)] #[template(resource = "/org/freedesktop/Bustle/ui/sizes_page.ui")] pub struct SizesPage { #[property(get, set = Self::set_model)] pub(super) model: RefCell>, #[template_child] pub(super) column_view: TemplateChild, #[template_child] pub(super) message_type_column: TemplateChild, #[template_child] pub(super) member_column: TemplateChild, #[template_child] pub(super) largest_column: TemplateChild, #[template_child] pub(super) mean_column: TemplateChild, #[template_child] pub(super) smallest_column: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for SizesPage { const NAME: &'static str = "BustleSizesPage"; type Type = super::SizesPage; type ParentType = adw::Bin; fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_instance_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for SizesPage { fn constructed(&self) { self.parent_constructed(); self.member_column .set_sorter(Some(>k::StringSorter::new(Some( &SizeItem::this_expression("member"), )))); self.message_type_column .set_sorter(Some(>k::CustomSorter::new(|a, b| { let a = a.downcast_ref::().unwrap(); let b = b.downcast_ref::().unwrap(); a.message_type().cmp(&b.message_type()).into() }))); self.largest_column.set_sorter(Some( >k::NumericSorter::builder() .expression(SizeItem::this_expression("largest")) .sort_order(gtk::SortType::Descending) .build(), )); self.smallest_column.set_sorter(Some( >k::NumericSorter::builder() .expression(SizeItem::this_expression("smallest")) .sort_order(gtk::SortType::Descending) .build(), )); self.mean_column.set_sorter(Some( >k::NumericSorter::builder() .expression(SizeItem::this_expression("mean")) .sort_order(gtk::SortType::Descending) .build(), )); } } impl WidgetImpl for SizesPage {} impl BinImpl for SizesPage {} impl SizesPage { fn set_model(&self, model: &FilteredMessageModel) { let mut sizes_map: HashMap<(MessageType, String), (usize, Vec)> = std::collections::HashMap::new(); let sizes = gio::ListStore::new::(); for message in model.iter() { let length = message.len(); let message_type = message.message_type(); let member_name = message.member_markup(true); sizes_map .entry((message_type, member_name)) .and_modify(|(calls, bytes_vec)| { *calls += 1; bytes_vec.push(length); }) .or_insert((1, vec![length])); } for ((message_type, member), (calls, mut bytes)) in sizes_map.into_iter() { bytes.sort(); let mean = bytes.iter().copied().sum::() / calls; sizes.append(&SizeItem::new( &member, *bytes.first().unwrap_or(&0) as u32, mean as u32, *bytes.last().unwrap_or(&0) as u32, message_type, )) } self.column_view .sort_by_column(Some(&self.mean_column), gtk::SortType::Ascending); let sorter = self.column_view.sorter(); let sorted_model = gtk::SortListModel::new(Some(sizes), sorter); let selection_model = gtk::NoSelection::new(Some(sorted_model)); self.column_view.set_model(Some(&selection_model)); self.model.replace(Some(model.clone())); } } } glib::wrapper! { pub struct SizesPage(ObjectSubclass) @extends gtk::Widget, adw::Bin, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } #[gtk::template_callbacks] impl SizesPage { #[template_callback] fn message_type(_: &glib::Object, entry: Option<&SizeItem>) -> String { entry.map(|e| e.message_type().i18n()).unwrap_or_default() } #[template_callback] fn smallest_bytes(_: &glib::Object, entry: Option<&SizeItem>) -> glib::GString { entry .map(|e| glib::format_size(e.smallest().into())) .unwrap_or_default() } #[template_callback] fn mean_bytes(_: &glib::Object, entry: Option<&SizeItem>) -> glib::GString { entry .map(|e| glib::format_size(e.mean().into())) .unwrap_or_default() } #[template_callback] fn largest_bytes(_: &glib::Object, entry: Option<&SizeItem>) -> glib::GString { entry .map(|e| glib::format_size(e.largest().into())) .unwrap_or_default() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/size_item.rs000066400000000000000000000027331506133026500260470ustar00rootroot00000000000000use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::message_type::MessageType; mod imp { use std::cell::{Cell, OnceCell}; use super::*; #[derive(Debug, Default, glib::Properties)] #[properties(wrapper_type = super::SizeItem)] pub struct SizeItem { #[property(get, set, construct_only)] pub(super) member: OnceCell, #[property(get, set, construct_only)] pub(super) smallest: Cell, #[property(get, set, construct_only)] pub(super) mean: Cell, #[property(get, set, construct_only)] pub(super) largest: Cell, #[property(get, set, construct_only, default)] pub(super) message_type: OnceCell, } #[glib::object_subclass] impl ObjectSubclass for SizeItem { const NAME: &'static str = "BustleSizeItem"; type Type = super::SizeItem; } #[glib::derived_properties] impl ObjectImpl for SizeItem {} } glib::wrapper! { pub struct SizeItem(ObjectSubclass); } impl SizeItem { pub fn new( member: &str, smallest: u32, mean: u32, largest: u32, message_type: MessageType, ) -> Self { glib::Object::builder() .property("member", member) .property("smallest", smallest) .property("mean", mean) .property("largest", largest) .property("message-type", message_type) .build() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/statistics/window.rs000066400000000000000000000041121506133026500253570ustar00rootroot00000000000000use adw::{prelude::*, subclass::prelude::*}; use gtk::glib; use crate::filtered_message_model::FilteredMessageModel; mod imp { use std::cell::OnceCell; use super::*; use crate::statistics::{DurationsPage, FrequenciesPage, SizesPage}; #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)] #[properties(wrapper_type = super::StatisticsWindow)] #[template(resource = "/org/freedesktop/Bustle/ui/statistics.ui")] pub struct StatisticsWindow { #[property(get, set, construct_only)] pub(super) model: OnceCell, #[template_child] pub(super) durations_page: TemplateChild, #[template_child] pub(super) sizes_page: TemplateChild, #[template_child] pub(super) frequencies_page: TemplateChild, } #[glib::object_subclass] impl ObjectSubclass for StatisticsWindow { const NAME: &'static str = "BustleStatisticsWindow"; type Type = super::StatisticsWindow; type ParentType = adw::Dialog; fn class_init(klass: &mut Self::Class) { klass.bind_template(); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } #[glib::derived_properties] impl ObjectImpl for StatisticsWindow { fn constructed(&self) { self.parent_constructed(); let model = self.obj().model(); self.durations_page.set_model(&model); self.frequencies_page.set_model(&model); self.sizes_page.set_model(&model); } } impl WidgetImpl for StatisticsWindow {} impl AdwDialogImpl for StatisticsWindow {} } glib::wrapper! { pub struct StatisticsWindow(ObjectSubclass) @extends gtk::Widget, adw::Dialog, @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible; } impl StatisticsWindow { pub fn new(model: &FilteredMessageModel) -> Self { glib::Object::builder().property("model", model).build() } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/timestamp.rs000066400000000000000000000021401506133026500236600ustar00rootroot00000000000000use std::{ ops, time::{Duration, SystemTime}, }; use gtk::glib; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, glib::Boxed)] #[boxed_type(name = "BustleTimestamp")] pub struct Timestamp(Duration); impl Timestamp { pub fn now() -> Self { let dur = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("SystemTime before UNIX EPOCH!"); Self(dur) } pub fn as_millis_f64(self) -> f64 { self.0.as_micros() as f64 / 1_000.0 } } impl ops::Sub for Timestamp { type Output = Self; fn sub(self, rhs: Self) -> Self { Self(self.0.sub(rhs.0)) } } impl ops::Div for Timestamp { type Output = Self; fn div(self, rhs: u32) -> Self { Self(self.0.div(rhs)) } } impl ops::AddAssign for Timestamp { fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0); } } impl From for Timestamp { fn from(dur: Duration) -> Self { Self(dur) } } impl From for Duration { fn from(ts: Timestamp) -> Self { ts.0 } } bustle-0.13.0-fe6e492887c1ea542300ac2860aac6f3c08c59a3/src/window.rs000066400000000000000000000563601506133026500232010ustar00rootroot00000000000000use adw::{prelude::*, subclass::prelude::*}; use anyhow::{Context, Result}; use gettextrs::gettext; use gtk::{ gio, glib::{self, clone}, }; use crate::{ ConnectionType, address_dialog::AddressDialog, application::Application, config::{APP_ID, PROFILE, VERSION}, details_view::DetailsView, diagram::Diagram, i18n::{gettext_f, ngettext_f}, message::Message, message_list::MessageList, monitor::{Cancelled, Monitor}, statistics::StatisticsWindow, }; #[derive(Default, Debug, Copy, Clone, glib::Enum, PartialEq)] #[repr(u32)] #[enum_type(name = "BustleView")] pub enum View { #[default] EmptyState, Loading, Diagram, } mod imp { use std::cell::RefCell; use super::*; use crate::{filter_pane::FilterPane, filtered_message_model::FilteredMessageModel}; #[derive(Debug, gtk::CompositeTemplate)] #[template(resource = "/org/freedesktop/Bustle/ui/window.ui")] pub struct Window { #[template_child] pub(super) toast_overlay: TemplateChild, #[template_child] pub(super) main_stack: TemplateChild, #[template_child] pub(super) empty_page: TemplateChild, #[template_child] pub(super) empty_status_page: TemplateChild, #[template_child] pub(super) loading_page: TemplateChild, #[template_child] pub(super) diagram_page: TemplateChild, #[template_child] pub(super) record_button_stack: TemplateChild, #[template_child] pub(super) diagram_title: TemplateChild, #[template_child] pub(super) diagram_page_stack: TemplateChild, #[template_child] pub(super) waiting_sub_page: TemplateChild, #[template_child] pub(super) split_view_sub_page: TemplateChild, #[template_child] pub(super) details_view_split_view: TemplateChild, #[template_child] pub(super) diagram: TemplateChild, #[template_child] pub(super) filtered_message_model: TemplateChild, #[template_child] pub(super) details_view: TemplateChild, #[template_child] pub(super) sidebar_button: TemplateChild, pub(super) settings: gio::Settings, pub(super) monitor: RefCell>, // The currently recorded filename pub(super) filename: RefCell>, } impl Default for Window { fn default() -> Self { Self { toast_overlay: TemplateChild::default(), main_stack: TemplateChild::default(), empty_page: TemplateChild::default(), empty_status_page: TemplateChild::default(), loading_page: TemplateChild::default(), diagram_page: TemplateChild::default(), record_button_stack: TemplateChild::default(), diagram_title: TemplateChild::default(), diagram_page_stack: TemplateChild::default(), waiting_sub_page: TemplateChild::default(), split_view_sub_page: TemplateChild::default(), details_view_split_view: TemplateChild::default(), sidebar_button: TemplateChild::default(), diagram: TemplateChild::default(), filtered_message_model: TemplateChild::default(), details_view: TemplateChild::default(), settings: gio::Settings::new(APP_ID), monitor: RefCell::default(), filename: RefCell::default(), } } } #[glib::object_subclass] impl ObjectSubclass for Window { const NAME: &'static str = "BustleWindow"; type Type = super::Window; type ParentType = adw::ApplicationWindow; fn class_init(klass: &mut Self::Class) { FilterPane::ensure_type(); klass.bind_template(); klass.bind_template_instance_callbacks(); klass.install_action("win.about", None, |window, _, _| { window.show_about_window(); }); klass.install_action("win.statistics", None, |window, _, _| { window.show_statistics(); }); klass.install_action("win.filter-services", None, |window, _, _| { let imp = window.imp(); imp.split_view_sub_page .set_show_sidebar(!imp.split_view_sub_page.shows_sidebar()); }); klass.install_action_async("win.record-session-bus", None, async |window, _, _| { if let Err(err) = window .start_recording(Monitor::session(), &gettext("Recording session bus…")) .await { tracing::error!("Failed to record session: {err:?}"); if !err.is::() { window.add_error_toast(&gettext("Failed to record session bus")); } } }); klass.install_action_async("win.record-system-bus", None, async |window, _, _| { if let Err(err) = window .start_recording(Monitor::system(), &gettext("Recording system bus…")) .await { if !err.is::() { tracing::error!("Failed to record system: {err:?}"); window.add_error_toast(&gettext("Failed to record system bus")); } } }); klass.install_action_async("win.record-address", None, async |window, _, _| { let Ok(address) = AddressDialog::choose(&window).await else { return; }; let address_display = address.to_string(); if let Err(err) = window .start_recording( Monitor::address(address), // Translators: Do NOT translate the contents between '{' and '}', this // is a variable name. &gettext_f("Recording {address}…", &[("address", &address_display)]), ) .await { tracing::error!( address = address_display, "Failed to record address: {err:?}" ); if !err.is::() { window.add_error_toast(&gettext("Failed to record address")); } } }); klass.install_action("win.stop-recording", None, |window, _, _| { window.stop_recording(); }); klass.install_action_async("win.open-log", None, async |window, _, _| { if let Err(err) = window.open_log().await { tracing::error!("Could not open log: {err:?}"); if !err .downcast_ref::() .is_some_and(|error| error.matches(gtk::DialogError::Dismissed)) { window.add_error_toast(&gettext("Failed to open file")); } } }); klass.install_action_async("win.save", None, async |window, _, _| { if let Err(err) = window.save().await { tracing::error!("Could not save: {err:?}"); if !err .downcast_ref::() .is_some_and(|error| error.matches(gtk::DialogError::Dismissed)) { window.add_error_toast(&gettext("Failed to save as PCAP")); } } else { window.add_message_toast(&gettext("Recording saved as PCAP")); } }); klass.install_action_async("win.save-dot", None, async |window, _, _| { if let Err(err) = window.save_as_dot().await { tracing::error!("Could not save: {err:?}"); if !err .downcast_ref::() .is_some_and(|error| error.matches(gtk::DialogError::Dismissed)) { window.add_error_toast(&gettext("Failed to save as DOT graph")); } } else { window.add_message_toast(&gettext("Recording saved as DOT graph")) } }); } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } impl ObjectImpl for Window { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); // Devel Profile if PROFILE == "Devel" { obj.add_css_class("devel"); } self.empty_status_page.set_icon_name(Some(APP_ID)); // Load latest window state obj.load_window_size(); obj.update_details_view(); obj.set_view(View::EmptyState); } } impl WidgetImpl for Window {} impl WindowImpl for Window { // Save window state on delete event fn close_request(&self) -> glib::Propagation { if let Err(err) = self.obj().save_window_size() { tracing::warn!("Failed to save window state: {:?}", err); } // Pass close request on to the parent self.parent_close_request() } } impl ApplicationWindowImpl for Window {} impl AdwApplicationWindowImpl for Window {} } glib::wrapper! { pub struct Window(ObjectSubclass) @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow, @implements gio::ActionMap, gio::ActionGroup, gtk::ConstraintTarget, gtk::Accessible, gtk::Buildable, gtk::ShortcutManager, gtk::Native, gtk::Root; } impl Window { fn new(app: &Application) -> Self { glib::Object::builder().property("application", app).build() } pub fn with_group(app: &Application) -> Self { let window = Self::new(app); let group = gtk::WindowGroup::new(); group.add_window(&window); window } pub async fn load_log(&self, file: &gio::File) -> anyhow::Result<()> { let path = file.path().context("Log file does not have a path")?; let prev_view = self.view(); self.set_view(View::Loading); let message_list = match MessageList::load_from_file(path).await { Ok(message_list) => message_list, Err(err) => { self.set_view(prev_view); return Err(err); } }; let imp = self.imp(); imp.filtered_message_model .set_message_list(Some(&message_list)); let filename = file.basename().unwrap_or_default().display().to_string(); imp.diagram_title.set_title(&filename); imp.filename .replace(Some(filename.trim_end_matches(".pcap").to_string())); self.set_view(View::Diagram); imp.diagram_page_stack .set_visible_child(&*imp.split_view_sub_page); Ok(()) } fn set_view(&self, view: View) { let imp = self.imp(); match view { View::EmptyState => imp.main_stack.set_visible_child(&*imp.empty_page), View::Loading => imp.main_stack.set_visible_child(&*imp.loading_page), View::Diagram => { imp.main_stack.set_visible_child(&*imp.diagram_page); } } let is_recording = imp.monitor.borrow().is_some(); self.action_set_enabled( "win.record-session-bus", view == View::EmptyState || (view == View::Diagram && !is_recording), ); self.action_set_enabled( "win.record-system-bus", view == View::EmptyState || (view == View::Diagram && !is_recording), ); self.action_set_enabled( "win.record-address", view == View::EmptyState || (view == View::Diagram && !is_recording), ); self.action_set_enabled("win.stop-recording", view == View::Diagram && is_recording); self.action_set_enabled( "win.open-log", view == View::EmptyState || (view == View::Diagram && !is_recording), ); self.action_set_enabled( "win.open-pair-logs", view == View::EmptyState || (view == View::Diagram && !is_recording), ); self.action_set_enabled("win.statistics", view == View::Diagram && !is_recording); self.action_set_enabled("win.filter-services", view == View::Diagram); self.action_set_enabled("win.save", view == View::Diagram && !is_recording); self.action_set_enabled("win.save-dot", view == View::Diagram && !is_recording); } fn view(&self) -> View { let imp = self.imp(); let visible_child = imp.main_stack.visible_child().unwrap(); if visible_child == *imp.empty_page { View::EmptyState } else if visible_child == *imp.loading_page { View::Loading } else if visible_child == *imp.diagram_page { View::Diagram } else { unreachable!("unexpected visible child: {:?}", visible_child) } } fn save_window_size(&self) -> Result<(), glib::BoolError> { let imp = self.imp(); let (width, height) = self.default_size(); imp.settings.set_int("window-width", width)?; imp.settings.set_int("window-height", height)?; imp.settings .set_boolean("is-maximized", self.is_maximized())?; Ok(()) } fn load_window_size(&self) { let imp = self.imp(); let width = imp.settings.int("window-width"); let height = imp.settings.int("window-height"); let is_maximized = imp.settings.boolean("is-maximized"); self.set_default_size(width, height); if is_maximized { self.maximize(); } } fn show_about_window(&self) { let dialog = adw::AboutDialog::builder() .application_name("Bustle") .application_icon(APP_ID) .copyright("© 2008–2023 Will Thompson, Collabora Ltd. and contributors") .license_type(gtk::License::Lgpl21) .website("https://apps.gnome.org/Bustle/") .version(VERSION) .translator_credits(gettext("translator-credits")) .developer_name(gettext("The Bustle developers")) .developers(vec![ "Bilal Elmoussaoui", "Dave Patrick Caberto", "Maximiliano Sandoval", "Will Thompson ", "Dafydd Harries", "Chris Lamb", "Marc Kleine-Budde", "Cosimo Alfarano", "Sergei Trofimovich", "Alex Merry", "Philip Withnall", "Jonny Lamb", "Daniel Firth", ]) .designers(vec!["Tobias Bernard"]) .build(); dialog.present(Some(self)); } fn show_statistics(&self) { let imp = self.imp(); debug_assert!(imp.filtered_message_model.message_list().is_some()); StatisticsWindow::new(&imp.filtered_message_model).present(Some(self)); } fn stop_recording(&self) { let imp = self.imp(); let monitor = imp .monitor .take() .expect("monitor must be set when recording"); drop(monitor); imp.diagram.set_should_stick(false); let filename = glib::DateTime::now_local() .unwrap() .format("%Y-%m-%d %H:%M:%S") .unwrap(); imp.diagram_title.set_title(&format!("*{filename}.pcap")); imp.filename.replace(Some(filename.to_string())); if imp.filtered_message_model.n_items() != 0 { self.set_view(View::Diagram); imp.diagram_page_stack .set_visible_child(&*imp.split_view_sub_page); imp.record_button_stack.set_visible_child_name("record"); } else { self.set_view(View::EmptyState); } } async fn start_recording(&self, mut monitor: Monitor, display_message: &str) -> Result<()> { let imp = self.imp(); let message_list = MessageList::default(); monitor .start(clone!( #[weak] message_list, move |event| { message_list.push(Message::from_event(event)); } )) .await .context("Failed to start monitor")?; imp.monitor.replace(Some(monitor)); imp.diagram.set_should_stick(true); imp.filtered_message_model .set_message_list(Some(&message_list)); imp.diagram_title.set_title(display_message); imp.diagram_title.set_subtitle(""); self.set_view(View::Diagram); imp.diagram_page_stack .set_visible_child(&*imp.waiting_sub_page); imp.record_button_stack.set_visible_child_name("stop"); Ok(()) } async fn open_log(&self) -> anyhow::Result<()> { let filter = gtk::FileFilter::new(); // Translators: PCAP is a type of file, do not translate. filter.set_property("name", gettext("PCAP Files")); filter.add_mime_type("application/vnd.tcpdump.pcap"); filter.add_suffix("pcapng"); let filters = gio::ListStore::new::(); filters.append(&filter); let chooser = gtk::FileDialog::builder() .title(gettext("Open Log")) .filters(&filters) .modal(true) .build(); let file = match chooser.open_future(Some(self)).await { Err(err) if err.matches(gtk::DialogError::Dismissed) => return Ok(()), res => res?, }; self.load_log(&file).await?; Ok(()) } async fn save(&self) -> anyhow::Result<()> { let imp = self.imp(); let filter = gtk::FileFilter::new(); // Translators: PCAP is a type of file, do not translate. filter.set_property("name", gettext("PCAP Files")); filter.add_mime_type("application/vnd.tcpdump.pcap"); filter.add_suffix("pcapng"); let filters = gio::ListStore::new::(); filters.append(&filter); let mut builder = gtk::FileDialog::builder() .title(gettext("Save Log")) .filters(&filters) .modal(true); if let Some(filename) = imp.filename.borrow().as_ref() { builder = builder.initial_name(format!("{filename}.pcap")); } let chooser = builder.build(); let file = chooser.save_future(Some(self)).await?; let path = file.path().context("Selected file does not have a path")?; let message_list = imp .filtered_message_model .message_list() .expect("message list must be set before saving"); message_list.save_to_file(path).await?; // Update the title once the save operation is done // removing the `*` prefix if let Some(filename) = imp.filename.borrow().as_ref() { imp.diagram_title.set_title(filename); } Ok(()) } async fn save_as_dot(&self) -> Result<()> { let imp = self.imp(); let filter = gtk::FileFilter::new(); // Translators: Dot is a type of file, do not translate. filter.set_property("name", gettext("DOT Graph")); filter.add_mime_type("text/vnd.graphviz"); let filters = gio::ListStore::new::(); filters.append(&filter); let mut builder = gtk::FileDialog::builder() .title(gettext("Save Log as DOT Graph File")) .filters(&filters) .modal(true); if let Some(filename) = imp.filename.borrow().as_ref() { builder = builder.initial_name(format!("{filename}.gv")); } let chooser = builder.build(); let file = chooser.save_future(Some(self)).await?; let message_list = imp .filtered_message_model .message_list() .expect("message list must be set before saving"); message_list.save_as_dot(&file).await?; Ok(()) } pub fn add_message_toast(&self, message: &str) { let toast = adw::Toast::new(message); self.imp().toast_overlay.add_toast(toast); } fn add_error_toast(&self, message: &str) { let toast = adw::Toast::builder() .title(message) .priority(adw::ToastPriority::High) .build(); self.imp().toast_overlay.add_toast(toast); } fn update_details_view(&self) { let imp = self.imp(); if let Some(message) = imp.diagram.selected_message() { imp.details_view.set_message(Some(message)); imp.details_view_split_view.set_show_sidebar(true); imp.sidebar_button.set_sensitive(true); } else { imp.details_view.set_message(None::); imp.details_view_split_view.set_show_sidebar(false); imp.sidebar_button.set_sensitive(false); } } } #[gtk::template_callbacks] impl Window { #[template_callback] fn diagram_selected_message_notify(&self) { self.update_details_view(); } #[template_callback] fn filtered_message_model_items_changed(&self, _position: u32, removed: u32, added: u32) { if removed == 0 && added == 0 { return; } let imp = self.imp(); let is_recording = imp.monitor.borrow().is_some(); if is_recording { let n_messages = imp.filtered_message_model.n_items(); imp.diagram_title.set_subtitle(&ngettext_f( // Translators: Do NOT translate the contents between '{' and '}', this is a // variable name. "Logged {n_messages} message", "Logged {n_messages} messages", n_messages, &[("n_messages", &n_messages.to_string())], )); if n_messages != 0 { imp.diagram_page_stack .set_visible_child(&*imp.split_view_sub_page); } } } pub fn connection_type(&self) -> Option { self.imp() .monitor .borrow() .as_ref() .map(|m| m.connection_type()) .cloned() } #[template_callback] fn copy_command_clicked(&self) { const CMD: &str = "dbus-monitor --pcap"; self.clipboard().set_text(CMD); self.add_message_toast(&gettext("Copied to clipboard")); } #[template_callback] fn details_view_show_message_request(&self, message: &Message) { if let Err(err) = self .imp() .diagram .scroll_to(message, gtk::ListScrollFlags::SELECT) { tracing::error!("Failed to scroll to message: {:?}", err); } } }