├── .gitlab-ci.yml ├── AUTHORS ├── COPYING ├── MAINTAINERS ├── NEWS.md ├── README.md ├── default.nix ├── docs └── reference │ ├── entities.docbook.in │ ├── meson.build │ ├── nautilus-python-class-reference.xml │ ├── nautilus-python-column-provider.xml │ ├── nautilus-python-column.xml │ ├── nautilus-python-enum-reference.xml │ ├── nautilus-python-file-info.xml │ ├── nautilus-python-info-provider.xml │ ├── nautilus-python-menu-item.xml │ ├── nautilus-python-menu-provider.xml │ ├── nautilus-python-menu.xml │ ├── nautilus-python-migrating-to-4.xml │ ├── nautilus-python-operation-result.xml │ ├── nautilus-python-overview-example.xml │ ├── nautilus-python-overview-methods.xml │ ├── nautilus-python-overview.xml │ ├── nautilus-python-properties-item.xml │ ├── nautilus-python-properties-model-provider.xml │ ├── nautilus-python-properties-model.xml │ ├── nautilus-python-provider-reference.xml │ └── nautilus-python-ref.xml ├── examples ├── README ├── TestExtension.py ├── background-image.py ├── block-size-column.py ├── md5sum-properties-model.py ├── meson.build ├── open-terminal.py ├── submenu.py └── update-file-info-async.py ├── meson.build ├── meson_options.txt ├── nautilus-python.doap ├── nix ├── sources.json └── sources.nix ├── shell.nix └── src ├── meson.build ├── nautilus-python-object.c ├── nautilus-python-object.h ├── nautilus-python.c └── nautilus-python.h /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - component: gitlab.gnome.org/GNOME/citemplates/release-service@master 3 | inputs: 4 | dist-job-name: "build-and-test" 5 | tarball-artifact-path: "${TARBALL_ARTIFACT_DIR}/${CI_PROJECT_NAME}-${CI_COMMIT_TAG}.tar.xz" 6 | 7 | variables: 8 | MESON_BUILD_DIR: _build 9 | TARBALL_ARTIFACT_DIR: "${MESON_BUILD_DIR}/meson-dist" 10 | 11 | build-and-test: 12 | image: nixpkgs/nix:latest 13 | script: 14 | - nix-shell --run "meson setup '${MESON_BUILD_DIR}'" 15 | - nix-shell --run "meson compile -C '${MESON_BUILD_DIR}'" 16 | - nix-shell --run "meson test -C '${MESON_BUILD_DIR}'" 17 | - nix-shell --run "meson dist -C '${MESON_BUILD_DIR}' --include-subprojects" 18 | artifacts: 19 | name: "Tarball" 20 | paths: 21 | - "${TARBALL_ARTIFACT_DIR}/" 22 | 23 | build-nix-derivation: 24 | image: nixpkgs/nix:latest 25 | script: 26 | # Build the project including docs. 27 | - nix-build -A all 28 | - cp -r result-devdoc/share/gtk-doc/html/nautilus-python/ public/ 29 | artifacts: 30 | paths: 31 | - public/ 32 | 33 | pages: 34 | stage: deploy 35 | dependencies: 36 | - build-nix-derivation 37 | script: 38 | - echo "Re-using public artifact from build job" 39 | artifacts: 40 | paths: 41 | - public/ 42 | rules: 43 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 44 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | nautilus-python is written by 2 | Johan Dahlin 3 | 4 | Somewhat on nautilus-mono by: 5 | Dave Camp 6 | Calvin Gaisford 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Adam Plumb 2 | E-mail: adamplumb@gmail.com 3 | Userid: adamplumb 4 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ## 4.0.1 (2024-04-19) 2 | 3 | - Fixed build failure due to underlinking. (https://gitlab.gnome.org/GNOME/nautilus-python/-/merge_requests/13) 4 | - Fixed build failure with Python 3.13 (Patrick Monnerat) 5 | 6 | ## 4.0 (2022-09-17) 7 | 8 | - Updated migration docs and rewrote overview. 9 | 10 | ## 4.0.alpha (2022-08-12) 11 | 12 | - The extension was ported to Nautilus 43 (https://gitlab.gnome.org/GNOME/nautilus-python/-/merge_requests/11) 13 | - Notably, this removes any direct access to GTK widgets from the API. **Most scripts will need to be modified.** Please check the migration guide in the docs on how to update your scripts. 14 | - Switched to Meson build system (https://gitlab.gnome.org/GNOME/nautilus-python/-/merge_requests/10) 15 | - Removed support for Python 2 16 | - Fixed build with `-fno-common` (Andreas Henriksson) 17 | - Added missing parent constructor calls to examples 18 | - Decorated the examples with typehints 19 | - Fixed several memory leaks (Michael Webster) 20 | 21 | ## 1.2.3 (2019-07-17) 22 | 23 | - First release from gitlab 24 | - Fixed open-terminal example extension (Maximillian Schambach) 25 | - Fixed invalid DOAP file category (Andre Klapper) 26 | - Fixed bug https://gitlab.gnome.org/GNOME/nautilus-python/-/issues/3, setting `argv[0]` to thunar 27 | - Fixed bug https://gitlab.gnome.org/GNOME/nautilus-python/-/issues/4, allow overriding the build date to enable reproducible builds 28 | - Removed build warnings (Christian Stadelman) 29 | - Fix to work with python 3.8 (stratakis) 30 | 31 | ## 1.2.2 (2018-01-14) 32 | 33 | - Fixed [bug #792427](https://bugzilla.gnome.org/show_bug.cgi?id=792427), Fix folder string comparison to prevent duplicate extension loading 34 | 35 | ## 1.2.1 (2018-01-08) 36 | 37 | - Fixed [bug #792348](https://bugzilla.gnome.org/show_bug.cgi?id=792348), Fix syntax error when building docs using python3 38 | 39 | ## 1.2.0 (2018-01-02) 40 | 41 | - Added capability to compile with python 3 support using PYTHON environment variable 42 | - Fix [#781232](https://bugzilla.gnome.org/show_bug.cgi?id=781232), Improve extension path loading semantics to follow XDG recommendations 43 | - Fix [#791208](https://bugzilla.gnome.org/show_bug.cgi?id=791208), require Nautilus 3.0 before importing extensions to prevent warnings 44 | - Switch to using gtkdoc mkhtml for generating devhelp2 documentation 45 | 46 | ## 1.1.0 (2011-10-17) 47 | 48 | - Added pygobject3 compatibility. Retained pygobject 2.28.x compatibility. 49 | - Updated extension examples to support pygobject3. 50 | - Fixed [bug #660290](https://bugzilla.gnome.org/show_bug.cgi?id=660290). Updated the FSF address 51 | - Fixed [bug #660288](https://bugzilla.gnome.org/show_bug.cgi?id=660288). Fix autogen warnings with additional macro ACLOCAL_AMFLAGS 52 | - Fixed [bug #660287](https://bugzilla.gnome.org/show_bug.cgi?id=660287). Make the docdir not hard-coded 53 | - Fixed [bug #660286](https://bugzilla.gnome.org/show_bug.cgi?id=660286). Fixes m4 underquoting warning 54 | - Fixed [bug #660283](https://bugzilla.gnome.org/show_bug.cgi?id=660283). Fixes html docs installation issue 55 | - Fixed [bug #653169](https://bugzilla.gnome.org/show_bug.cgi?id=653169). Upated the COPYING file with the most recent GPLv2 license 56 | 57 | ## 1.0.0 (2011-04-17) 58 | 59 | - Use the gobject instrospection dynamic bindings, breaks compatibility for existing extensions. 60 | - For Nautilus3, the `get_toolbar_items` methods have been removed. (Only reporting that change, it isn't up to us). Should still work with Nautilus 2.x annotations. 61 | - No longer look in the old `~/.nautilus/python-extensions` or `/usr/lib/nautilus/extensions2.0/python` folders for extensions. Now look in `~/.local/share/nautilus-python/extensions` or `$XDG_DATA_DIR/nautilus-python/extensions`. Extensions should be arch-independent now. 62 | - General clean up of the repository, removed old .cvsignore files 63 | - Updated the gtk-doc documentation and examples to work with new dynamic bindings. 64 | 65 | ## 0.7.3 (2011-03-28) 66 | 67 | - Use `PyCapsule_Import` for pygobject as well as pygtk 68 | - Fixed bug [#644399](https://bugzilla.gnome.org/show_bug.cgi?id=644399), in the property page example plugin, get the md5sum of the file contents, not the file name 69 | - Removed extraneous `.cvsignore` files 70 | - Fixed the update-file-info-async example plugin 71 | 72 | ## 0.7.2 (2011-03-04) 73 | 74 | - Fix to get the PyCapsule patch to work even if there is no PyCapsule object set up for pygtk. 75 | 76 | ## 0.7.1 (2011-03-02) 77 | 78 | - Fixed AM_CHECK_PYTHON_LIBS usage of test (GNOME [bug #619440](https://bugzilla.gnome.org/show_bug.cgi?id=619440)) 79 | - Cleaned up the docs Makefile.am to make it more consistent 80 | - Fix some python3 syntax issues in the python.m4 script 81 | - Add python 2.7 support (GNOME [bug #633171](https://bugzilla.gnome.org/show_bug.cgi?id=633171)) 82 | 83 | ## 0.7.0 (2010-05-21) 84 | 85 | - Added methods: 86 | - `nautilus.InfoProvider.update_file_info_full` 87 | - `nautilus.InfoProvider.update_complete_invoke` 88 | - `nautilus.InfoProvider.cancel_update` 89 | - `nautilus.MenuProvider.get_file_items_full` 90 | - `nautilus.MenuProvider.get_background_items_full` 91 | - `nautilus.MenuProvider.get_toolbar_items_full` 92 | - `nautilus.MenuProvider.emit_items_updated_signal` 93 | - Added complete gtk-doc documentation: tutorial and reference. Enable by adding the `--enable-gtk-doc` argument to `./configure` or `./autogen.sh` 94 | - Updated the `autogen.sh` file so it is not a copied-over obsolete version of gnome-autogen.sh, but a shell that calls the user's installed gnome-autogen.sh. 95 | - Removed obsolete .spec file 96 | - Removed obsolete `examples/documentation.py` file 97 | - Added an example plugin for the LocationWidgetProvider 98 | - Look for python plugins in `$XDG_DATA_DIR/nautilus-python/extensions`. This includes `~/.local/share` and `/usr/share` (or whatever `$XDG_DATA_DIR` is set to) 99 | 100 | ## 0.6.1 (2010-01-19) 101 | 102 | - Look for `libpython2.6.so.1.0` instead of `libpython2.6.so`, the latter is general available from -devel packages only. 103 | - Re-added the missing spec and pc files 104 | - Free pygobject data directly after using file objects. Stops segfaults from occurring when nautilus wants to free pygobject data after `Py_Finalize()` is called. 105 | 106 | ## 0.6.0 (2010-01-15) 107 | 108 | - Remove eel dependency (Vincent Untz) 109 | - Sanitize python search path (Mark Lee) 110 | - Remove gnomevfs dependency and unused code (Daniel Holbach) 111 | - Load python from lib64 in 64 bit multilib distributions (Ted Toth) 112 | - Fix segfault when a plugin doesn't implement the `__init__` method 113 | - Added the `can_write`, `get_mount`, `get_file_type`, `get_location`, and `get_parent_location` methods to the `NautilusFileInfo` object 114 | - Added the `NautilusMenu.get_items` method 115 | - Updated some example plugins 116 | - Bug fix for some `PyThreadState_New` segmentation faults 117 | - Use the nautilus prefix by default for distfiles, rather than /usr 118 | - Require nautilus-2.22 and pygobject-2.16 now that we support GIO 119 | 120 | ## 0.5.2 (2010-03-01) 121 | 122 | - Look for libpython2.6.so.1.0 instead of libpython2.6.so, the latter is general available from -devel packages only. 123 | - Remove eel dependency (Vincent Untz) 124 | - Sanitize python search path (Mark Lee) 125 | - Load python from lib64 in 64 bit multilib distributions (Ted Toth) 126 | - Updated some example plugins 127 | - Added explicit gnome-vfs build dependency 128 | - Removed obsolete .spec file 129 | 130 | ## 0.5.1 (2008-09-16) 131 | 132 | - Add support for location widgets (Tim Cole) 133 | 134 | ## 0.5.0 (2008-01-11) 135 | 136 | - Add support for submenus (Lukáš Lalinský, Sylvain Baubeau, Erik Wien) 137 | - Make it work with nautilus 2.22 (Brian Pepple) 138 | - Initialize gnomevfs explicitly (Scott Tsai) 139 | - Compilable with Python 2.5 (Jonathan Rogers) 140 | - Shutdown Python (Gustavo Carneiro) 141 | - Initialize PyGObject/PyGTK/PyGNOMEVFS on demand (Gustavo Carneiro) 142 | 143 | ## 0.4.3 (2006-02-15) 144 | 145 | - Make debugging messages a run-time option 146 | - Fix bug in loading extensions from system extensions dir 147 | - Fix crash when no extensions were loaded 148 | 149 | ## 0.4.2 (2006-02-10) 150 | 151 | - Make it work again with nautilus ≥ 2.13.4 152 | - Minor bug fixes 153 | 154 | ## 0.4.1 (2005-09-22) 155 | 156 | (missing) 157 | 158 | ## 0.4.0 (2005-05-30) 159 | 160 | First public release 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nautilus-python 2 | 3 | This is an extension for Nautilus that allows further extending it with Python scripts with the help of Nautilus’s GObject API. 4 | 5 | Documentation, including an API reference, is available through the [Devhelp app](https://apps.gnome.org/app/org.gnome.Devhelp/), if you have `nautilus-python` package installed, or [on-line](https://gnome.pages.gitlab.gnome.org/nautilus-python/). For sample extensions, check the `examples/` subdirectory in the source distribution. Note that some distros move the documentation into a separate package. 6 | 7 | ## Requirements 8 | 9 | - Nautilus ≥ 43.beta 10 | - Python 3.x 11 | - PyGObject 3 12 | 13 | ## Running Extensions 14 | 15 | Scripts are loaded in the following order: 16 | 17 | 1. `$XDG_DATA_HOME/nautilus-python/extensions` (i.e. `~/.local/share/…`) 18 | 2. `nautilus_prefix/share/nautilus-python/extensions` (i.e. `~/Development/…`) 19 | 3. `$XDG_DATA_DIRS/nautilus-python/extensions` (i.e. `/usr/share/…`) 20 | 21 | Simply copy your Python scripts into one of those directories and restart Nautilus. 22 | 23 | ## Issues 24 | 25 | It is currently not possible to reload a Python script without restarting Nautilus. 26 | 27 | Run Nautilus with the `NAUTILUS_PYTHON_DEBUG=misc` environment variable to print out debug information. 28 | 29 | ## License 30 | 31 | nautilus-python is released under the terms of the GNU General Public License, either version 2.0 or, at your option, any later version. 32 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | How to use? 4 | *********** 5 | 6 | If you have Nix installed, you can get an environment with everything 7 | needed to compile nautilus-python by running: 8 | 9 | $ nix-shell 10 | 11 | at the root of the nautilus-python repository. 12 | 13 | You can also compile nautilus-python and ‘install’ it by running: 14 | 15 | $ nix-build 16 | 17 | at the root of the nautilus-python repository. The command will install 18 | nautilus-python to a location under Nix store and create a ‘result’ symlink 19 | in the current directory pointing to the in-store location. 20 | 21 | The dependencies are pinned, you can update them to latest versions with: 22 | 23 | $ nix-shell --run 'niv update' 24 | 25 | How to tweak default arguments? 26 | ******************************* 27 | 28 | Nix supports the ‘--arg’ option (see nix-build(1)) that allows you 29 | to override the top-level arguments. 30 | 31 | For instance, to use your local copy of Nixpkgs: 32 | 33 | $ nix-build --arg pkgs "import $HOME/Projects/nixpkgs {}" 34 | 35 | Or to speed up the build by not running the test suite: 36 | 37 | $ nix-build --arg doCheck false 38 | 39 | */ 40 | 41 | { 42 | # Nixpkgs instance, will default to one from Niv. 43 | pkgs ? null, 44 | # Whether to run tests when building File Roller using nix-build. 45 | doCheck ? true, 46 | # Whether to build File Roller, or shell for developing it. 47 | # We do not use lib.inNixShell because that would also apply 48 | # when in a nix-shell of some package depending on this one. 49 | shell ? false, 50 | } @ args: 51 | 52 | let 53 | # Pinned Nix dependencies (e.g. Nixpkgs) managed by Niv. 54 | sources = import ./nix/sources.nix; 55 | 56 | # Setting pkgs to the pinned version 57 | # when not overridden in args. 58 | pkgs = 59 | if args.pkgs or null == null 60 | then 61 | import sources.nixpkgs { 62 | overlays = []; 63 | config = {}; 64 | } 65 | else args.pkgs; 66 | 67 | inherit (pkgs) lib; 68 | 69 | # Function for building File Roller or shell for developing it. 70 | makeDerivation = 71 | if shell 72 | then pkgs.mkShell 73 | else pkgs.stdenv.mkDerivation; 74 | in 75 | makeDerivation rec { 76 | name = "nautilus-python"; 77 | 78 | outputs = [ "out" "devdoc" ]; 79 | 80 | src = 81 | let 82 | # Do not copy to the store paths listed in .gitignore files 83 | cleanSource = path: pkgs.nix-gitignore.gitignoreFilterRecursiveSource (_: _: true) [ ] path; 84 | in 85 | if shell then null else cleanSource ./.; 86 | 87 | # Dependencies for build platform 88 | nativeBuildInputs = with pkgs; ([ 89 | pkg-config 90 | meson 91 | ninja 92 | gtk-doc 93 | docbook-xsl-nons 94 | docbook_xml_dtd_412 95 | ] ++ lib.optionals shell [ 96 | niv 97 | ]); 98 | 99 | # Dependencies for host platform 100 | buildInputs = with pkgs; [ 101 | python3 102 | python3.pkgs.pygobject3 103 | nautilus 104 | ]; 105 | 106 | inherit doCheck; 107 | } 108 | -------------------------------------------------------------------------------- /docs/reference/entities.docbook.in: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/reference/meson.build: -------------------------------------------------------------------------------- 1 | entities = configure_file( 2 | input: 'entities.docbook.in', 3 | output: 'entities.docbook', 4 | configuration: { 5 | 'VERSION': meson.project_version(), 6 | }, 7 | ) 8 | 9 | gnome.gtkdoc( 10 | 'nautilus-python', 11 | main_xml: 'nautilus-python-ref.xml', 12 | src_dir: 'src', 13 | content_files: [ 14 | entities, 15 | 'nautilus-python-class-reference.xml', 16 | 'nautilus-python-column-provider.xml', 17 | 'nautilus-python-column.xml', 18 | 'nautilus-python-enum-reference.xml', 19 | 'nautilus-python-file-info.xml', 20 | 'nautilus-python-info-provider.xml', 21 | 'nautilus-python-menu-item.xml', 22 | 'nautilus-python-menu-provider.xml', 23 | 'nautilus-python-menu.xml', 24 | 'nautilus-python-migrating-to-4.xml', 25 | 'nautilus-python-operation-result.xml', 26 | 'nautilus-python-overview-example.xml', 27 | 'nautilus-python-overview-methods.xml', 28 | 'nautilus-python-overview.xml', 29 | 'nautilus-python-properties-item.xml', 30 | 'nautilus-python-properties-model-provider.xml', 31 | 'nautilus-python-properties-model.xml', 32 | 'nautilus-python-provider-reference.xml', 33 | '../../examples/TestExtension.py', 34 | '../../examples/background-image.py', 35 | '../../examples/block-size-column.py', 36 | '../../examples/md5sum-properties-model.py', 37 | '../../examples/open-terminal.py', 38 | '../../examples/submenu.py', 39 | '../../examples/update-file-info-async.py', 40 | ], 41 | install: true, 42 | ) 43 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-class-reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | Available Classes 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-column-provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.ColumnProvider 8 | 9 | 10 | Nautilus.ColumnProvider 11 | Nautilus.ColumnProvider Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.ColumnProvider 23 | 24 | 25 | get_columns 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Description 37 | 38 | 39 | If subclassed, Nautilus will request a list of Nautilus.Column 40 | objects, which are then displayed when the user is in List mode. 41 | 42 | An extension sub-classing Nautilus.ColumnProvider 43 | will almost always want to sub-class Nautilus.InfoProvider 44 | as well, since that is how an extension provides information for each item in a directory listing. 45 | 46 | 47 | 48 | 49 | Nautilus.ColumnProvider Example 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Passive Methods 61 | 62 | 63 | Nautilus.ColumnProvider.get_columns 64 | 65 | 66 | get_columns 67 | 68 | 69 | 70 | 71 | 72 | Returns : 73 | a list of Nautilus.Column 74 | 75 | 76 | 77 | 78 | The get_columns() method returns a list of 79 | Nautilus.Column. 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-column.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.Column 8 | 9 | 10 | Nautilus.Column 11 | Nautilus.Column Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.Column 23 | gobject.GObject 24 | 25 | 26 | Nautilus.Column 27 | name 28 | attribute 29 | label 30 | description 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Ancestry 41 | 42 | +-- gobject.GObject 43 | +-- Nautilus.Column 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Description 54 | 55 | 56 | A list of Nautilus.Column objects is returned by Nautilus.ColumnProvider extensions. 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Properties 68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | "attribute" 79 | The attribute name to display. Default value: None 80 | Read-Write 81 | 82 | 83 | 84 | "attribute-q" 85 | The attribute name to display, in quark form. Default value: 0 86 | Read 87 | 88 | 89 | 90 | "description" 91 | The user-visible description of the column. Default value: None 92 | Read-Write 93 | 94 | 95 | 96 | "label" 97 | The label to display in the column. Default value: None 98 | Read-Write 99 | 100 | 101 | 102 | "name" 103 | The name of the column. Default value: None 104 | Read-Write-ConstructOnly 105 | 106 | 107 | 108 | "xalign" 109 | The x-alignment of the column. Allowed values: [0,1]. Default value: 0 110 | Read-Write 111 | 112 | 113 | 114 | 115 | 116 |
117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | Constructor 126 | 127 | 128 | Nautilus.Column 129 | name 130 | attribute 131 | label 132 | description 133 | 134 | 135 | 136 | 137 | name : 138 | identifier of the column 139 | 140 | 141 | attribute : 142 | the file attribute to be displayed in the column 143 | 144 | 145 | label : 146 | the user-visible label for the column 147 | 148 | 149 | description : 150 | a user-visible description of the column 151 | 152 | 153 | 154 | 155 | Creates a new Nautilus.Column object. 156 | 157 | 158 | 159 | 160 | 161 |
162 | 163 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-enum-reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | Available Enums 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-file-info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.FileInfo 8 | 9 | 10 | Nautilus.FileInfo 11 | Nautilus.FileInfo Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.FileInfo 23 | gobject.GInterface 24 | 25 | 26 | is_gone 27 | 28 | 29 | 30 | 31 | get_file_type 32 | 33 | 34 | 35 | 36 | get_location 37 | 38 | 39 | 40 | 41 | get_name 42 | 43 | 44 | 45 | 46 | get_uri 47 | 48 | 49 | 50 | 51 | get_activation_uri 52 | 53 | 54 | 55 | 56 | get_parent_info 57 | 58 | 59 | 60 | 61 | get_parent_location 62 | 63 | 64 | 65 | 66 | get_parent_uri 67 | 68 | 69 | 70 | 71 | get_mount 72 | 73 | 74 | 75 | 76 | get_uri_scheme 77 | 78 | 79 | 80 | 81 | get_mime_type 82 | 83 | 84 | 85 | 86 | is_mime_type 87 | mime_type 88 | 89 | 90 | 91 | is_directory 92 | 93 | 94 | 95 | 96 | can_write 97 | 98 | 99 | 100 | 101 | add_emblem 102 | emblem_name 103 | 104 | 105 | 106 | get_string_attribute 107 | attribute_name 108 | 109 | 110 | 111 | add_string_attribute 112 | attribute_name 113 | value 114 | 115 | 116 | 117 | invalidate_extension_info 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | Ancestry 130 | 131 | +-- gobject.GInterface 132 | +-- Nautilus.FileInfo 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | Description 143 | 144 | 145 | Nautilus.FileInfo objects are passed 146 | to extensions by the Nautilus.InfoProvider. 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | Public Methods 158 | 159 | 160 | Nautilus.FileInfo.is_gone 161 | 162 | is_gone 163 | 164 | 165 | 166 | Returns : 167 | whether the file still exists 168 | 169 | 170 | 171 | 172 | 173 | Nautilus.FileInfo.get_file_type 174 | 175 | get_file_type 176 | 177 | 178 | 179 | Returns : 180 | the gio.FileType associated with the file 181 | 182 | 183 | 184 | 185 | 186 | Nautilus.FileInfo.get_location 187 | 188 | get_location 189 | 190 | 191 | 192 | Returns : 193 | the gio.File associated with the file 194 | 195 | 196 | 197 | 198 | 199 | Nautilus.FileInfo.get_name 200 | 201 | get_name 202 | 203 | 204 | 205 | Returns : 206 | the basename of the file 207 | 208 | 209 | 210 | 211 | 212 | Nautilus.FileInfo.get_uri 213 | 214 | get_uri 215 | 216 | 217 | 218 | Returns : 219 | the uri of the file 220 | 221 | 222 | 223 | 224 | 225 | Nautilus.FileInfo.get_activation_uri 226 | 227 | get_activation_uri 228 | 229 | 230 | 231 | Returns : 232 | the activation uri of the file 233 | 234 | 235 | 236 | 237 | 238 | Nautilus.FileInfo.get_parent_info 239 | 240 | get_parent_info 241 | 242 | 243 | 244 | Returns : 245 | the Nautilus.FileInfo object associated with the file's parent 246 | 247 | 248 | 249 | 250 | 251 | Nautilus.FileInfo.get_parent_location 252 | 253 | get_parent_location 254 | 255 | 256 | 257 | Returns : 258 | the gio.File associated with the file's parent location 259 | 260 | 261 | 262 | 263 | 264 | Nautilus.FileInfo.get_parent_uri 265 | 266 | get_parent_uri 267 | 268 | 269 | 270 | Returns : 271 | the uri of the file's parent 272 | 273 | 274 | 275 | 276 | 277 | Nautilus.FileInfo.get_mount 278 | 279 | get_mount 280 | 281 | 282 | 283 | Returns : 284 | the gio.GMount associated with the file, if it exists 285 | 286 | 287 | 288 | 289 | 290 | Nautilus.FileInfo.get_uri_scheme 291 | 292 | get_uri_scheme 293 | 294 | 295 | 296 | Returns : 297 | the uri scheme of the file 298 | 299 | 300 | 301 | 302 | 303 | Nautilus.FileInfo.get_mime_type 304 | 305 | get_mime_type 306 | 307 | 308 | 309 | Returns : 310 | the mimetype of the file 311 | 312 | 313 | 314 | 315 | 316 | Nautilus.FileInfo.is_mime_type 317 | 318 | is_mime_type 319 | mimetype 320 | 321 | 322 | 323 | mimetype : 324 | a mimetype string (i.e. "text/plain") 325 | 326 | 327 | Returns : 328 | whether the file's mimetype string matches the provided mimetype string 329 | 330 | 331 | 332 | 333 | 334 | Nautilus.FileInfo.is_directory 335 | 336 | is_directory 337 | 338 | 339 | 340 | Returns : 341 | whether the item is a directory 342 | 343 | 344 | 345 | 346 | 347 | Nautilus.FileInfo.can_write 348 | 349 | can_write 350 | 351 | 352 | 353 | Returns : 354 | whether the file is writeable 355 | 356 | 357 | 358 | 359 | 360 | Nautilus.FileInfo.add_emblem 361 | 362 | add_emblem 363 | emblem_name 364 | 365 | 366 | 367 | emblem_name : 368 | the name of an emblem to add 369 | 370 | 371 | 372 | 373 | 374 | Nautilus.FileInfo.get_string_attribute 375 | 376 | get_string_attribute 377 | attribute_name 378 | 379 | 380 | 381 | attribute_name : 382 | a string attribute name 383 | 384 | 385 | Returns : 386 | the value associated with the file attribute 387 | 388 | 389 | 390 | 391 | 392 | Nautilus.FileInfo.add_string_attribute 393 | 394 | add_string_attribute 395 | attribute_name 396 | value 397 | 398 | 399 | 400 | attribute_name : 401 | a string attribute name 402 | 403 | 404 | value : 405 | an attribute value 406 | 407 | 408 | 409 | 410 | 411 | Nautilus.FileInfo.invalidate_extension_info 412 | 413 | invalidate_extension_info 414 | 415 | Invalidates the information Nautilus has about this file, which causes it to request new information 416 | from its Nautilus.InfoProvider providers. 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-info-provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.InfoProvider 8 | 9 | 10 | Nautilus.InfoProvider 11 | Nautilus.InfoProvider Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.InfoProvider 23 | 24 | 25 | update_file_info 26 | file 27 | 28 | 29 | 30 | update_file_info_full 31 | provider 32 | handle 33 | closure 34 | file 35 | 36 | 37 | 38 | cancel_update 39 | provider 40 | handle 41 | 42 | 43 | 44 | Nautilus.info_provider_update_complete_invoke 45 | provider 46 | handle 47 | closure 48 | resultNautilus.OperationResult.COMPLETE 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Description 59 | 60 | 61 | If subclassed, Nautilus will call update_file_info(_full) to notify extensions of which 62 | files are being viewed by the user. This gives extensions an opportunity to invoke actions on the files, 63 | or to add emblems or attributes. 64 | 65 | 66 | 67 | Nautilus.InfoProvider Example 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Passive Methods 79 | 80 | 81 | Nautilus.InfoProvider.update_file_info 82 | 83 | 84 | update_file_info 85 | file 86 | 87 | 88 | 89 | 90 | file : 91 | a Nautilus.FileInfo object 92 | 93 | 94 | 95 | 96 | This method is called by Nautilus for each file or folder that exists under the 97 | current directory listing. There is no return value. 98 | 99 | 100 | 101 | 102 | 103 | Nautilus.InfoProvider.update_file_info_full 104 | 105 | 106 | update_file_info_full 107 | provider 108 | handle 109 | closure 110 | file 111 | 112 | 113 | 114 | 115 | provider : 116 | the current Nautilus.InfoProvider instance 117 | 118 | 119 | handle : 120 | a gobject.gpointer generated solely to track this call 121 | 122 | 123 | closure : 124 | a C Closure that must be passed to Nautilus.info_provider_update_complete_invoke if that method is called 125 | 126 | 127 | file : 128 | a Nautilus.FileInfo object 129 | 130 | 131 | Returns : 132 | None or a Nautilus.OperationResult enum 133 | 134 | 135 | 136 | 137 | This method is called by Nautilus for each file or folder that exists under the 138 | current directory listing. Originally, Nautilus.InfoProvider 139 | only provided the update_file_info 140 | method, which blocked Nautilus when the method required a lot of computation time. This method was 141 | created to allow an extension to tell Nautilus that it will be spending time on an operation and that 142 | Nautilus should not block itself during that time. 143 | 144 | 145 | In order to notify Nautilus of your extension's intentions, you must return a 146 | Nautilus.OperationResult enum. 147 | Then, when the operation has completed, call the Nautilus.info_provider_update_complete_invoke method, passing the provider, 148 | handle and closure variables as parameters. 149 | 150 | 151 | This method was created for backwards compatibility reasons. If your 152 | extension used the update_file_info method and you want non-blocking 153 | usage, you should switch to this method. 154 | 155 | 156 | This method was introduced in nautilus-python 0.7.0. 157 | 158 | 159 | 160 | 161 | 162 | Nautilus.InfoProvider.cancel_update 163 | 164 | 165 | cancel_update 166 | provider 167 | handle 168 | 169 | 170 | 171 | 172 | provider : 173 | the current Nautilus.InfoProvider instance 174 | 175 | 176 | handle : 177 | a gobject.gpointer generated by a specific update_file_info_full call 178 | 179 | 180 | 181 | 182 | This method is called by Nautilus when an update_file_info_full call is in progress 183 | but is no longer required. This may happen because the user is moving directories or a file 184 | has been deleted, etc. You may use the handle parameter here to match the 185 | handle parameter passed in update_file_info_full. 186 | 187 | 188 | This method was introduced in nautilus-python 0.7.0. 189 | 190 | 191 | 192 | 193 | 194 | Active Methods 195 | 196 | 197 | Nautilus.info_provider_update_complete_invoke 198 | 199 | 200 | info_provider_update_complete_invoke 201 | provider 202 | handle 203 | closure 204 | resultNautilus.OperationResult.COMPLETE 205 | 206 | 207 | 208 | 209 | provider : 210 | the current Nautilus.InfoProvider instance 211 | 212 | 213 | handle : 214 | a gobject.gpointer generated by a specific update_file_info_full call 215 | 216 | 217 | closure : 218 | a C Closure that must be passed to Nautilus.info_provider_update_complete_invoke if that method is called 219 | 220 | 221 | result : 222 | an optional parameter. If left out, Nautilus.OperationResult.COMPLETE is assumed. 223 | Otherwise, you may pass any any of the Nautilus.OperationResult enums. 224 | 225 | 226 | 227 | 228 | An extension must call this method for each update_file_info_full method that 229 | returns the Nautilus.OperationResult.IN_PROGRESS enum. 230 | The method must be called with the provider, handle, and closure parameters which were passed to the earlier update_file_info_full method. 231 | 232 | 233 | This method was introduced in nautilus-python 0.7.0. 234 | 235 | 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-menu-item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.MenuItem 8 | 9 | 10 | Nautilus.MenuItem 11 | Nautilus.MenuItem Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.MenuItem 23 | gobject.GObject 24 | 25 | 26 | Nautilus.MenuItem 27 | name 28 | label 29 | tooltip 30 | icon 31 | 32 | 33 | 34 | activate 35 | 36 | 37 | 38 | set_submenu 39 | menu 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Ancestry 51 | 52 | +-- gobject.GObject 53 | +-- Nautilus.MenuItem 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Description 64 | 65 | 66 | Nautilus.MenuItem objects are appended to lists to create menus and submenus. 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Properties 78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | "icon" 89 | Name of the icon to display in the menu item. Default value: None 90 | Read-Write 91 | 92 | 93 | 94 | "label" 95 | The label to display to the user. Default value: None 96 | Read-Write 97 | 98 | 99 | 100 | "menu" 101 | The Nautilus.Menu menu object belonging to this item. May be None. 102 | Read-Write 103 | 104 | 105 | 106 | "name" 107 | The name of the item. Default value: None 108 | Read-Write-ConstructOnly 109 | 110 | 111 | 112 | "priority" 113 | Whether or not to show priority text in toolbars. Default value: True 114 | Read-Write 115 | 116 | 117 | 118 | "sensitive" 119 | Whether or not the menu item is sensitive. Default value: True 120 | Read-Write 121 | 122 | 123 | 124 | "tip" 125 | The tooltip for the menu item. Default value: None 126 | Read-Write 127 | 128 | 129 | 130 | 131 | 132 |
133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | Signals 142 | 143 | 144 | "activate" 145 | 146 | callback 147 | item 148 | user_param1 149 | ... 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Constructor 165 | 166 | 167 | Nautilus.MenuItem 168 | name 169 | label 170 | tooltip 171 | icon 172 | 173 | 174 | 175 | 176 | name : 177 | identifier of the item 178 | 179 | 180 | label : 181 | the user-visible label for the item 182 | 183 | 184 | tooltip : 185 | the user-visible tooltip for the item 186 | 187 | 188 | icon : 189 | Name of the icon to display in the item 190 | 191 | 192 | 193 | 194 | Creates a new Nautilus.MenuItem object. 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | Public Methods 205 | 206 | 207 | Nautilus.MenuItem.activate 208 | 209 | activate 210 | 211 | 212 | 213 | Generates the "activate" signal for this Nautilus.MenuItem. 214 | 215 | 216 | 217 | 218 | Nautilus.MenuItem.set_submenu 219 | 220 | set_submenu 221 | menu 222 | 223 | 224 | 225 | menu : 226 | a Nautilus.Menu 227 | 228 | 229 | 230 | Attaches a Nautilus.Menu as the submenu for this Nautilus.MenuItem. 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | Signal Details 242 | 243 | 244 | The "activate" Nautilus.MenuItem Signal 245 | 246 | 247 | callback 248 | item 249 | user_param1 250 | ... 251 | 252 | 253 | 254 | 255 | item : 256 | the Nautilus.MenuItem being activated 257 | 258 | 259 | user_param1 : 260 | User-defined parameter the user attaches to the signal connector 261 | 262 | 263 | ... : 264 | Additional parameter(s) the user attaches to the signal connector 265 | 266 | 267 | 268 | 269 | Emits the "activate" signal. 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 |
278 | 279 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-menu-provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.MenuProvider 8 | 9 | 10 | Nautilus.MenuProvider 11 | Nautilus.MenuProvider Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.MenuProvider 23 | 24 | 25 | get_file_items 26 | files 27 | 28 | 29 | 30 | get_file_items_full 31 | provider 32 | files 33 | 34 | 35 | 36 | get_background_items 37 | folder 38 | 39 | 40 | 41 | get_background_items_full 42 | provider 43 | folder 44 | 45 | 46 | 47 | Nautilus.menu_provider_emit_items_updated_signal 48 | provider 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Description 59 | 60 | 61 | If subclassed, Nautilus will request a list of Nautilus.MenuItem objects, 62 | which are then attached to various menus. Nautilus expects at least one of 63 | the following methods to be defined (or their *_full variants): get_file_items or 64 | get_background_items. 65 | 66 | The get_toolbar_items methods were removed in nautilus-python 1.0 because they were removed from Nautilus 3. Technically, you should still be 67 | able to call those methods with nautilus-python 1.0 if you are running Nautilus 2.x with annotations. 68 | 69 | 70 | 71 | Nautilus.MenuProvider Example 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Signals 82 | 83 | 84 | "items-updated" 85 | 86 | callback 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Passive Methods 102 | 103 | 104 | Nautilus.MenuProvider.get_file_items 105 | 106 | 107 | get_file_items 108 | files 109 | 110 | 111 | 112 | 113 | menu : 114 | a list of Nautilus.FileInfo objects. 115 | 116 | 117 | Returns : 118 | a list of Nautilus.MenuItem objects 119 | 120 | 121 | 122 | 123 | The get_file_items() method returns a list of 124 | Nautilus.MenuItem objects. 125 | 126 | 127 | 128 | 129 | Nautilus.MenuProvider.get_file_items_full 130 | 131 | 132 | get_file_items_full 133 | provider 134 | files 135 | 136 | 137 | 138 | 139 | provider : 140 | the current Nautilus.MenuProvider instance 141 | 142 | 143 | files : 144 | a list of Nautilus.FileInfo objects. 145 | 146 | 147 | Returns : 148 | a list of Nautilus.MenuItem objects 149 | 150 | 151 | 152 | 153 | The get_file_items_full() method returns a list of 154 | Nautilus.MenuItem objects. 155 | 156 | 157 | This method was created in order to allow extension writers to call the 158 | Nautilus.menu_provider_emit_items_updated_signal, which must 159 | be passed the current provider instance. 160 | 161 | 162 | This method was introduced in nautilus-python 0.7.0. 163 | 164 | 165 | 166 | 167 | 168 | Nautilus.MenuProvider.get_background_items 169 | 170 | 171 | get_background_items 172 | folder 173 | 174 | 175 | 176 | 177 | folder : 178 | the current folder, as a Nautilus.FileInfo object. 179 | 180 | 181 | Returns : 182 | a list of Nautilus.MenuItem objects 183 | 184 | 185 | 186 | 187 | The get_background_items() method returns a list of 188 | Nautilus.MenuItem objects. 189 | 190 | 191 | 192 | 193 | Nautilus.MenuProvider.get_background_items_full 194 | 195 | 196 | get_background_items_full 197 | provider 198 | folder 199 | 200 | 201 | 202 | 203 | provider : 204 | the current Nautilus.MenuProvider instance 205 | 206 | 207 | folder : 208 | the current folder, as a Nautilus.FileInfo object. 209 | 210 | 211 | Returns : 212 | a list of Nautilus.MenuItem objects 213 | 214 | 215 | 216 | 217 | The get_background_items_full() method returns a list of 218 | Nautilus.MenuItem objects. 219 | 220 | 221 | This method was created in order to allow extension writers to call the 222 | Nautilus.menu_provider_emit_items_updated_signal, which must 223 | be passed the current provider instance. 224 | 225 | 226 | This method was introduced in nautilus-python 0.7.0. 227 | 228 | 229 | 230 | 231 | 232 | 233 | Active Methods 234 | 235 | 236 | Nautilus.menu_provider_emit_items_updated_signal 237 | 238 | 239 | menu_provider_emit_items_updated_signal 240 | provider 241 | 242 | 243 | 244 | 245 | provider : 246 | the current Nautilus.MenuProvider instance 247 | 248 | 249 | 250 | 251 | Emits the "items-updated" signal. 252 | 253 | 254 | This method was introduced in nautilus-python 0.7.0. 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | Signal Details 267 | 268 | 269 | The "items-updated" Nautilus.MenuProvider Signal 270 | 271 | 272 | Emits the "items-updated" signal. 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.Menu 8 | 9 | 10 | Nautilus.Menu 11 | Nautilus.Menu Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.Menu 23 | gobject.GObject 24 | 25 | 26 | Nautilus.Menu 27 | 28 | 29 | 30 | append_item 31 | item 32 | 33 | 34 | 35 | get_items 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Ancestry 47 | 48 | +-- gobject.GObject 49 | +-- Nautilus.Menu 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Description 60 | 61 | 62 | A Nautilus.Menu object allows an extension to create sub-menus. 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Constructor 75 | 76 | 77 | Nautilus.Menu 78 | 79 | 80 | 81 | Creates a new Nautilus.Menu object. 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | Public Methods 93 | 94 | 95 | Nautilus.Menu.append_item 96 | 97 | append_item 98 | item 99 | 100 | 101 | 102 | item : 103 | a Nautilus.MenuItem 104 | 105 | 106 | 107 | 108 | Append a Nautilus.MenuItem to a Nautilus.Menu. 109 | 110 | 111 | 112 | 113 | Nautilus.Menu.get_items 114 | 115 | get_items 116 | 117 | 118 | Returns a list of Nautilus.MenuItem objects attached to the Nautilus.Menu. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-migrating-to-4.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Migrating to Nautilus API 4.0 7 | 8 | Nautilus 43 no longer allows extensions to directly manipulate GTK widgets – all UI changes now need to happen through model objects. If your script implements any of the following provider interfaces, you will need to update it: 9 | 10 | 11 | LocationWidgetProvider 12 | The Nautilus.LocationWidgetProvider was removed without replacement. If your script requires it, you can request a new model-based API for your specific use case on the Nautilus issue tracker. 13 | 14 | 15 | 16 | MenuProvider 17 | The get_file_items, get_file_items_full, get_background_items a get_background_items_full methods of Nautilus.MenuProvider no longer take the window argument. Remove it from your implementations. 18 | If you need to keep supporting older versions of Nautilus, you can use variadic arguments: 19 | 20 | def get_file_items(self, *args): 21 | # `args` will be `[files: List[Nautilus.FileInfo]]` in Nautilus 4.0 API, 22 | # and `[window: Gtk.Widget, files: List[Nautilus.FileInfo]]` in Nautilus 3.0 API. 23 | files = args[-1] 24 | 25 | 26 | 27 | 28 | PropertyPageProvider 29 | The Nautilus.PropertyPageProvider was replaced by Nautilus.PropertiesModelProvider class. Unlike the previous unrestricted property pages that could contain any GTK widget, the new model-based interface currently only supports a pre-defined layout. Scripts can add pages, each of which can display a list of text properties. 30 | Subclass the Nautilus.PropertiesModelProvider abstract class and have the get_models method return the list of Nautilus.PropertiesModel, one for each properties page. 31 | If you need to keep supporting older versions of Nautilus, you can keep the old definition conditionally: 32 | 33 | if hasattr(Nautilus, "PropertiesModelProvider"): 34 | # For Nautilus 4.0 API 35 | class MD5SumPropertiesModel(GObject.GObject, Nautilus.PropertiesModelProvider): 36 | ... 37 | else: 38 | class MD5SumPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider): 39 | ... 40 | 41 | 42 | 43 | 44 | Direct use of GTK and other libraries 45 | If you use GTK directly, e.g. to create dialogue windows, you will need to upgrade the code to GTK 4 as well, since Nautilus switched to GTK 4 and it is not possible to use multiple versions of GTK in the same process. Do not forget to replace gi.require_version("Gtk", "3.0") with gi.require_version("Gtk", "4.0"), then. 46 | The other option is moving the window and dialogues into a separate program and having the extension just launch it. But unless the code base is very large, migrating to GTK 4 will probably be easier. 47 | 48 | 49 | 50 | More information 51 | See the relevant Nautilus change for more context and the corresponding nautilus-python update for more migration examples. 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-operation-result.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.OperationResult 8 | 9 | 10 | Nautilus.OperationResult 11 | Nautilus.OperationResult Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Description 22 | 23 | 24 | The Nautilus.OperationResult constants are 25 | used by the Nautilus.InfoProvider provider to 26 | notify Nautilus what the extension intends to do when Nautilus calls the extension's update_file_info_full 27 | method. The possible values are as follows: 28 | 29 | 30 | 31 | 32 | 33 | Nautilus.OperationResult.COMPLETE 34 | 35 | The operation is complete and Nautilus can move on to the next update_file_info_full call. 36 | 37 | 38 | 39 | Nautilus.OperationResult.IN_PROGRESS 40 | 41 | The operation is in progress and running asynchronously and Nautilus should wait until the Nautilus.info_provider_update_complete_invoke method 42 | is called before moving on to the next update_file_info_full call. 43 | 44 | 45 | 46 | Nautilus.OperationResult.FAILED 47 | 48 | The operation has failed. 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-overview-example.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | A Simple Extension 8 | 9 | Create an empty file with the following code: 10 | 11 | 12 | A Simple Extension 13 | 14 | 15 | 16 | Save this file as TestExtension.py in the ~/.local/share/nautilus-python/extensions folder. 17 | You may need to create this folder. To run, simply restart Nautilus. 18 | 19 | Once Nautilus restarts, right-click on a file and you should see a new menu item, 20 | "Showing #filename#". It is as simple as that! 21 | 22 | As mentioned above, in order to 23 | get loaded by Nautilus, a python extension must import the Nautilus module from gi.repository, 24 | create a class derived from a nautilus *Provider and a gobject.GObject, and create the methods that 25 | will be called by Nautilus when it requests information from its providers. 26 | In this case, when someone right-clicks on a file, Nautilus will ask all of its 27 | MenuProviders for additional menu items to show the user. When folders or files are clicked, 28 | the get_file_items method is called and a list of Nautilus.MenuItems is expected. 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-overview-methods.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | Explanation of Passive/Active Methods 9 | 10 | Because nautilus-python is an interface to a Nautilus' extension interface rather than a true library, it is 11 | rather quirky in how it works. One example of this is that several providers have additional public methods 12 | that an extension actively calls, rather than the extension defining and the method in their class that is called by 13 | Nautilus. You can see this with the Nautilus.menu_provider_emit_items_updated_signal 14 | and Nautilus.info_provider_update_complete_invoke methods, which 15 | the extension actively calls, passing the provider instance as a parameter. 16 | 17 | 18 | Due to this confusion, I have termed these actively-called methods Active Methods and the methods 19 | called by Nautilus are termed Passive Methods. 20 | 21 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-overview.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Overview 7 | 8 | Writing a Nautilus-Python extension is a fairly straight-forward process. One simply imports the Nautilus module from the GObject Introspection repository and creates a class which is derived from a GObject.GObject and one or more of the Nautilus module’s abstract classes. When an extension derives a class, it becomes a “provider”, telling Nautilus to ask it for information. 9 | 10 | There are several types of providers available for extensions to use, all of which will be explained in more detail in later chapters: 11 | 12 | 13 | ColumnProvider 14 | InfoProvider 15 | MenuProvider 16 | PropertiesModelProvider 17 | 18 | 19 | Your class can be derived from multiple providers. 20 | 21 | Then the script needs to be installed to one of the paths nautilus-python looks for extensions to. That will be nautilus-python/extensions subdirectory of one of the following data directories: 22 | 23 | the path in XDG_DATA_HOME environment variable , falling back to $HOME/.local/share if not set 24 | datadir configured when building nautilus-python (such as /usr/local/share) 25 | Any path listed in the XDG_DATA_DIRS environment variable 26 | 27 | 28 | After that, you will need to (re)start Nautilus, which will loads the nautilus-python C extension, which in turn will load all python extensions it can find. 29 | 30 | 31 | Do not forget to have the extension class derive from a GObject.GObject in addition to the standard Nautilus classes. 32 | 33 | 34 | 35 | For now, some Nautilus class constructors require passing named arguments instead of a standard argument list. This requirement may go away at some point. 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-properties-item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Nautilus.PropertiesItem 7 | 8 | 9 | Nautilus.PropertiesItem 10 | Nautilus.PropertiesItem Reference 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Synopsis 19 | 20 | 21 | Nautilus.PropertiesItem 22 | gobject.GObject 23 | 24 | 25 | Nautilus.PropertiesItem 26 | name 27 | value 28 | 29 | 30 | 31 | get_name 32 | 33 | 34 | 35 | get_value 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Ancestry 46 | 47 | +-- gobject.GObject 48 | +-- Nautilus.PropertiesItem 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Description 59 | 60 | 61 | Nautilus.PropertiesItem is an object that describes a name & value pair in file properties. Extensions can provide Nautilus.PropertiesItem objects grouped in Nautilus.PropertiesModel. 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Properties 73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | "name" 84 | The user-visible name for the properties item 85 | Read-Write-ConstructOnly 86 | 87 | 88 | 89 | "value" 90 | The user-visible value for the properties item 91 | Read-Write-ConstructOnly 92 | 93 | 94 | 95 | 96 | 97 |
98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Constructor 107 | 108 | 109 | Nautilus.PropertiesItem 110 | name 111 | value 112 | 113 | 114 | 115 | 116 | name : 117 | the user-visible name for the properties item 118 | 119 | 120 | value : 121 | the user-visible value for the properties item 122 | 123 | 124 | 125 | 126 | Creates a new Nautilus.PropertiesItem object. 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | Public Methods 138 | 139 | 140 | Nautilus.Menu.get_name 141 | 142 | get_name 143 | 144 | 145 | Returns the name of this Nautilus.PropertiesItem. 146 | 147 | 148 | 149 | 150 | Nautilus.Menu.get_value 151 | 152 | get_value 153 | 154 | 155 | Returns the value of this Nautilus.PropertiesItem. 156 | 157 | 158 | 159 | 160 | 161 | 162 |
163 | 164 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-properties-model-provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.PropertiesModelProvider 8 | 9 | 10 | Nautilus.PropertiesModelProvider 11 | Nautilus.PropertiesModelProvider Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.PropertiesModelProvider 23 | 24 | 25 | get_models 26 | files 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Description 37 | 38 | 39 | If subclassed, Nautilus will request a list of custom properties models that should 40 | appear when a user opens the Properties dialog for a file or folder. 41 | 42 | 43 | 44 | Nautilus.PropertiesModelProvider Example 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Passive Methods 56 | 57 | 58 | Nautilus.PropertiesModelProvider.get_models 59 | 60 | 61 | get_models 62 | files 63 | 64 | 65 | 66 | 67 | files : 68 | a list of Nautilus.FileInfo objects. 69 | 70 | 71 | Returns : 72 | a list of Nautilus.PropertiesModel objects 73 | 74 | 75 | 76 | 77 | This function is called by Nautilus when it wants properties model items from the extension. 78 | It is called in the main thread before a properties model is shown, so it should return quickly. 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-properties-model.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Nautilus.PropertiesModel 8 | 9 | 10 | Nautilus.PropertiesModel 11 | Nautilus.PropertiesModel Reference 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Synopsis 20 | 21 | 22 | Nautilus.PropertiesModel 23 | gobject.GObject 24 | 25 | 26 | Nautilus.PropertiesModel 27 | title 28 | model 29 | 30 | 31 | 32 | set_title 33 | title 34 | 35 | 36 | 37 | get_title 38 | 39 | 40 | 41 | get_model 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Ancestry 52 | 53 | +-- gobject.GObject 54 | +-- Nautilus.PropertiesModel 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Description 65 | 66 | 67 | A Nautilus.PropertiesModel object an model that describes a set of file properties. Extensions can provide Nautilus.PropertiesModel objects by registering it in Nautilus.PropertiesModelProvider. 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Properties 79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | "title" 90 | The user-visible name for the set of properties in this model. 91 | Read-Write 92 | 93 | 94 | 95 | "model" 96 | The GListModel containing Nautilus.PropertyItem objects. Default value: [] 97 | Read-Write-ConstructOnly 98 | 99 | 100 | 101 | 102 | 103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Constructor 113 | 114 | 115 | Nautilus.PropertiesModel 116 | title 117 | model 118 | 119 | 120 | 121 | 122 | title : 123 | the user-visible name for the set of properties in this model 124 | 125 | 126 | model : 127 | The GListModel containing Nautilus.PropertyItem objects. 128 | 129 | 130 | 131 | 132 | Creates a new Nautilus.PropertiesModel object. 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Public Methods 144 | 145 | 146 | Nautilus.Menu.set_title 147 | 148 | set_title 149 | title 150 | 151 | 152 | 153 | title : 154 | a new name of this Nautilus.PropertiesModel 155 | 156 | 157 | 158 | 159 | Sets a new name of this Nautilus.PropertiesModel. 160 | 161 | 162 | 163 | 164 | Nautilus.Menu.get_title 165 | 166 | get_title 167 | 168 | 169 | Returns the title of this Nautilus.PropertiesModel. 170 | 171 | 172 | 173 | 174 | Nautilus.Menu.get_model 175 | 176 | get_model 177 | 178 | 179 | Returns a GListModel containing Nautilus.PropertyItem objects. 180 | 181 | 182 | 183 | 184 | 185 | 186 |
187 | 188 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-provider-reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | Provider Interfaces 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/reference/nautilus-python-ref.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | %entities; 6 | ]> 7 | 8 | 9 | nautilus-python Reference Manual 10 | Reference Manual for nautilus-python &version; 11 | For nautilus-python version &version; 12 | 13 | 14 | Adam 15 | Plumb 16 | 17 | 18 | 19 | This reference describes the classes of the nautilus-python module. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | To try any of the examples, copy them over to: 2 | 3 | $XDG_DATA_DIR/share/nautilus-python/extensions/ 4 | or: 5 | ~/.local/share/nautilus-python/extensions/ 6 | 7 | FYI, it is still possible to load plugins from the old locations. 8 | 9 | Then restart nautilus. 10 | 11 | Hint: if you're testing an extension that you're developing, it may 12 | be useful to start a 'private' instance of nautilus, like this: 13 | $ mkdir /tmp/testing 14 | $ export TMPDIR=/tmp/testing 15 | $ nautilus --no-desktop 16 | -------------------------------------------------------------------------------- /examples/TestExtension.py: -------------------------------------------------------------------------------- 1 | from gi.repository import Nautilus, GObject 2 | from typing import List 3 | 4 | 5 | class TestExtension(GObject.GObject, Nautilus.MenuProvider): 6 | def __init__(self): 7 | super().__init__() 8 | print("Initialized test extension") 9 | 10 | def menu_activate_cb( 11 | self, 12 | menu: Nautilus.MenuItem, 13 | file: Nautilus.FileInfo, 14 | ) -> None: 15 | print("menu_activate_cb", file) 16 | 17 | def get_file_items( 18 | self, 19 | files: List[Nautilus.FileInfo], 20 | ) -> List[Nautilus.MenuItem]: 21 | if len(files) != 1: 22 | return [] 23 | 24 | file = files[0] 25 | 26 | item = Nautilus.MenuItem( 27 | name="SimpleMenuExtension::Show_File_Name", 28 | label="Showing %s" % file.get_name(), 29 | tip="Showing %s" % file.get_name(), 30 | ) 31 | item.connect("activate", self.menu_activate_cb, file) 32 | 33 | return [ 34 | item, 35 | ] 36 | 37 | # Even though we're not using background items, Nautilus will generate 38 | # a warning if the method isn't present 39 | def get_background_items( 40 | self, 41 | current_folder: Nautilus.FileInfo, 42 | ) -> List[Nautilus.MenuItem]: 43 | return [] 44 | -------------------------------------------------------------------------------- /examples/background-image.py: -------------------------------------------------------------------------------- 1 | from gi.repository import Nautilus, GObject, Gio 2 | from typing import List 3 | 4 | SUPPORTED_FORMATS = "image/jpeg", "image/png" 5 | BACKGROUND_SCHEMA = "org.gnome.desktop.background" 6 | BACKGROUND_KEY = "picture-uri" 7 | 8 | 9 | class BackgroundImageExtension(GObject.GObject, Nautilus.MenuProvider): 10 | def __init__(self): 11 | super().__init__() 12 | self.bgsettings = Gio.Settings.new(BACKGROUND_SCHEMA) 13 | 14 | def menu_activate_cb( 15 | self, 16 | menu: Nautilus.MenuItem, 17 | file: Nautilus.FileInfo, 18 | ) -> None: 19 | if file.is_gone(): 20 | return 21 | 22 | self.bgsettings[BACKGROUND_KEY] = file.get_uri() 23 | 24 | def get_file_items( 25 | self, 26 | files: List[Nautilus.FileInfo], 27 | ) -> List[Nautilus.MenuItem]: 28 | if len(files) != 1: 29 | return [] 30 | 31 | file = files[0] 32 | 33 | # We're only going to put ourselves on images context menus 34 | if not file.get_mime_type() in SUPPORTED_FORMATS: 35 | return [] 36 | 37 | # Gnome can only handle file: 38 | # In the future we might want to copy the file locally 39 | if file.get_uri_scheme() != "file": 40 | return [] 41 | 42 | item = Nautilus.MenuItem( 43 | name="Nautilus::set_background_image", 44 | label="Use as background image", 45 | tip="Set the current image as a background image", 46 | ) 47 | item.connect("activate", self.menu_activate_cb, file) 48 | 49 | return [ 50 | item, 51 | ] 52 | 53 | # Current versions of Nautilus will throw a warning if get_background_items 54 | # isn't present 55 | def get_background_items( 56 | self, 57 | current_folder: Nautilus.FileInfo, 58 | ) -> List[Nautilus.MenuItem]: 59 | return [] 60 | -------------------------------------------------------------------------------- /examples/block-size-column.py: -------------------------------------------------------------------------------- 1 | import os 2 | from urllib.parse import unquote 3 | from gi.repository import GObject, Nautilus 4 | from typing import List 5 | 6 | 7 | class ColumnExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider): 8 | def get_columns(self) -> List[Nautilus.Column]: 9 | column = Nautilus.Column( 10 | name="NautilusPython::block_size_column", 11 | attribute="block_size", 12 | label="Block size", 13 | description="Get the block size", 14 | ) 15 | 16 | return [ 17 | column, 18 | ] 19 | 20 | def update_file_info(self, file: Nautilus.FileInfo) -> None: 21 | if file.get_uri_scheme() != "file": 22 | return 23 | 24 | filename = unquote(file.get_uri()[7:]) 25 | 26 | file.add_string_attribute("block_size", str(os.stat(filename).st_blksize)) 27 | -------------------------------------------------------------------------------- /examples/md5sum-properties-model.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from urllib.parse import unquote 4 | from gi.repository import Nautilus, Gio, GObject 5 | from typing import List 6 | 7 | 8 | class MD5SumPropertiesModel(GObject.GObject, Nautilus.PropertiesModelProvider): 9 | def get_models( 10 | self, 11 | files: List[Nautilus.FileInfo], 12 | ) -> List[Nautilus.PropertiesModel]: 13 | if len(files) != 1: 14 | return [] 15 | 16 | file = files[0] 17 | if file.get_uri_scheme() != "file": 18 | return [] 19 | 20 | if file.is_directory(): 21 | return [] 22 | 23 | filename = unquote(file.get_uri()[7:]).encode("utf-8") 24 | 25 | section_model = Gio.ListStore.new(item_type=Nautilus.PropertiesItem) 26 | 27 | section_model.append( 28 | Nautilus.PropertiesItem( 29 | name="MD5 sum of the filename", 30 | value=hashlib.md5(filename).hexdigest(), 31 | ) 32 | ) 33 | 34 | return [ 35 | Nautilus.PropertiesModel( 36 | title="MD5Sum", 37 | model=section_model, 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /examples/meson.build: -------------------------------------------------------------------------------- 1 | install_data( 2 | 'README', 3 | install_dir: docdir, 4 | ) 5 | 6 | install_data( 7 | 'background-image.py', 8 | 'block-size-column.py', 9 | 'open-terminal.py', 10 | 'md5sum-properties-model.py', 11 | 'submenu.py', 12 | 'TestExtension.py', 13 | 'update-file-info-async.py', 14 | install_dir: docdir / 'examples', 15 | ) 16 | -------------------------------------------------------------------------------- /examples/open-terminal.py: -------------------------------------------------------------------------------- 1 | # This example is contributed by Martin Enlund 2 | import os 3 | from urllib.parse import unquote 4 | from gi.repository import Nautilus, GObject 5 | from typing import List 6 | 7 | 8 | class OpenTerminalExtension(GObject.GObject, Nautilus.MenuProvider): 9 | def _open_terminal(self, file: Nautilus.FileInfo) -> None: 10 | filename = unquote(file.get_uri()[7:]) 11 | 12 | os.chdir(filename) 13 | os.system("gnome-terminal") 14 | 15 | def menu_activate_cb( 16 | self, 17 | menu: Nautilus.MenuItem, 18 | file: Nautilus.FileInfo, 19 | ) -> None: 20 | self._open_terminal(file) 21 | 22 | def menu_background_activate_cb( 23 | self, 24 | menu: Nautilus.MenuItem, 25 | file: Nautilus.FileInfo, 26 | ) -> None: 27 | self._open_terminal(file) 28 | 29 | def get_file_items( 30 | self, 31 | files: List[Nautilus.FileInfo], 32 | ) -> List[Nautilus.MenuItem]: 33 | if len(files) != 1: 34 | return [] 35 | 36 | file = files[0] 37 | if not file.is_directory() or file.get_uri_scheme() != "file": 38 | return [] 39 | 40 | item = Nautilus.MenuItem( 41 | name="NautilusPython::openterminal_file_item", 42 | label="Open Terminal", 43 | tip="Open Terminal In %s" % file.get_name(), 44 | ) 45 | item.connect("activate", self.menu_activate_cb, file) 46 | 47 | return [ 48 | item, 49 | ] 50 | 51 | def get_background_items( 52 | self, 53 | current_folder: Nautilus.FileInfo, 54 | ) -> List[Nautilus.MenuItem]: 55 | item = Nautilus.MenuItem( 56 | name="NautilusPython::openterminal_file_item2", 57 | label="Open Terminal", 58 | tip="Open Terminal In %s" % current_folder.get_name(), 59 | ) 60 | item.connect("activate", self.menu_background_activate_cb, current_folder) 61 | 62 | return [ 63 | item, 64 | ] 65 | -------------------------------------------------------------------------------- /examples/submenu.py: -------------------------------------------------------------------------------- 1 | from gi.repository import Nautilus, GObject 2 | from typing import List 3 | 4 | 5 | class ExampleMenuProvider(GObject.GObject, Nautilus.MenuProvider): 6 | def get_file_items( 7 | self, 8 | files: List[Nautilus.FileInfo], 9 | ) -> List[Nautilus.MenuItem]: 10 | top_menuitem = Nautilus.MenuItem( 11 | name="ExampleMenuProvider::Foo", 12 | label="Foo", 13 | tip="", 14 | icon="", 15 | ) 16 | 17 | submenu = Nautilus.Menu() 18 | top_menuitem.set_submenu(submenu) 19 | 20 | sub_menuitem = Nautilus.MenuItem( 21 | name="ExampleMenuProvider::Bar", 22 | label="Bar", 23 | tip="", 24 | icon="", 25 | ) 26 | submenu.append_item(sub_menuitem) 27 | 28 | return [ 29 | top_menuitem, 30 | ] 31 | 32 | def get_background_items( 33 | self, 34 | current_folder: Nautilus.FileInfo, 35 | ) -> List[Nautilus.MenuItem]: 36 | submenu = Nautilus.Menu() 37 | submenu.append_item( 38 | Nautilus.MenuItem( 39 | name="ExampleMenuProvider::Bar2", 40 | label="Bar2", 41 | tip="", 42 | icon="", 43 | ) 44 | ) 45 | 46 | menuitem = Nautilus.MenuItem( 47 | name="ExampleMenuProvider::Foo2", 48 | label="Foo2", 49 | tip="", 50 | icon="", 51 | ) 52 | menuitem.set_submenu(submenu) 53 | 54 | return [ 55 | menuitem, 56 | ] 57 | -------------------------------------------------------------------------------- /examples/update-file-info-async.py: -------------------------------------------------------------------------------- 1 | from gi.repository import Nautilus, GObject 2 | 3 | 4 | class UpdateFileInfoAsync(GObject.GObject, Nautilus.InfoProvider): 5 | def __init__(self): 6 | super().__init__() 7 | self.timers = [] 8 | pass 9 | 10 | def update_file_info_full(self, provider, handle, closure, file): 11 | print("update_file_info_full") 12 | self.timers.append( 13 | GObject.timeout_add_seconds(3, self.update_cb, provider, handle, closure) 14 | ) 15 | return Nautilus.OperationResult.IN_PROGRESS 16 | 17 | def update_cb(self, provider, handle, closure): 18 | print("update_cb") 19 | Nautilus.info_provider_update_complete_invoke( 20 | closure, 21 | provider, 22 | handle, 23 | Nautilus.OperationResult.FAILED, 24 | ) 25 | 26 | def cancel_update(self, provider, handle): 27 | print("cancel_update") 28 | for t in self.timers: 29 | GObject.source_remove(t) 30 | self.timers = [] 31 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'nautilus-python', 3 | 'c', 4 | version: '4.0.1', 5 | meson_version: '>= 0.59.0', 6 | ) 7 | 8 | gnome = import('gnome', required: get_option('docs')) 9 | pkg = import('pkgconfig') 10 | python = import('python') 11 | 12 | prefix = get_option('prefix') 13 | libdir = get_option('libdir') 14 | # Needs to be absolute for C constant. 15 | datadir = prefix / get_option('datadir') 16 | docdir = datadir / 'doc' / meson.project_name() 17 | 18 | python3 = python.find_installation('python3') 19 | pygobject_major_version = 3 20 | pygobject_minor_version = 0 21 | pygobject_micro_version = 0 22 | pygobject = dependency('pygobject-3.0', version: f'>= @pygobject_major_version@.@pygobject_minor_version@.@pygobject_micro_version@') 23 | libnautilus_extension = dependency('libnautilus-extension-4', version: '>= 43.beta') 24 | gmodule = dependency('gmodule-2.0', version: '>= 2.44') 25 | python_dep = python3.dependency(embed: true) 26 | 27 | nautilus_extension_dir = libnautilus_extension.get_variable('extensiondir', pkgconfig_define: ['libdir', libdir]) 28 | 29 | py_so_filename = python3.get_variable('INSTSONAME') 30 | python_libpath = python3.get_variable('LIBDIR') / py_so_filename 31 | 32 | conf = configuration_data() 33 | conf.set_quoted('DATADIR', datadir) 34 | conf.set('PYGOBJECT_MAJOR_VERSION', pygobject_major_version) 35 | conf.set('PYGOBJECT_MINOR_VERSION', pygobject_minor_version) 36 | conf.set('PYGOBJECT_MICRO_VERSION', pygobject_micro_version) 37 | conf.set_quoted('PYTHON_LIBPATH', python_libpath) 38 | 39 | configure_file( 40 | output: 'config.h', 41 | configuration: conf, 42 | ) 43 | 44 | root_incdir = include_directories('.') 45 | 46 | if get_option('docs').enabled() 47 | subdir('docs/reference') 48 | endif 49 | subdir('examples') 50 | subdir('src') 51 | 52 | pkg.generate( 53 | name: 'nautilus-python', 54 | description: 'Nautilus-Python Components', 55 | variables: [ 56 | # TODO: Remove when bumping minimum Meson version to 0.62 57 | # https://mesonbuild.com/Release-notes-for-0-62-0.html#pkgconfiggenerate-will-now-include-variables-for-builtin-directories-when-referenced 58 | f'prefix=@prefix@', 59 | 'datadir=${prefix}/share', 60 | 'pythondir=${datadir}/nautilus-python/extensions', 61 | ], 62 | dataonly: true, 63 | ) 64 | 65 | summary({ 66 | 'PyGObject Version': 'pygobject-3.0', 67 | 'Python Library': python_libpath, 68 | 'Documentation': get_option('docs'), 69 | }) 70 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option( 2 | 'docs', 3 | type: 'feature', 4 | description: 'Generate and install API docs', 5 | ) 6 | -------------------------------------------------------------------------------- /nautilus-python.doap: -------------------------------------------------------------------------------- 1 | 6 | 7 | nautilus-python 8 | Python bindings for the Nautilus extension framework 9 | 10 | 11 | 12 | 13 | 14 | Adam Plumb 15 | 16 | adamplumb 17 | 18 | 19 | 20 | 21 | 22 | Jan Tojnar 23 | 24 | jtojnar 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "niv": { 3 | "branch": "master", 4 | "description": "Easy dependency management for Nix projects", 5 | "homepage": "https://github.com/nmattia/niv", 6 | "owner": "nmattia", 7 | "repo": "niv", 8 | "rev": "e2f66fe558481d6b569358d27db06f7e972ed71b", 9 | "sha256": "1xn822jajags6bigdr1ssxvfiyd7d3adhnmmrr9x3maphchkr0x0", 10 | "type": "tarball", 11 | "url": "https://github.com/nmattia/niv/archive/e2f66fe558481d6b569358d27db06f7e972ed71b.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | }, 14 | "nixpkgs": { 15 | "branch": "nixos-unstable", 16 | "description": "Nix Packages collection", 17 | "homepage": "", 18 | "owner": "NixOS", 19 | "repo": "nixpkgs", 20 | "rev": "5df43628fdf08d642be8ba5b3625a6c70731c19c", 21 | "sha256": "05xhbk4yjbv0f760ld6q9z2v0nphakgk78kgd0wnnmzdjqqkbfad", 22 | "type": "tarball", 23 | "url": "https://github.com/NixOS/nixpkgs/archive/5df43628fdf08d642be8ba5b3625a6c70731c19c.tar.gz", 24 | "url_template": "https://github.com///archive/.tar.gz" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nix/sources.nix: -------------------------------------------------------------------------------- 1 | # This file has been generated by Niv. 2 | 3 | let 4 | 5 | # 6 | # The fetchers. fetch_ fetches specs of type . 7 | # 8 | 9 | fetch_file = pkgs: name: spec: 10 | let 11 | name' = sanitizeName name + "-src"; 12 | in 13 | if spec.builtin or true then 14 | builtins_fetchurl { inherit (spec) url sha256; name = name'; } 15 | else 16 | pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; 17 | 18 | fetch_tarball = pkgs: name: spec: 19 | let 20 | name' = sanitizeName name + "-src"; 21 | in 22 | if spec.builtin or true then 23 | builtins_fetchTarball { name = name'; inherit (spec) url sha256; } 24 | else 25 | pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; 26 | 27 | fetch_git = name: spec: 28 | let 29 | ref = 30 | spec.ref or ( 31 | if spec ? branch then "refs/heads/${spec.branch}" else 32 | if spec ? tag then "refs/tags/${spec.tag}" else 33 | abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!" 34 | ); 35 | submodules = spec.submodules or false; 36 | submoduleArg = 37 | let 38 | nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0; 39 | emptyArgWithWarning = 40 | if submodules 41 | then 42 | builtins.trace 43 | ( 44 | "The niv input \"${name}\" uses submodules " 45 | + "but your nix's (${builtins.nixVersion}) builtins.fetchGit " 46 | + "does not support them" 47 | ) 48 | { } 49 | else { }; 50 | in 51 | if nixSupportsSubmodules 52 | then { inherit submodules; } 53 | else emptyArgWithWarning; 54 | in 55 | builtins.fetchGit 56 | ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg); 57 | 58 | fetch_local = spec: spec.path; 59 | 60 | fetch_builtin-tarball = name: throw 61 | ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. 62 | $ niv modify ${name} -a type=tarball -a builtin=true''; 63 | 64 | fetch_builtin-url = name: throw 65 | ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. 66 | $ niv modify ${name} -a type=file -a builtin=true''; 67 | 68 | # 69 | # Various helpers 70 | # 71 | 72 | # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 73 | sanitizeName = name: 74 | ( 75 | concatMapStrings (s: if builtins.isList s then "-" else s) 76 | ( 77 | builtins.split "[^[:alnum:]+._?=-]+" 78 | ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) 79 | ) 80 | ); 81 | 82 | # The set of packages used when specs are fetched using non-builtins. 83 | mkPkgs = sources: system: 84 | let 85 | sourcesNixpkgs = 86 | import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; 87 | hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; 88 | hasThisAsNixpkgsPath = == ./.; 89 | in 90 | if builtins.hasAttr "nixpkgs" sources 91 | then sourcesNixpkgs 92 | else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then 93 | import { } 94 | else 95 | abort 96 | '' 97 | Please specify either (through -I or NIX_PATH=nixpkgs=...) or 98 | add a package called "nixpkgs" to your sources.json. 99 | ''; 100 | 101 | # The actual fetching function. 102 | fetch = pkgs: name: spec: 103 | 104 | if ! builtins.hasAttr "type" spec then 105 | abort "ERROR: niv spec ${name} does not have a 'type' attribute" 106 | else if spec.type == "file" then fetch_file pkgs name spec 107 | else if spec.type == "tarball" then fetch_tarball pkgs name spec 108 | else if spec.type == "git" then fetch_git name spec 109 | else if spec.type == "local" then fetch_local spec 110 | else if spec.type == "builtin-tarball" then fetch_builtin-tarball name 111 | else if spec.type == "builtin-url" then fetch_builtin-url name 112 | else 113 | abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; 114 | 115 | # If the environment variable NIV_OVERRIDE_${name} is set, then use 116 | # the path directly as opposed to the fetched source. 117 | replace = name: drv: 118 | let 119 | saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name; 120 | ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; 121 | in 122 | if ersatz == "" then drv else 123 | # this turns the string into an actual Nix path (for both absolute and 124 | # relative paths) 125 | if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; 126 | 127 | # Ports of functions for older nix versions 128 | 129 | # a Nix version of mapAttrs if the built-in doesn't exist 130 | mapAttrs = builtins.mapAttrs or ( 131 | f: set: with builtins; 132 | listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) 133 | ); 134 | 135 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 136 | range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1); 137 | 138 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 139 | stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); 140 | 141 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 142 | stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); 143 | concatMapStrings = f: list: concatStrings (map f list); 144 | concatStrings = builtins.concatStringsSep ""; 145 | 146 | # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 147 | optionalAttrs = cond: as: if cond then as else { }; 148 | 149 | # fetchTarball version that is compatible between all the versions of Nix 150 | builtins_fetchTarball = { url, name ? null, sha256 }@attrs: 151 | let 152 | inherit (builtins) lessThan nixVersion fetchTarball; 153 | in 154 | if lessThan nixVersion "1.12" then 155 | fetchTarball ({ inherit url; } // (optionalAttrs (name != null) { inherit name; })) 156 | else 157 | fetchTarball attrs; 158 | 159 | # fetchurl version that is compatible between all the versions of Nix 160 | builtins_fetchurl = { url, name ? null, sha256 }@attrs: 161 | let 162 | inherit (builtins) lessThan nixVersion fetchurl; 163 | in 164 | if lessThan nixVersion "1.12" then 165 | fetchurl ({ inherit url; } // (optionalAttrs (name != null) { inherit name; })) 166 | else 167 | fetchurl attrs; 168 | 169 | # Create the final "sources" from the config 170 | mkSources = config: 171 | mapAttrs 172 | ( 173 | name: spec: 174 | if builtins.hasAttr "outPath" spec 175 | then 176 | abort 177 | "The values in sources.json should not have an 'outPath' attribute" 178 | else 179 | spec // { outPath = replace name (fetch config.pkgs name spec); } 180 | ) 181 | config.sources; 182 | 183 | # The "config" used by the fetchers 184 | mkConfig = 185 | { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null 186 | , sources ? if sourcesFile == null then { } else builtins.fromJSON (builtins.readFile sourcesFile) 187 | , system ? builtins.currentSystem 188 | , pkgs ? mkPkgs sources system 189 | }: rec { 190 | # The sources, i.e. the attribute set of spec name to spec 191 | inherit sources; 192 | 193 | # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers 194 | inherit pkgs; 195 | }; 196 | 197 | in 198 | mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); } 199 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? null, 3 | doCheck ? true, 4 | }: 5 | import ./default.nix { 6 | inherit doCheck pkgs; 7 | shell = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | nautilus_python = shared_module( 2 | 'nautilus-python', 3 | sources: [ 4 | 'nautilus-python.c', 5 | 'nautilus-python-object.c', 6 | ], 7 | dependencies: [ 8 | libnautilus_extension, 9 | pygobject, 10 | gmodule, 11 | python_dep, 12 | ], 13 | include_directories: [ 14 | root_incdir, 15 | ], 16 | install: true, 17 | install_dir: nautilus_extension_dir, 18 | ) 19 | -------------------------------------------------------------------------------- /src/nautilus-python-object.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* 3 | * Copyright (C) 2004,2005 Johan Dahlin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | 22 | #include "nautilus-python-object.h" 23 | #include "nautilus-python.h" 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #define METHOD_PREFIX "" 32 | 33 | static GObjectClass *parent_class; 34 | 35 | /* These macros assumes the following things: 36 | * a METHOD_NAME is defined with is a string 37 | * a goto label called beach 38 | * the return value is called ret 39 | */ 40 | 41 | #define CHECK_METHOD_NAME(self) \ 42 | if (!PyObject_HasAttrString(self, METHOD_NAME)) \ 43 | goto beach; 44 | 45 | #define CHECK_OBJECT(object) \ 46 | if (object->instance == NULL) \ 47 | { \ 48 | g_object_unref (object); \ 49 | goto beach; \ 50 | } \ 51 | 52 | #define CONVERT_LIST(py_files, files) \ 53 | { \ 54 | GList *l; \ 55 | py_files = PyList_New(0); \ 56 | for (l = files; l; l = l->next) \ 57 | { \ 58 | PyObject *item = pygobject_new ((GObject *)l->data); \ 59 | PyList_Append(py_files, item); \ 60 | Py_DECREF (item); \ 61 | } \ 62 | } 63 | 64 | #define HANDLE_RETVAL(py_ret) \ 65 | if (!py_ret) \ 66 | { \ 67 | PyErr_Print(); \ 68 | goto beach; \ 69 | } \ 70 | else if (py_ret == Py_None) \ 71 | { \ 72 | goto beach; \ 73 | } 74 | 75 | #define HANDLE_LIST(py_ret, type, type_name) \ 76 | { \ 77 | Py_ssize_t i = 0; \ 78 | if (!PySequence_Check(py_ret) || PyUnicode_Check(py_ret)) \ 79 | { \ 80 | PyErr_SetString(PyExc_TypeError, \ 81 | METHOD_NAME " must return a sequence"); \ 82 | goto beach; \ 83 | } \ 84 | for (i = 0; i < PySequence_Size (py_ret); i++) \ 85 | { \ 86 | PyGObject *py_item; \ 87 | py_item = (PyGObject*)PySequence_GetItem (py_ret, i); \ 88 | if (!pygobject_check(py_item, &Py##type##_Type)) \ 89 | { \ 90 | PyErr_SetString(PyExc_TypeError, \ 91 | METHOD_NAME \ 92 | " must return a sequence of " \ 93 | type_name); \ 94 | goto beach; \ 95 | } \ 96 | ret = g_list_append (ret, (type*) g_object_ref(py_item->obj)); \ 97 | Py_DECREF(py_item); \ 98 | } \ 99 | } 100 | 101 | 102 | static void 103 | free_pygobject_data(gpointer data, gpointer user_data) { 104 | /* Some NautilusFileInfo objects are cached and not freed until nautilus 105 | itself is closed. Since PyGObject stores data that must be freed by 106 | the Python interpreter, we must always free it before the interpreter 107 | is finalized. */ 108 | g_object_set_data((GObject *)data, "PyGObject::instance-data", NULL); 109 | } 110 | 111 | static void 112 | free_pygobject_data_list(GList *list) { 113 | if (list == NULL) 114 | return; 115 | 116 | g_list_foreach(list, (GFunc)free_pygobject_data, NULL); 117 | } 118 | 119 | static PyObject * 120 | nautilus_python_boxed_new (PyTypeObject *type, gpointer boxed, gboolean free_on_dealloc) { 121 | PyGBoxed *self = (PyGBoxed *) type->tp_alloc (type, 0); 122 | self->gtype = pyg_type_from_object ( (PyObject *) type); 123 | self->boxed = boxed; 124 | self->free_on_dealloc = free_on_dealloc; 125 | 126 | return (PyObject *) self; 127 | } 128 | 129 | #define METHOD_NAME "get_models" 130 | static GList * 131 | nautilus_python_object_get_models (NautilusPropertiesModelProvider *provider, 132 | GList *files) { 133 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 134 | PyObject *py_files, *py_ret = NULL; 135 | GList *ret = NULL; 136 | PyGILState_STATE state = pyg_gil_state_ensure(); 137 | 138 | debug_enter(); 139 | 140 | CHECK_OBJECT(object); 141 | CHECK_METHOD_NAME(object->instance); 142 | 143 | CONVERT_LIST(py_files, files); 144 | 145 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX METHOD_NAME, 146 | "(N)", py_files); 147 | HANDLE_RETVAL(py_ret); 148 | 149 | HANDLE_LIST(py_ret, NautilusPropertiesModel, "Nautilus.PropertiesModel"); 150 | 151 | beach: 152 | free_pygobject_data_list(files); 153 | Py_XDECREF(py_ret); 154 | pyg_gil_state_release(state); 155 | return ret; 156 | } 157 | #undef METHOD_NAME 158 | 159 | static void 160 | nautilus_python_object_properties_model_provider_interface_init (NautilusPropertiesModelProviderInterface *interface) { 161 | interface->get_models = nautilus_python_object_get_models; 162 | } 163 | 164 | #define METHOD_NAME "get_file_items" 165 | static GList * 166 | nautilus_python_object_get_file_items (NautilusMenuProvider *provider, 167 | GList *files) { 168 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 169 | GList *ret = NULL; 170 | PyObject *py_ret = NULL, *py_files; 171 | PyGILState_STATE state = pyg_gil_state_ensure(); 172 | 173 | debug_enter(); 174 | 175 | CHECK_OBJECT(object); 176 | 177 | if (PyObject_HasAttrString(object->instance, "get_file_items_full")) { 178 | CONVERT_LIST(py_files, files); 179 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX "get_file_items_full", 180 | "(NN)", 181 | pygobject_new((GObject *)provider), 182 | py_files); 183 | } 184 | else if (PyObject_HasAttrString(object->instance, "get_file_items")) { 185 | CONVERT_LIST(py_files, files); 186 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX METHOD_NAME, 187 | "(N)", 188 | py_files); 189 | } 190 | else { 191 | goto beach; 192 | } 193 | 194 | HANDLE_RETVAL(py_ret); 195 | 196 | HANDLE_LIST(py_ret, NautilusMenuItem, "Nautilus.MenuItem"); 197 | 198 | beach: 199 | free_pygobject_data_list(files); 200 | Py_XDECREF(py_ret); 201 | pyg_gil_state_release(state); 202 | return ret; 203 | } 204 | #undef METHOD_NAME 205 | 206 | #define METHOD_NAME "get_background_items" 207 | static GList * 208 | nautilus_python_object_get_background_items (NautilusMenuProvider *provider, 209 | NautilusFileInfo *file) { 210 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 211 | GList *ret = NULL; 212 | PyObject *py_ret = NULL; 213 | PyGILState_STATE state = pyg_gil_state_ensure(); 214 | 215 | debug_enter(); 216 | 217 | CHECK_OBJECT(object); 218 | 219 | if (PyObject_HasAttrString(object->instance, "get_background_items_full")) { 220 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX "get_background_items_full", 221 | "(NN)", 222 | pygobject_new((GObject *)provider), 223 | pygobject_new((GObject *)file)); 224 | } 225 | else if (PyObject_HasAttrString(object->instance, "get_background_items")) { 226 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX METHOD_NAME, 227 | "(N)", 228 | pygobject_new((GObject *)file)); 229 | } 230 | else { 231 | goto beach; 232 | } 233 | 234 | HANDLE_RETVAL(py_ret); 235 | 236 | HANDLE_LIST(py_ret, NautilusMenuItem, "Nautilus.MenuItem"); 237 | 238 | beach: 239 | free_pygobject_data(file, NULL); 240 | Py_XDECREF(py_ret); 241 | pyg_gil_state_release(state); 242 | return ret; 243 | } 244 | #undef METHOD_NAME 245 | 246 | static void 247 | nautilus_python_object_menu_provider_interface_init (NautilusMenuProviderInterface *interface) { 248 | interface->get_background_items = nautilus_python_object_get_background_items; 249 | interface->get_file_items = nautilus_python_object_get_file_items; 250 | } 251 | 252 | #define METHOD_NAME "get_columns" 253 | static GList * 254 | nautilus_python_object_get_columns (NautilusColumnProvider *provider) { 255 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 256 | GList *ret = NULL; 257 | PyObject *py_ret = NULL; 258 | PyGILState_STATE state = pyg_gil_state_ensure(); \ 259 | 260 | debug_enter(); 261 | 262 | CHECK_OBJECT(object); 263 | CHECK_METHOD_NAME(object->instance); 264 | 265 | py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX METHOD_NAME, 266 | NULL); 267 | 268 | HANDLE_RETVAL(py_ret); 269 | 270 | HANDLE_LIST(py_ret, NautilusColumn, "Nautilus.Column"); 271 | 272 | beach: 273 | if (py_ret != NULL) 274 | Py_XDECREF(py_ret); 275 | pyg_gil_state_release(state); 276 | return ret; 277 | } 278 | #undef METHOD_NAME 279 | 280 | static void 281 | nautilus_python_object_column_provider_interface_init (NautilusColumnProviderInterface *interface) { 282 | interface->get_columns = nautilus_python_object_get_columns; 283 | } 284 | 285 | 286 | #define METHOD_NAME "cancel_update" 287 | static void 288 | nautilus_python_object_cancel_update (NautilusInfoProvider *provider, 289 | NautilusOperationHandle *handle) { 290 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 291 | PyGILState_STATE state = pyg_gil_state_ensure(); 292 | PyObject *py_handle = nautilus_python_boxed_new (_PyNautilusOperationHandle_Type, handle, FALSE); 293 | 294 | debug_enter(); 295 | 296 | CHECK_OBJECT(object); 297 | CHECK_METHOD_NAME(object->instance); 298 | 299 | PyObject_CallMethod(object->instance, 300 | METHOD_PREFIX METHOD_NAME, "(NN)", 301 | pygobject_new((GObject*)provider), 302 | py_handle); 303 | 304 | beach: 305 | pyg_gil_state_release(state); 306 | } 307 | #undef METHOD_NAME 308 | 309 | #define METHOD_NAME "update_file_info" 310 | static NautilusOperationResult 311 | nautilus_python_object_update_file_info (NautilusInfoProvider *provider, 312 | NautilusFileInfo *file, 313 | GClosure *update_complete, 314 | NautilusOperationHandle **handle) { 315 | NautilusPythonObject *object = (NautilusPythonObject*)provider; 316 | NautilusOperationResult ret = NAUTILUS_OPERATION_COMPLETE; 317 | PyObject *py_ret = NULL; 318 | PyGILState_STATE state = pyg_gil_state_ensure(); 319 | PyObject *py_handle = nautilus_python_boxed_new (_PyNautilusOperationHandle_Type, *handle, FALSE); 320 | 321 | debug_enter(); 322 | 323 | CHECK_OBJECT(object); 324 | 325 | if (PyObject_HasAttrString(object->instance, "update_file_info_full")) { 326 | py_ret = PyObject_CallMethod(object->instance, 327 | METHOD_PREFIX "update_file_info_full", "(NNNN)", 328 | pygobject_new((GObject*)provider), 329 | py_handle, 330 | pyg_boxed_new(G_TYPE_CLOSURE, update_complete, TRUE, TRUE), 331 | pygobject_new((GObject*)file)); 332 | } 333 | else if (PyObject_HasAttrString(object->instance, "update_file_info")) { 334 | py_ret = PyObject_CallMethod(object->instance, 335 | METHOD_PREFIX METHOD_NAME, "(N)", 336 | pygobject_new((GObject*)file)); 337 | } 338 | else { 339 | goto beach; 340 | } 341 | 342 | HANDLE_RETVAL(py_ret); 343 | 344 | if (!PyLong_Check(py_ret)) { 345 | PyErr_SetString(PyExc_TypeError, 346 | METHOD_NAME " must return None or a int"); 347 | goto beach; 348 | } 349 | 350 | ret = PyLong_AsLong(py_ret); 351 | 352 | beach: 353 | free_pygobject_data(file, NULL); 354 | Py_XDECREF(py_ret); 355 | pyg_gil_state_release(state); 356 | return ret; 357 | } 358 | #undef METHOD_NAME 359 | 360 | static void 361 | nautilus_python_object_info_provider_interface_init (NautilusInfoProviderInterface *interface) { 362 | interface->cancel_update = nautilus_python_object_cancel_update; 363 | interface->update_file_info = nautilus_python_object_update_file_info; 364 | } 365 | 366 | static void 367 | nautilus_python_object_instance_init (NautilusPythonObject *object) { 368 | NautilusPythonObjectClass *class; 369 | debug_enter(); 370 | 371 | class = (NautilusPythonObjectClass*)(((GTypeInstance*)object)->g_class); 372 | 373 | object->instance = PyObject_CallObject(class->type, NULL); 374 | if (object->instance == NULL) 375 | PyErr_Print(); 376 | } 377 | 378 | static void 379 | nautilus_python_object_finalize (GObject *object) { 380 | debug_enter(); 381 | 382 | if (((NautilusPythonObject *)object)->instance != NULL) 383 | Py_DECREF(((NautilusPythonObject *)object)->instance); 384 | } 385 | 386 | static void 387 | nautilus_python_object_class_init (NautilusPythonObjectClass *class, 388 | gpointer class_data) { 389 | debug_enter(); 390 | 391 | parent_class = g_type_class_peek_parent (class); 392 | 393 | class->type = (PyObject*)class_data; 394 | 395 | G_OBJECT_CLASS (class)->finalize = nautilus_python_object_finalize; 396 | } 397 | 398 | GType 399 | nautilus_python_object_get_type (GTypeModule *module, 400 | PyObject *type) { 401 | PyObject *name_str = NULL; 402 | g_autofree GTypeInfo *info = NULL; 403 | g_autofree gchar *type_name = NULL; 404 | GType gtype; 405 | 406 | static const GInterfaceInfo properties_model_provider_interface_info = { 407 | (GInterfaceInitFunc) nautilus_python_object_properties_model_provider_interface_init, 408 | NULL, 409 | NULL 410 | }; 411 | 412 | static const GInterfaceInfo menu_provider_interface_info = { 413 | (GInterfaceInitFunc) nautilus_python_object_menu_provider_interface_init, 414 | NULL, 415 | NULL 416 | }; 417 | 418 | static const GInterfaceInfo column_provider_interface_info = { 419 | (GInterfaceInitFunc) nautilus_python_object_column_provider_interface_init, 420 | NULL, 421 | NULL 422 | }; 423 | 424 | static const GInterfaceInfo info_provider_interface_info = { 425 | (GInterfaceInitFunc) nautilus_python_object_info_provider_interface_init, 426 | NULL, 427 | NULL 428 | }; 429 | 430 | name_str = PyObject_GetAttrString(type, "__name__"); 431 | debug_enter_args("type=%s", PyUnicode_AsUTF8(name_str)); 432 | info = g_new0 (GTypeInfo, 1); 433 | 434 | info->class_size = sizeof (NautilusPythonObjectClass); 435 | info->class_init = (GClassInitFunc)nautilus_python_object_class_init; 436 | info->instance_size = sizeof (NautilusPythonObject); 437 | info->instance_init = (GInstanceInitFunc)nautilus_python_object_instance_init; 438 | 439 | info->class_data = type; 440 | Py_INCREF(type); 441 | 442 | type_name = g_strdup_printf("%s+NautilusPython", PyUnicode_AsUTF8(name_str)); 443 | 444 | Py_XDECREF(name_str); 445 | 446 | gtype = g_type_module_register_type (module, 447 | G_TYPE_OBJECT, 448 | type_name, 449 | info, 0); 450 | 451 | if (PyObject_IsSubclass(type, (PyObject*)&PyNautilusPropertiesModelProvider_Type)) { 452 | g_type_module_add_interface (module, gtype, 453 | NAUTILUS_TYPE_PROPERTIES_MODEL_PROVIDER, 454 | &properties_model_provider_interface_info); 455 | } 456 | 457 | if (PyObject_IsSubclass(type, (PyObject*)&PyNautilusMenuProvider_Type)) { 458 | g_type_module_add_interface (module, gtype, 459 | NAUTILUS_TYPE_MENU_PROVIDER, 460 | &menu_provider_interface_info); 461 | } 462 | 463 | if (PyObject_IsSubclass(type, (PyObject*)&PyNautilusColumnProvider_Type)) { 464 | g_type_module_add_interface (module, gtype, 465 | NAUTILUS_TYPE_COLUMN_PROVIDER, 466 | &column_provider_interface_info); 467 | } 468 | 469 | if (PyObject_IsSubclass(type, (PyObject*)&PyNautilusInfoProvider_Type)) { 470 | g_type_module_add_interface (module, gtype, 471 | NAUTILUS_TYPE_INFO_PROVIDER, 472 | &info_provider_interface_info); 473 | } 474 | 475 | return gtype; 476 | } 477 | -------------------------------------------------------------------------------- /src/nautilus-python-object.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* 3 | * Copyright (C) 2004,2005 Johan Dahlin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifndef NAUTILUS_PYTHON_OBJECT_H 21 | #define NAUTILUS_PYTHON_OBJECT_H 22 | 23 | #include 24 | #include 25 | 26 | G_BEGIN_DECLS 27 | 28 | typedef struct _NautilusPythonObject NautilusPythonObject; 29 | typedef struct _NautilusPythonObjectClass NautilusPythonObjectClass; 30 | 31 | struct _NautilusPythonObject { 32 | GObject parent_slot; 33 | PyObject *instance; 34 | }; 35 | 36 | struct _NautilusPythonObjectClass { 37 | GObjectClass parent_slot; 38 | PyObject *type; 39 | }; 40 | 41 | GType nautilus_python_object_get_type (GTypeModule *module, PyObject *type); 42 | 43 | G_END_DECLS 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/nautilus-python.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* 3 | * Copyright (C) 2004,2005 Johan Dahlin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #define NO_IMPORT_PYGOBJECT //To avoid a multiple definition, nautilus-python-object.c also includes and does the import. 24 | #include 25 | #include 26 | 27 | #include "nautilus-python.h" 28 | #include "nautilus-python-object.h" 29 | 30 | #include 31 | 32 | static const GDebugKey nautilus_python_debug_keys[] = { 33 | {"misc", NAUTILUS_PYTHON_DEBUG_MISC}, 34 | }; 35 | NautilusPythonDebug nautilus_python_debug; 36 | 37 | static gboolean nautilus_python_init_python(void); 38 | 39 | static GArray *all_types = NULL; 40 | 41 | 42 | PyTypeObject *_PyNautilusColumn_Type; 43 | PyTypeObject *_PyNautilusColumnProvider_Type; 44 | PyTypeObject *_PyNautilusInfoProvider_Type; 45 | PyTypeObject *_PyNautilusMenu_Type; 46 | PyTypeObject *_PyNautilusMenuItem_Type; 47 | PyTypeObject *_PyNautilusMenuProvider_Type; 48 | PyTypeObject *_PyNautilusPropertiesItem_Type; 49 | PyTypeObject *_PyNautilusPropertiesModel_Type; 50 | PyTypeObject *_PyNautilusPropertiesModelProvider_Type; 51 | PyTypeObject *_PyNautilusOperationHandle_Type; 52 | 53 | static inline gboolean 54 | np_init_pygobject(void) { 55 | PyObject *gobject = pygobject_init (PYGOBJECT_MAJOR_VERSION, PYGOBJECT_MINOR_VERSION, PYGOBJECT_MICRO_VERSION); 56 | 57 | if (gobject == NULL) { 58 | PyErr_Print (); 59 | return FALSE; 60 | } 61 | 62 | return TRUE; 63 | } 64 | 65 | static void 66 | nautilus_python_load_file(GTypeModule *type_module, 67 | const gchar *filename) { 68 | PyObject *main_module, *main_locals, *locals, *key, *value; 69 | PyObject *module; 70 | GType gtype; 71 | Py_ssize_t pos = 0; 72 | 73 | debug_enter_args("filename=%s", filename); 74 | 75 | main_module = PyImport_AddModule("__main__"); 76 | if (main_module == NULL) { 77 | g_warning("Could not get __main__."); 78 | return; 79 | } 80 | 81 | main_locals = PyModule_GetDict(main_module); 82 | module = PyImport_ImportModuleEx((char *) filename, main_locals, main_locals, NULL); 83 | if (!module) { 84 | PyErr_Print(); 85 | return; 86 | } 87 | 88 | locals = PyModule_GetDict(module); 89 | 90 | while (PyDict_Next(locals, &pos, &key, &value)) { 91 | if (!PyType_Check(value)) 92 | continue; 93 | 94 | if (PyObject_IsSubclass(value, (PyObject*)&PyNautilusColumnProvider_Type) || 95 | PyObject_IsSubclass(value, (PyObject*)&PyNautilusInfoProvider_Type) || 96 | PyObject_IsSubclass(value, (PyObject*)&PyNautilusMenuProvider_Type) || 97 | PyObject_IsSubclass(value, (PyObject*)&PyNautilusPropertiesModelProvider_Type)) { 98 | gtype = nautilus_python_object_get_type(type_module, value); 99 | g_array_append_val(all_types, gtype); 100 | } 101 | } 102 | 103 | debug("Loaded python modules"); 104 | } 105 | 106 | static void 107 | nautilus_python_load_dir (GTypeModule *module, 108 | const char *dirname) { 109 | GDir *dir; 110 | const char *name; 111 | gboolean initialized = FALSE; 112 | 113 | debug_enter_args("dirname=%s", dirname); 114 | 115 | dir = g_dir_open(dirname, 0, NULL); 116 | if (!dir) 117 | return; 118 | 119 | while ((name = g_dir_read_name(dir))) { 120 | if (g_str_has_suffix(name, ".py")) { 121 | size_t len = strlen (name) - 3; 122 | g_autofree char *modulename = g_strndup (name, len); 123 | 124 | if (!initialized) { 125 | PyObject *sys_path, *py_path; 126 | 127 | /* n-p python part is initialized on demand (or not 128 | * at all if no extensions are found) */ 129 | if (!nautilus_python_init_python()) { 130 | g_warning("nautilus_python_init_python failed"); 131 | break; 132 | } 133 | 134 | /* sys.path.insert(0, dirname) */ 135 | sys_path = PySys_GetObject("path"); 136 | py_path = PyUnicode_FromString(dirname); 137 | PyList_Insert(sys_path, 0, py_path); 138 | Py_DECREF(py_path); 139 | } 140 | 141 | nautilus_python_load_file(module, modulename); 142 | } 143 | } 144 | 145 | g_dir_close (dir); 146 | } 147 | 148 | static gboolean 149 | nautilus_python_init_python (void) { 150 | PyObject *nautilus; 151 | GModule *libpython; 152 | 153 | if (Py_IsInitialized()) 154 | return TRUE; 155 | 156 | debug("g_module_open " PYTHON_LIBPATH); 157 | libpython = g_module_open (PYTHON_LIBPATH, 0); 158 | if (!libpython) 159 | g_warning("g_module_open libpython failed: %s", g_module_error()); 160 | 161 | debug("Py_Initialize"); 162 | Py_Initialize(); 163 | if (PyErr_Occurred()) { 164 | PyErr_Print(); 165 | return FALSE; 166 | } 167 | 168 | debug("Sanitize the python search path and set sys.argv"); 169 | PyRun_SimpleString("import sys; " 170 | "sys.path = [path for path in sys.path if path]; " 171 | "sys.argv = ['nautilus']"); 172 | if (PyErr_Occurred()) { 173 | PyErr_Print(); 174 | return FALSE; 175 | } 176 | 177 | /* import gobject */ 178 | debug("init_pygobject"); 179 | if (!np_init_pygobject()) { 180 | g_warning("pygobject initialization failed"); 181 | return FALSE; 182 | } 183 | 184 | /* import nautilus */ 185 | g_setenv("INSIDE_NAUTILUS_PYTHON", "", FALSE); 186 | debug("import nautilus"); 187 | PyRun_SimpleString("import gi; gi.require_version('Nautilus', '4.0')"); 188 | nautilus = PyImport_ImportModule("gi.repository.Nautilus"); 189 | if (!nautilus) { 190 | PyErr_Print(); 191 | return FALSE; 192 | } 193 | 194 | #define IMPORT(x, y) \ 195 | _PyNautilus##x##_Type = (PyTypeObject *)PyObject_GetAttrString(nautilus, y); \ 196 | if (_PyNautilus##x##_Type == NULL) { \ 197 | PyErr_Print(); \ 198 | return FALSE; \ 199 | } 200 | 201 | IMPORT(Column, "Column"); 202 | IMPORT(ColumnProvider, "ColumnProvider"); 203 | IMPORT(InfoProvider, "InfoProvider"); 204 | IMPORT(Menu, "Menu"); 205 | IMPORT(MenuItem, "MenuItem"); 206 | IMPORT(MenuProvider, "MenuProvider"); 207 | IMPORT(PropertiesItem, "PropertiesItem"); 208 | IMPORT(PropertiesModel, "PropertiesModel"); 209 | IMPORT(PropertiesModelProvider, "PropertiesModelProvider"); 210 | IMPORT(OperationHandle, "OperationHandle"); 211 | 212 | #undef IMPORT 213 | 214 | return TRUE; 215 | } 216 | 217 | 218 | static void 219 | nautilus_python_check_all_directories(GTypeModule *module) { 220 | GList *dirs = NULL; 221 | 222 | // Check ~/.local/share first 223 | dirs = g_list_append(dirs, g_build_filename(g_get_user_data_dir(), 224 | "nautilus-python", "extensions", NULL)); 225 | 226 | // If nautilus is built in a non-standard prefix 227 | // Check nautilus prefix's DATADIR 228 | gchar *prefix_extension_dir = DATADIR "/nautilus-python/extensions"; 229 | dirs = g_list_append(dirs, g_strdup (prefix_extension_dir)); 230 | 231 | // Check all system data dirs 232 | const gchar *const *temp = g_get_system_data_dirs(); 233 | while (*temp != NULL) { 234 | gchar *dir = g_build_filename(*temp, 235 | "nautilus-python", "extensions", NULL); 236 | if (g_strcmp0(dir, prefix_extension_dir) != 0) { 237 | dirs = g_list_append(dirs, dir); 238 | } else { 239 | g_free (dir); 240 | } 241 | 242 | temp++; 243 | } 244 | 245 | dirs = g_list_first(dirs); 246 | while (dirs != NULL) { 247 | gchar *dir = dirs->data; 248 | nautilus_python_load_dir(module, dir); 249 | dirs = dirs->next; 250 | } 251 | 252 | g_list_free_full (dirs, g_free); 253 | } 254 | 255 | void 256 | nautilus_module_initialize(GTypeModule *module) { 257 | const gchar *env_string; 258 | 259 | env_string = g_getenv("NAUTILUS_PYTHON_DEBUG"); 260 | if (env_string != NULL) { 261 | nautilus_python_debug = g_parse_debug_string(env_string, 262 | nautilus_python_debug_keys, 263 | G_N_ELEMENTS (nautilus_python_debug_keys)); 264 | env_string = NULL; 265 | } 266 | 267 | debug_enter(); 268 | 269 | all_types = g_array_new(FALSE, FALSE, sizeof(GType)); 270 | 271 | nautilus_python_check_all_directories(module); 272 | } 273 | 274 | void 275 | nautilus_module_shutdown(void) { 276 | debug_enter(); 277 | 278 | if (Py_IsInitialized()) 279 | Py_Finalize(); 280 | 281 | g_array_free(all_types, TRUE); 282 | } 283 | 284 | void 285 | nautilus_module_list_types(const GType **types, 286 | int *num_types) { 287 | debug_enter(); 288 | 289 | *types = (GType*)all_types->data; 290 | *num_types = all_types->len; 291 | } 292 | -------------------------------------------------------------------------------- /src/nautilus-python.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* 3 | * Copyright (C) 2004,2005 Johan Dahlin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifndef NAUTILUS_PYTHON_H 21 | #define NAUTILUS_PYTHON_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | typedef enum { 28 | NAUTILUS_PYTHON_DEBUG_MISC = 1 << 0, 29 | } NautilusPythonDebug; 30 | 31 | extern NautilusPythonDebug nautilus_python_debug; 32 | 33 | #define debug(x) { if (nautilus_python_debug & NAUTILUS_PYTHON_DEBUG_MISC) \ 34 | g_printf( "nautilus-python:" x "\n"); } 35 | #define debug_enter() { if (nautilus_python_debug & NAUTILUS_PYTHON_DEBUG_MISC) \ 36 | g_printf("%s: entered\n", __FUNCTION__); } 37 | #define debug_enter_args(x, y) { if (nautilus_python_debug & NAUTILUS_PYTHON_DEBUG_MISC) \ 38 | g_printf("%s: entered " x "\n", __FUNCTION__, y); } 39 | 40 | extern PyTypeObject *_PyNautilusColumn_Type; 41 | #define PyNautilusColumn_Type (*_PyNautilusColumn_Type) 42 | 43 | extern PyTypeObject *_PyNautilusColumnProvider_Type; 44 | #define PyNautilusColumnProvider_Type (*_PyNautilusColumnProvider_Type) 45 | 46 | extern PyTypeObject *_PyNautilusInfoProvider_Type; 47 | #define PyNautilusInfoProvider_Type (*_PyNautilusInfoProvider_Type) 48 | 49 | extern PyTypeObject *_PyNautilusMenu_Type; 50 | #define PyNautilusMenu_Type (*_PyNautilusMenu_Type) 51 | 52 | extern PyTypeObject *_PyNautilusMenuItem_Type; 53 | #define PyNautilusMenuItem_Type (*_PyNautilusMenuItem_Type) 54 | 55 | extern PyTypeObject *_PyNautilusMenuProvider_Type; 56 | #define PyNautilusMenuProvider_Type (*_PyNautilusMenuProvider_Type) 57 | 58 | extern PyTypeObject *_PyNautilusPropertiesItem_Type; 59 | #define PyNautilusPropertiesItem_Type (*_PyNautilusPropertiesItem_Type) 60 | 61 | extern PyTypeObject *_PyNautilusPropertiesModel_Type; 62 | #define PyNautilusPropertiesModel_Type (*_PyNautilusPropertiesModel_Type) 63 | 64 | extern PyTypeObject *_PyNautilusPropertiesModelProvider_Type; 65 | #define PyNautilusPropertiesModelProvider_Type (*_PyNautilusPropertiesModelProvider_Type) 66 | 67 | extern PyTypeObject *_PyNautilusOperationHandle_Type; 68 | #define PyNautilusOperationHandle_Type (*_PyNautilusOperationHandle_Type) 69 | 70 | #endif /* NAUTILUS_PYTHON_H */ 71 | --------------------------------------------------------------------------------