diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..cd41265e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +end_of_line = lf + +[*.{css,html,scss,yml}] +indent_size = 2 + +[LICENSE] +insert_final_newline = false + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore index 5669ad3b..dad54b00 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -hamster@projecthamster.wordpress.com.zip -hamster-shell-extension.pot +docs/_test_build +docs/_build +build +dist diff --git a/.jshint.cfg b/.jshint.cfg new file mode 100644 index 00000000..122e90ca --- /dev/null +++ b/.jshint.cfg @@ -0,0 +1,3 @@ +{ + "moz": true +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..10a1f5fd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: python + +install: + - pip install -r requirements.pip + - npm i -g jshint + +script: + - make test-style + - make test-docs diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 00000000..2ddb5269 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,42 @@ +Credits +======= + +Development Lead +---------------- + +* Eric Goller + +Contributors +------------ + +Original Authors (pre 0.10.0) +------------------------------ +* Tom Baugis +* Jérôme Oufella +* Markus Koller + + +Original Contributors (pre 0.10.0) +----------------------------------- + +* Piotr Plenik +* dougle (github: @dougle) +* Alexander Hofbauer +* Martey Dodoo +* Andrew Stubbs +* Larissa Reis +* Aleksei Lissitsin +* udarnik386 (github: @udarnik386) +* Matías Croce +* msize (guthub: @msize) +* Stephen White +* Martin Mlynář +* Matt Molyneaux +* Raphaël Doursenaud +* Gregory DK +* juanmah (githu: @juanmah) +* Daniel Doblado +* WBTMagnum (github: @WBTMagnum) +* fosero (guthub: @fosero) +* Boris (github: @bwcknr) +* Lukáš Doktor diff --git a/CHANGES b/CHANGES deleted file mode 100644 index 501680ad..00000000 --- a/CHANGES +++ /dev/null @@ -1,91 +0,0 @@ -Build 18 (Oct 03, 2015) -========================= - - * Fix GNOME 3.18 compatibility issues - -Build 17 (Jul 28, 2015) -========================= - - * Activities are now scrolled to the bottom when opening the menu (@rrthomas) - - -Build 12 (Oct 28, 2014) -========================= - - * The uploaded extension was missing subfolders - - -Build 11 (Oct 26, 2014) -========================= - - * Cosmetic fixes to the dropdown - set min-width and fix the input box - - -Build 10 (Oct 18, 2014) -========================= - - * Bump Gnome compatibility to 3.14. Seems to work - - -Build 8 (Sep 7, 2014) -========================= - - * Allow autocomplete after deltas and timestams (@ams-cs) - * update icons (@0rAX0) - * German locale (@bwcknr) - * Check tag presence correctly and avoid adding hashes to activities (@toupeira) - - -Build 7 (Mar 14, 2014) -========================= - - * Call the windows synchronously, so hopefully less of two-click "window is ready" - * Fix typo in Czechz locale (@Idoktor) - * Fix vertical label alignment in panel (@beanaroo) - * Fix too much shadow (HT @jfcahce) - - -Build 6 (Oct 22, 2013) -========================= - - * Update to 3.10 (patch by @exine) - - -Build 5 (Jul 7, 2013) -========================= - - * Update to 3.8 (patches by @aleho and @WBTMagnum) - - -Build 4.4 (Dec 32, 2012) -========================= - - * Fix bug where unlock screen breaks just because we changed stage focus upon - showing menu (issue #50) - * Attempt to fix bug with locale switching to english when hamster is running - - -Build 4.2, 4.3 (Nov 8, 2012) -============================= - - * Properly kill the refresh timeout on disabling - - -Build 4, 4.1 (Oct 30, 2012) -============================ - - * Switch over to Gnome Shell 3.6+ - - -Build 3 (Oct 4, 2012) -====================== - - * "Add earlier activity" now back in the dropdown - * reactivating task via extension now also includes tags and the description - * global hotkey is back - tweak it via extension preferences page - - -Build 2 (Aug 13, 2012) -======================= - - * initial push to extensions.gnome.org diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..a5b9bb44 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,89 @@ +============ +Contributing +============ +Contributions are welcome, and they are greatly appreciated! Every +little bit helps, and credit will always be given. + +You can contribute in many ways: + +Types of Contributions +---------------------- + +Report Bugs +~~~~~~~~~~~ +Report bugs at the +`issue tracker `_. + +If you are reporting a bug, please include: + +- Your operating system name and version. +- Any details about your local setup that might be helpful in troubleshooting. +- Detailed steps to reproduce the bug. + +Fix Bugs +~~~~~~~~ +Look through the GitHub issues for bugs. Anything tagged with "bug" +is open to whoever wants to implement it. + +Implement Features +~~~~~~~~~~~~~~~~~~ +Look through the GitHub issues for features. + +Write Documentation +~~~~~~~~~~~~~~~~~~~ +*Hamster-Shell-Extension* could always use more documentation, whether as part +of the official docs, in docstrings, or even on the web in blog posts, +articles, and such. + +Submit Feedback +~~~~~~~~~~~~~~~ +The best way to send feedback is to +`file an issue `_. + +If you are proposing a feature: + +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that contributions + are welcome :) + +Get Started! +------------ +Ready to contribute? Here's how to set up `hamster-shell-extension` for local +development. + +For additional information on coding conventions and commit/PR best practices +please refer to the :doc:`styleguide `. + +#. Fork `the repository `_ + on github. +#. Clone your fork locally:: + + $ git clone git@github.com:your_name_here/hamster-shell-extension.git + +#. Create a branch for local development:: + + $ git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +#. Commit your changes and push your branch to GitHub:: + + $ git add . + $ git commit -m "Your detailed description of your changes." + $ git push origin name-of-your-bugfix-or-feature + +#. Submit a `Pull Request + `_ with your + branch. + +Pull Request Guidelines +----------------------- + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. Put + your new functionality into a function with a docstring, and add the + feature to the list in README.rst. +3. Add yourself to AUTHORS.rst. diff --git a/HISTORY.rst b/HISTORY.rst new file mode 100644 index 00000000..1d5a49aa --- /dev/null +++ b/HISTORY.rst @@ -0,0 +1,104 @@ +.. :changelog: + +History +======== + +Version 0.10.0 +---------------------------------------------------------------------------------------------------------------------------------------------------------- +As this release contains a massive refactoring effort as well as a general overhaul of the entire project, the following +gives just general overview over the most significant changes. For a full list of all closed issued please refer to the +`issue tracker `_. + +- Switch to a semantic versioning scheme (starting at ``0.10.0``). +- Provide an all new ``Makefile`` that handles common development and packaging tasks. +- Support ``gnome-shell`` versions up to ``3.28``. +- Add 'weblate.org' translation workflow. +- Provide basic documentation. +- Improve handling of async dbus calls. +- Update copyright notices. +- ``JSHint`` compliance (enforced by CI setup). +- Fix multiple ``variable redeclaration error`` s. +- Switch to a new UUID for the extension (``contact@projecthamster.org``). +- Split codebase over multiple files for clarity. +- Show extension version in preference dialog. +- Removed obsolete legacy imports. + + + +Build 18 (2015-10-03) +------------------------ +- Fix GNOME Shell 3.18 compatibility issues. + +Build 17 (2015-07-28) +---------------------- +- Activities are now scrolled to the bottom when opening the menu (@rrthomas). + + +Build 12 (2014-10-28) +----------------------- +- The uploaded extension was missing subfolders. + + +Build 11 (2014-10-26) +--------------------- +- Cosmetic fixes to the dropdown - set ``min-width`` and fix the input box. + + +Build 10 (2014-10-18) +---------------------- + * Bump Gnome compatibility to 3.14. + + +Build 8 (2014-09-07) +--------------------- +- Allow autocomplete after deltas and timestams (@ams-cs). +- Update icons (@0rAX0). +- German locale (@bwcknr). +- Check tag presence correctly and avoid adding hashes to activities (@toupeira). + + +Build 7 (2014-03-14) +--------------------- +- Call the windows synchronously, so hopefully less of two-click "window is ready". +- Fix typo in Czechz locale (@Idoktor). +- Fix vertical label alignment in panel (@beanaroo). +- Fix too much shadow (HT @jfcahce). + + +Build 6 (2013-10-22) +--------------------- +- Update to 3.10 (patch by @exine). + + +Build 5 (2013-07-07) +--------------------- +- Update to 3.8 (patches by @aleho and @WBTMagnum). + + +Build 4.4 (2012-12-31) +----------------------- +- Fix bug where unlock screen breaks just because we changed stage focus upon + showing menu (issue #50). +- Attempt to fix bug with locale switching to english when hamster is running. + + +Build 4.2, 4.3 (2012-11-08) +--------------------------- +- Properly kill the refresh timeout on disabling. + + +Build 4, 4.1 (2012-10-30) +---------------------------- +- Switch over to GNOME Shell 3.6+. + + +Build 3 (2012-10-04) +--------------------- +- "Add earlier activity" now back in the dropdown. +- Reactivating task via extension now also includes tags and the description. +- Global hotkey is back - tweak it via extension preferences page. + + +Build 2 (2012-08-13) +--------------------- +- Initial push to ``extensions.gnome.org``. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..b2981267 --- /dev/null +++ b/Makefile @@ -0,0 +1,94 @@ +# Sphinx directories +SPHINX_BUILDDIR = _build +SPHINX_TEST_SPHINX_BUILDDIR = _test_build + +# Directory to collect all sourc file to in order to build. +BUILDDIR = build +# Directory to save a 'ready to deploy extension' archive +DISTDIR = dist + +# Script to lauch a browser in order to open passed path. +define BROWSER_PYSCRIPT +import os, webbrowser, sys +try: + from urllib import pathname2url +except: + from urllib.request import pathname2url + +webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) +endef +export BROWSER_PYSCRIPT + +BROWSER := python -c "$$BROWSER_PYSCRIPT" + +.PHONY: docs clean + +.NOTPARALLEL: clean-build collect + +help: + @echo "Please use 'make ' where is one of" + @echo " clean" + @echo " clean-build to clean the build directory of any leftovers." + @echo " clean-docs" + @echo " collect to collect all required files to the build directory." + @echo " compile to compile file that needs to be shipped as a binary." + @echo " develop to install (or update) all packages required for development" + @echo " dist to package a release as a ready to deploy extension archive" + @echo " gettext-catalogue to generate a new gettext catalogue" + @echo " open-docs to build and open the documentation" + @echo " test-style to run the code against a set of stylechecks and linter." + @echo " (Requires JSHint)." + @echo " test-docs to run automated tests on the documentation." + +clean: clean-build clean-docs clean-test-docs + rm -f dist/* + +clean-build: + rm -fr build + +clean-docs: + $(MAKE) -C docs clean SPHINX_BUILDDIR=$(SPHINX_BUILDDIR) + +clean-test-docs: + $(MAKE) -C docs clean SPHINX_BUILDDIR=$(SPHINX_TEST_SPHINX_BUILDDIR) + +collect: + mkdir -p $(BUILDDIR) + cp -R extension/* $(BUILDDIR) + cp -R data/* $(BUILDDIR) + wget https://gitlab.gnome.org/GNOME/gnome-shell-extensions/raw/master/lib/convenience.js -O $(BUILDDIR)/convenience.js + +compile: collect + glib-compile-schemas $(BUILDDIR)/schemas + find $(BUILDDIR) -name \*.po -execdir msgfmt hamster-shell-extension.po -o hamster-shell-extension.mo \; + +gettext-catalogue: + find ./extension/ -type f -name '*.js' -print > list + xgettext -L JavaScript --from-code=UTF-8 --files-from=list -k_ -kN_ -o ./messages.pot + rm ./list + +develop: + pip install -U pip setuptools wheel + pip install -U -r requirements.pip + +dist: clean-build compile +# We need to do this like this as 'zip' always uses the cwd as archive root. +# And for the extension to work extension.js etc. need to be at the root. + mkdir -p $(DISTDIR); + cd $(BUILDDIR); zip -rq ../dist/contact@projecthamster.org.zip ./* + cd $(BUILDDIR); tar -czf ../dist/contact@projecthamster.org.tgz * + @ls -l dist + +docs: + $(MAKE) -C docs clean + $(MAKE) -C docs html + +open-docs: docs + $(BROWSER) docs/_build/html/index.html + +test-docs: + make docs SPHINX_BUILDDIR=$(SPHINX_TEST_SPHINX_BUILDDIR) SPHINXOPTS='-W' + make -C docs linkcheck SPHINX_BUILDDIR=$(SPHINX_TEST_SPHINX_BUILDDIR) + +test-style: + jshint --config .jshint.cfg extension/ diff --git a/README.md b/README.md deleted file mode 100644 index dd2506c5..00000000 --- a/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Hamster Gnome Shell extension - -Simple Hamster shell extension for Gnome 3. - -## Install - -**The extension is available on extensions.gnome.org:** -https://extensions.gnome.org/extension/425/project-hamster-extension/ - -Current compatible Gnome shell version: 3.18 -For previous shell versions check [releases](https://github.com/projecthamster/shell-extension/tags). - -#### Manual installation for testing and development - -To install just symlink the checkout folder into `~/.local/share/gnome-shell/extensions/` as `hamster@projecthamster.wordpress.com`. It looks something like this: - -```sh -cd ~/.local/share/gnome-shell/extensions/ -ln -s /path/to/the/checkout hamster@projecthamster.wordpress.com -``` - -After that you can enable the extension and change the preferences using Tweak Tool, or on https://extensions.gnome.org/local/ - -## Contributing - -1. [Fork](https://github.com/projecthamster/shell-extension/fork) this project -2. Create a topic branch - `git checkout -b my_branch` -3. Push to your branch - `git push origin my_branch` -4. Submit a [Pull Request](https://github.com/projecthamster/shell-extension/pulls) with your branch -5. That's it! - -## Release process - -1. Bump version and Gnome Shell compatibility in `metadata.json` -2. Commit your changes and run `./build.sh` -3. Go to https://extensions.gnome.org/upload/ and upload the generated file `hamster@projecthamster.wordpress.com.zip` -5. You can check the review progress at https://extensions.gnome.org/review/ diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..b12e26e2 --- /dev/null +++ b/README.rst @@ -0,0 +1,76 @@ +Hamster Gnome Shell extension +=============================== + +A Simple Hamster shell extension for Gnome 3. + +Important: Testers needed +------------------------- +One of the main reasons development on this extension is slow is that there is +hardly any testing for feature/bugfix branches. As automated tests are not +really an options we would love to **hear from you** if you would be willing to +take new feature branches for a test drive and provide some feedback every now +and then. **Please get in touch!** + +Usage +----- +Quick categorization of activities is done by entering your activity in the +following format: 'activity@category, description #tag1 #tag2', where the comma +is mandatory when adding a description and/or tag(s). + +Install +-------- + +Dependencies +~~~~~~~~~~~~ +Because *Hamster-Shell-Extension* is just a frontend to the hamster dbus +service the presence of `hamster-time-tracker +`_ is required. You can verify that +the relevant dbus services are up and running by issuing ``ps aux | grep +hamster`` which should bring up ``hamster-service`` and +``hamster-windows-service``. + +Install For Production +~~~~~~~~~~~~~~~~~~~~~~~ +The extension is available on `the central extension repository `_. + +Current compatible Gnome shell version: 3.28 +For previous shell versions check `releases `_. + +Creating a development environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +As ``hamster-shell-extension`` is mainly simple JS there is not much of a development +setup needed if you just want to get hacking right away. We do however provide +a few convenience functionalities that make documenting and releasing the extension +easier. For those purposes some additional python packages are required. +The easiest and cleanest way to go about this is to create a new virtual environment and activate +it:: + + python3 -m venv .venv + source .venv/bin/activate + +Now you are all setup to run ``make develop`` and related make targets without +changing you main environment. + +Manual Installation For Testing and Development +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Clone the repository:: + + git clone https://github.com/projecthamster/hamster-shell-extension.git + +Make sure you are on the development branch:: + + git checkout develop + +Build a fresh distribution package:: + + make dist + +This will create a distributable archive. +You can now use the ``tweaktool`` (at the bottom of the ``extensions`` tab) +to install and activate the new ``zip`` file located in the ``dist`` directory. + +Alternatively you just can unpack the tar archive to ``~/.local/share/gnome-shell/extensions/``. +As a result, a directory named ``contact@projecthamster.org`` should be there now. + +After that you can enable the extension and change the preferences using Tweak +Tool, or on ``https://extensions.gnome.org/local/`` diff --git a/build.sh b/build.sh deleted file mode 100755 index 87cba357..00000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# build zip suitable for upload to extensions.gnome.org -zip -r hamster@projecthamster.wordpress.com.zip * diff --git a/convenience.js b/convenience.js deleted file mode 100644 index 82843b6f..00000000 --- a/convenience.js +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */ -/* - Copyright (c) 2011-2012, Giovanni Campagna - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the GNOME nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -const Gettext = imports.gettext; -const Gio = imports.gi.Gio; - -const Config = imports.misc.config; -const ExtensionUtils = imports.misc.extensionUtils; - -/** - * initTranslations: - * @domain: (optional): the gettext domain to use - * - * Initialize Gettext to load translations from extensionsdir/locale. - * If @domain is not provided, it will be taken from metadata['gettext-domain'] - */ -function initTranslations(domain) { - let extension = ExtensionUtils.getCurrentExtension(); - - domain = domain || extension.metadata['gettext-domain']; - - // check if this extension was built with "make zip-file", and thus - // has the locale files in a subfolder - // otherwise assume that extension has been installed in the - // same prefix as gnome-shell - let localeDir = extension.dir.get_child('locale'); - if (localeDir.query_exists(null)) - Gettext.bindtextdomain(domain, localeDir.get_path()); - else - Gettext.bindtextdomain(domain, Config.LOCALEDIR); -} - -/** - * getSettings: - * @schema: (optional): the GSettings schema id - * - * Builds and return a GSettings schema for @schema, using schema files - * in extensionsdir/schemas. If @schema is not provided, it is taken from - * metadata['settings-schema']. - */ -function getSettings(schema) { - let extension = ExtensionUtils.getCurrentExtension(); - - schema = schema || extension.metadata['settings-schema']; - - const GioSSS = Gio.SettingsSchemaSource; - - // check if this extension was built with "make zip-file", and thus - // has the schema files in a subfolder - // otherwise assume that extension has been installed in the - // same prefix as gnome-shell (and therefore schemas are available - // in the standard folders) - let schemaDir = extension.dir.get_child('schemas'); - let schemaSource; - if (schemaDir.query_exists(null)) - schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), - GioSSS.get_default(), - false); - else - schemaSource = GioSSS.get_default(); - - let schemaObj = schemaSource.lookup(schema, true); - if (!schemaObj) - throw new Error('Schema ' + schema + ' could not be found for extension ' - + extension.metadata.uuid + '. Please check your installation.'); - - return new Gio.Settings({ settings_schema: schemaObj }); -} - diff --git a/images/hamster-idle-symbolic.svg b/data/images/hamster-idle-symbolic.svg similarity index 100% rename from images/hamster-idle-symbolic.svg rename to data/images/hamster-idle-symbolic.svg diff --git a/images/hamster-tracking-symbolic.svg b/data/images/hamster-tracking-symbolic.svg similarity index 100% rename from images/hamster-tracking-symbolic.svg rename to data/images/hamster-tracking-symbolic.svg diff --git a/data/locale/cs/LC_MESSAGES/hamster-shell-extension.po b/data/locale/cs/LC_MESSAGES/hamster-shell-extension.po new file mode 100644 index 00000000..2ed5e2e7 --- /dev/null +++ b/data/locale/cs/LC_MESSAGES/hamster-shell-extension.po @@ -0,0 +1,64 @@ +msgid "" +msgstr "" +"Project-Id-Version: hamster-shell-extension\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-12 01:39+0100\n" +"PO-Revision-Date: 2018-04-10 10:49+0000\n" +"Last-Translator: Pavel Borecki \n" +"Language-Team: Czech \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Weblate 2.20\n" + +#: widgets/factsBox.js:58 +msgid "What are you doing?" +msgstr "Co děláte?" + +#: extension.js:105 +msgid "Today's activities" +msgstr "Dnešní aktivity" + +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Zadejte aktivitu…" + +#: widgets/panelWidget.js:82 +msgid "Loading..." +msgstr "Načítání…" + +#: widgets/panelWidget.js:103 +msgid "Show Overview" +msgstr "Zobrazit přehled" + +#: widgets/panelWidget.js:110 +msgid "Stop Tracking" +msgstr "Zastavit sledování" + +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Zadat dřívější aktivity" + +#: widgets/panelWidget.js:121 +msgid "Tracking Settings" +msgstr "Nastavení sledování" + +#: widgets/panelWidget.js:227 +msgid "No activity" +msgstr "Žádná aktivita" + +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "hamster-shell-extension: „hamster-service“ není spuštěná. Ukončuje se." + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" +"hamster-shell-extension: „hamster-windows-service“ není spuštěná. Ukončuje " +"se." diff --git a/data/locale/de/LC_MESSAGES/hamster-shell-extension.po b/data/locale/de/LC_MESSAGES/hamster-shell-extension.po new file mode 100644 index 00000000..013829ea --- /dev/null +++ b/data/locale/de/LC_MESSAGES/hamster-shell-extension.po @@ -0,0 +1,64 @@ +msgid "" +msgstr "" +"Project-Id-Version: hamster-shell-extension\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-12 01:37+0100\n" +"PO-Revision-Date: 2018-04-10 11:56+0000\n" +"Last-Translator: Eric Goller \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 2.20\n" + +#: widgets/factsBox.js:58 +msgid "What are you doing?" +msgstr "Was machen Sie gerade?" + +#: widgets/factsBox.js:66 +msgid "Today's activities" +msgstr "Heutige Tätigkeiten" + +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Tätigkeit starten..." + +#: widgets/panelWidget.js:82 +msgid "Loading..." +msgstr "Lade…" + +#: widgets/panelWidget.js:103 +msgid "Show Overview" +msgstr "Übersicht anzeigen" + +#: widgets/panelWidget.js:110 +msgid "Stop Tracking" +msgstr "Erfassung anhalten" + +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Frühere Tätigkeit hinzufügen" + +#: widgets/panelWidget.js:121 +msgid "Tracking Settings" +msgstr "Erfassungseinstellungen" + +#: widgets/panelWidget.js:227 +msgid "No activity" +msgstr "Keine Tätigkeit" + +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" +"hamster-shell-extension: \"hamster-service\" läuft nicht. Fahre herunter." + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" +"hamster-shell-extension: \"hamster-window-service\" läuft nicht. Fahre " +"herunter." diff --git a/locale/es/LC_MESSAGES/hamster-shell-extension.po b/data/locale/es/LC_MESSAGES/hamster-shell-extension.po similarity index 56% rename from locale/es/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/es/LC_MESSAGES/hamster-shell-extension.po index b31c9039..69a5e7c1 100644 --- a/locale/es/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/es/LC_MESSAGES/hamster-shell-extension.po @@ -2,49 +2,59 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 16:42+0100\n" -"PO-Revision-Date: 2015-10-24 15:02-0300\n" +"POT-Creation-Date: 2018-03-12 01:39+0100\n" +"PO-Revision-Date: 2018-03-12 01:39+0100\n" "Last-Translator: Matías Croce \n" "Language-Team: Piotr Plenik \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 1.8.6\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" -#: extension.js:85 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "¿Que estas haciendo?" -#: extension.js:91 -msgid "Enter activity..." -msgstr "Introduzca actividad..." - -#: extension.js:105 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Actividades de hoy" -#: extension.js:215 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Introduzca actividad..." + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Cargando..." -#: extension.js:238 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Mostrar Resumen" -#: extension.js:243 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Detener Seguimiento" -#: extension.js:249 +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Añadir Actividad Anterior" + +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Preferencias" -#: extension.js:398 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Sin actividad" -#: extension.js:254 -msgid "Add Earlier Activity" -msgstr "Añadir Actividad Anterior" +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/locale/fr/LC_MESSAGES/hamster-shell-extension.po b/data/locale/fr/LC_MESSAGES/hamster-shell-extension.po similarity index 59% rename from locale/fr/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/fr/LC_MESSAGES/hamster-shell-extension.po index 13500a73..7150be1f 100644 --- a/locale/fr/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/fr/LC_MESSAGES/hamster-shell-extension.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 16:42+0100\n" -"PO-Revision-Date: 2012-11-13 12:27+0100\n" +"POT-Creation-Date: 2018-03-12 01:40+0100\n" +"PO-Revision-Date: 2018-03-12 01:40+0100\n" "Last-Translator: Raphaël Doursenaud \n" "Language-Team: français <>\n" "Language: fr_FR\n" @@ -14,40 +14,49 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -"X-Generator: Gtranslator 2.91.5\n" -#: extension.js:85 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "Que faîtes vous ?" -#: extension.js:91 -msgid "Enter activity..." -msgstr "Entrez l'activité…" - -#: extension.js:105 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Activités du jour" -#: extension.js:215 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Entrez l'activité…" + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Chargement…" -#: extension.js:238 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Montrer le résumé" -#: extension.js:243 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Arrêter le suivi" -#: extension.js:249 +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Ajouter une activité antérieure" + +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Préférences" -#: extension.js:398 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Pas d'activité" -#: extension.js:254 -msgid "Add Earlier Activity" -msgstr "Ajouter une activité antérieure" +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/data/locale/messages.pot b/data/locale/messages.pot new file mode 100644 index 00000000..a3ae48c4 --- /dev/null +++ b/data/locale/messages.pot @@ -0,0 +1,64 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-04-10 12:06+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: extension/widgets/factsBox.js:61 +msgid "What are you doing?" +msgstr "" + +#: extension/widgets/factsBox.js:69 +msgid "Today's activities" +msgstr "" + +#: extension/widgets/ongoingFactEntry.js:51 +msgid "Enter activity..." +msgstr "" + +#: extension/widgets/panelWidget.js:85 +msgid "Loading..." +msgstr "" + +#: extension/widgets/panelWidget.js:106 +msgid "Show Overview" +msgstr "" + +#: extension/widgets/panelWidget.js:113 +msgid "Stop Tracking" +msgstr "" + +#: extension/widgets/panelWidget.js:118 +msgid "Add Earlier Activity" +msgstr "" + +#: extension/widgets/panelWidget.js:124 +msgid "Tracking Settings" +msgstr "" + +#: extension/widgets/panelWidget.js:231 +msgid "No activity" +msgstr "" + +#: extension/extension.js:153 extension/extension.js:154 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension/extension.js:162 extension/extension.js:163 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/data/locale/nb_NO/LC_MESSAGES/hamster-shell-extension.po b/data/locale/nb_NO/LC_MESSAGES/hamster-shell-extension.po new file mode 100644 index 00000000..c2d20b55 --- /dev/null +++ b/data/locale/nb_NO/LC_MESSAGES/hamster-shell-extension.po @@ -0,0 +1,63 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-04-10 12:06+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: nb_NO\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: extension/widgets/factsBox.js:61 +msgid "What are you doing?" +msgstr "" + +#: extension/widgets/factsBox.js:69 +msgid "Today's activities" +msgstr "" + +#: extension/widgets/ongoingFactEntry.js:51 +msgid "Enter activity..." +msgstr "" + +#: extension/widgets/panelWidget.js:85 +msgid "Loading..." +msgstr "" + +#: extension/widgets/panelWidget.js:106 +msgid "Show Overview" +msgstr "" + +#: extension/widgets/panelWidget.js:113 +msgid "Stop Tracking" +msgstr "" + +#: extension/widgets/panelWidget.js:118 +msgid "Add Earlier Activity" +msgstr "" + +#: extension/widgets/panelWidget.js:124 +msgid "Tracking Settings" +msgstr "" + +#: extension/widgets/panelWidget.js:231 +msgid "No activity" +msgstr "" + +#: extension/extension.js:153 extension/extension.js:154 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension/extension.js:162 extension/extension.js:163 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/locale/nl/LC_MESSAGES/hamster-shell-extension.po b/data/locale/nl/LC_MESSAGES/hamster-shell-extension.po similarity index 54% rename from locale/nl/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/nl/LC_MESSAGES/hamster-shell-extension.po index 7c2bac40..2095166a 100644 --- a/locale/nl/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/nl/LC_MESSAGES/hamster-shell-extension.po @@ -2,9 +2,9 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-06-18 13:27+0200\n" -"PO-Revision-Date: 2014-06-18 13:33+0200\n" -"Last-Translator: \n" +"POT-Creation-Date: 2018-03-12 01:42+0100\n" +"PO-Revision-Date: 2018-03-12 01:42+0100\n" +"Last-Translator: bwcknr@gmail.com\n" "Language-Team: Dutch\n" "Language: nl\n" "MIME-Version: 1.0\n" @@ -12,38 +12,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: extension.js:98 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "Wat ben je aan het doen?" -#: extension.js:104 -msgid "Enter activity..." -msgstr "Activiteit starten..." - -#: extension.js:115 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Huidige activiteiten" -#: extension.js:254 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Activiteit starten..." + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Laden..." -#: extension.js:278 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Overzicht tonen" -#: extension.js:283 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Registratie stoppen" -#: extension.js:288 +#: widgets/panelWidget.js:115 msgid "Add Earlier Activity" msgstr "Eerdere activiteit toevoegen" -#: extension.js:294 +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Registratieinstellingen" -#: extension.js:456 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Geen activiteit" + +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/locale/pl/LC_MESSAGES/hamster-shell-extension.po b/data/locale/pl/LC_MESSAGES/hamster-shell-extension.po similarity index 56% rename from locale/pl/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/pl/LC_MESSAGES/hamster-shell-extension.po index b211db24..72f35c1d 100644 --- a/locale/pl/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/pl/LC_MESSAGES/hamster-shell-extension.po @@ -2,51 +2,59 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 16:42+0100\n" -"PO-Revision-Date: 2012-10-01 11:14+0100\n" +"POT-Creation-Date: 2018-03-12 01:42+0100\n" +"PO-Revision-Date: 2018-03-12 01:42+0100\n" "Last-Translator: Daniel Craig \n" "Language-Team: Piotr Plenik \n" -"Language: \n" +"Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Poedit-Language: Polish\n" -"X-Poedit-Country: Poland\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" -#: extension.js:85 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "Co robisz?" -#: extension.js:91 -msgid "Enter activity..." -msgstr "Wpisz aktywność..." - -#: extension.js:105 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Dzisiejsze aktywności" -#: extension.js:215 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Wpisz aktywność..." + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Ładowanie..." -#: extension.js:238 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Przegląd sprawozdania" -#: extension.js:243 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Zatrzymaj śledzenie" -#: extension.js:249 +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Dodaj wcześniejszą czynność" + +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Preferencje programu" -#: extension.js:398 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Brak aktywności" -#: extension.js:254 -msgid "Add Earlier Activity" -msgstr "Dodaj wcześniejszą czynność" +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po b/data/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po similarity index 57% rename from locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po index 2dc84c56..538d0d4b 100644 --- a/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.po @@ -2,49 +2,57 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-07 10:04-0300\n" -"PO-Revision-Date: 2015-10-07 10:15-0300\n" +"POT-Creation-Date: 2018-03-12 01:43+0100\n" +"PO-Revision-Date: 2018-03-12 01:43+0100\n" +"Last-Translator: Fábio Nogueira \n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Last-Translator: Fábio Nogueira \n" -"Language-Team: \n" -"X-Generator: Poedit 1.7.5\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"Language: pt_BR\n" -#: extension.js:98 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "O que você está fazendo?" -#: extension.js:104 -msgid "Enter activity..." -msgstr "Digite a atividade..." - -#: extension.js:115 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Atividades de hoje" -#: extension.js:261 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Digite a atividade..." + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Carregando..." -#: extension.js:285 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Exibir visão geral" -#: extension.js:290 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Parar rastreamento" -#: extension.js:295 +#: widgets/panelWidget.js:115 msgid "Add Earlier Activity" msgstr "Adicionar atividade anterior" -#: extension.js:301 +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Configurações do rastreamento" -#: extension.js:463 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Sem atividade" + +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/locale/ru/LC_MESSAGES/hamster-shell-extension.po b/data/locale/ru/LC_MESSAGES/hamster-shell-extension.po similarity index 57% rename from locale/ru/LC_MESSAGES/hamster-shell-extension.po rename to data/locale/ru/LC_MESSAGES/hamster-shell-extension.po index aa44f5b4..eea96ef0 100644 --- a/locale/ru/LC_MESSAGES/hamster-shell-extension.po +++ b/data/locale/ru/LC_MESSAGES/hamster-shell-extension.po @@ -2,49 +2,59 @@ msgid "" msgstr "" "Project-Id-Version: hamster-shell-extension\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 16:42+0100\n" -"PO-Revision-Date: 2015-02-25 01:15+0300\n" +"POT-Creation-Date: 2018-03-12 01:43+0100\n" +"PO-Revision-Date: 2018-03-12 01:43+0100\n" "Last-Translator: Daniel Craig \n" "Language-Team: Egor Garadja \n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 1.7.4\n" -"Language: ru\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: extension.js:85 +#: widgets/factsBox.js:58 msgid "What are you doing?" msgstr "Чем вы заняты?" -#: extension.js:91 -msgid "Enter activity..." -msgstr "Занятие..." - -#: extension.js:105 +#: widgets/factsBox.js:66 msgid "Today's activities" msgstr "Сегодня" -#: extension.js:215 +#: widgets/ongoingFactEntry.js:48 +msgid "Enter activity..." +msgstr "Занятие..." + +#: widgets/panelWidget.js:82 msgid "Loading..." msgstr "Загрузка..." -#: extension.js:238 +#: widgets/panelWidget.js:103 msgid "Show Overview" msgstr "Обзор" -#: extension.js:243 +#: widgets/panelWidget.js:110 msgid "Stop Tracking" msgstr "Прекратить учёт" -#: extension.js:249 +#: widgets/panelWidget.js:115 +msgid "Add Earlier Activity" +msgstr "Добавить раннее занятие" + +#: widgets/panelWidget.js:121 msgid "Tracking Settings" msgstr "Параметры" -#: extension.js:398 +#: widgets/panelWidget.js:227 msgid "No activity" msgstr "Нет записей" -#: extension.js:254 -msgid "Add Earlier Activity" -msgstr "Добавить раннее занятие" +#: extension.js:113 extension.js:114 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension.js:122 extension.js:123 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/data/locale/sv/LC_MESSAGES/hamster-shell-extension.po b/data/locale/sv/LC_MESSAGES/hamster-shell-extension.po new file mode 100644 index 00000000..abed02de --- /dev/null +++ b/data/locale/sv/LC_MESSAGES/hamster-shell-extension.po @@ -0,0 +1,63 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-04-10 12:06+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: extension/widgets/factsBox.js:61 +msgid "What are you doing?" +msgstr "" + +#: extension/widgets/factsBox.js:69 +msgid "Today's activities" +msgstr "" + +#: extension/widgets/ongoingFactEntry.js:51 +msgid "Enter activity..." +msgstr "" + +#: extension/widgets/panelWidget.js:85 +msgid "Loading..." +msgstr "" + +#: extension/widgets/panelWidget.js:106 +msgid "Show Overview" +msgstr "" + +#: extension/widgets/panelWidget.js:113 +msgid "Stop Tracking" +msgstr "" + +#: extension/widgets/panelWidget.js:118 +msgid "Add Earlier Activity" +msgstr "" + +#: extension/widgets/panelWidget.js:124 +msgid "Tracking Settings" +msgstr "" + +#: extension/widgets/panelWidget.js:231 +msgid "No activity" +msgstr "" + +#: extension/extension.js:153 extension/extension.js:154 +msgid "hamster-shell-extension: 'hamster-service' not running. Shutting down." +msgstr "" + +#: extension/extension.js:162 extension/extension.js:163 +msgid "" +"hamster-shell-extension: 'hamster-windows-service' not running. Shutting " +"down." +msgstr "" diff --git a/metadata.json b/data/metadata.json similarity index 62% rename from metadata.json rename to data/metadata.json index 1cbbac44..5001f30b 100644 --- a/metadata.json +++ b/data/metadata.json @@ -1,5 +1,5 @@ { - "description": "Shell extension for Hamster - the GNOME time tracker. File your issues here: https://github.com/projecthamster/shell-extension/issues", + "description": "Shell extension for Hamster - the GNOME time tracker. File your issues here: https://github.com/projecthamster/hamster-shell-extension/issues", "extension-id": "project-hamster", "name": "Hamster Time Tracker", "original-authors": [ @@ -7,6 +7,7 @@ "jerome@oufella.com", "markus-koller@gmx.ch" ], + "gettext-domain": "hamster-shell-extension", "settings-schema": "org.gnome.shell.extensions.project-hamster", "shell-version": [ "3.10", @@ -14,9 +15,13 @@ "3.14", "3.16", "3.18", - "3.20" + "3.20", + "3.22", + "3.24", + "3.26", + "3.28" ], - "url": "https://github.com/projecthamster/shell-extension.git", - "uuid": "hamster@projecthamster.wordpress.com", - "version": 22 + "url": "https://github.com/projecthamster/hamster-shell-extension.git", + "uuid": "contact@projecthamster.org", + "version": "0.10.0" } diff --git a/schemas/org.gnome.shell.extensions.project-hamster.gschema.xml b/data/schemas/org.gnome.shell.extensions.project-hamster.gschema.xml similarity index 100% rename from schemas/org.gnome.shell.extensions.project-hamster.gschema.xml rename to data/schemas/org.gnome.shell.extensions.project-hamster.gschema.xml diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..83dad38d --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/authors.rst b/docs/authors.rst new file mode 100644 index 00000000..e122f914 --- /dev/null +++ b/docs/authors.rst @@ -0,0 +1 @@ +.. include:: ../AUTHORS.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100755 index 00000000..8ce48055 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# shell-extension documentation build configuration file, created by +# sphinx-quickstart on Tue Jul 9 22:26:36 2013. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import datetime +import shlex +import alabaster + +# If extensions (or modules to document with autodoc) are in another +# directory, add these directories to sys.path here. If the directory is +# relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# Get the project root dir, which is the parent dir of this +cwd = os.getcwd() +project_root = os.path.dirname(cwd) + +# Insert the project root dir as the first element in the PYTHONPATH. +# This lets us ensure that the source package is imported, and that its +# version is used. +sys.path.insert(0, project_root) + +# -- General configuration --------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Prevent non local immage warnings from showing +suppress_warnings = ['image.nonlocal_uri'] + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + #'sphinx.ext.autodoc', + #'sphinx.ext.viewcode', + #'sphinx.ext.todo', + #'sphinx.ext.coverage', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'hamster-shell-extension' +copyright = u'2016, Eric Goller' +author = u'Eric Goller' + +# The version info for the project you're documenting, acts as replacement +# for |version| and |release|, also used in various other places throughout +# the built documents. +# +# The short X.Y version. +version = '0.10.0' +# The full version, including alpha/beta/rc tags. +release = '0.10.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to +# some non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built +# documents. +#keep_warnings = False + + +# -- Options for HTML output ------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a +# theme further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + # Relative path (from $PROJECT/_static/) to a logo image, which will appear + # in the upper left corner above the name of the project. + # 'logo': 'logo.png', + 'logo_name': True, + 'description': 'Gnome-Shell-Extention for Project Hamster', + 'github_user': 'projecthamster', + 'github_repo': 'shell-extension', + 'github_button': True, + 'github_banner': False, + 'travis_button': False, + 'analytics_id': '', + 'show_related': False, + 'show_powered_by': True, +} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = [alabaster.get_path()] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as +# html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the +# top of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon +# of the docs. This file should be a Windows icon file (.ico) being +# 16x16 or 32x32 pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) +# here, relative to this directory. They are copied after the builtin +# static files, so a file named "default.css" will overwrite the builtin +# "default.css". +#html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + 'donate.html', + ] +} + +# Additional templates that should be rendered to pages, maps page names +# to template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. +# Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. +# Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages +# will contain a tag referring to it. The value of this option +# must be the base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'shell-extensiondoc' + + +# -- Options for LaTeX output ------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + #'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', 'shell-extension.tex', + u'hamster-shell-extension Documentation', + u'Eric Goller', 'manual'), +] + +# The name of an image file (relative to this directory) to place at +# the top of the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings +# are parts, not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output ------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'shell-extension', + u'hamster-shell-extension Documentation', + [u'Eric Goller'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ---------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'shell-extension', + u'hamster-shell-extension Documentation', + u'Eric Goller', + 'shell-extension', + 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 00000000..e582053e --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst diff --git a/docs/hacking.rst b/docs/hacking.rst new file mode 100644 index 00000000..53692a8d --- /dev/null +++ b/docs/hacking.rst @@ -0,0 +1,36 @@ +Hacking +======== + +One of the great challenges developing or contributing to a *GNOME-Shell* +extension is the lack of documentation for it. +In order to be as inviting as possible and to allow you to get hacking on +*Hamster-Shell-Extension* without dwelling all to deep in the muddy waters of +*GNOME-Shell* development we aim to do two things. + +#. Highlight those parts of the code that are closely coupled with specific + parts of the *GNOME-Shell* framework. This way you have a good idea if there + are hidden constrains to be considered or if "what you see is what you get". +#. Provide some references and bits and pieces about various aspects of + *GNOME-Shell* development in general. This is particularly helpful if you + actually need to interact with the shell framework to get you work done. + +References +----------- +- `GNOME's own entry point for extension development `_ +- `Notes on i18n and preferences dialog `_. +- `UUID Guidelines `_ +- `GJS `_: Javascript Bindings for *GNOME*. This also mentions the possibility of tests. +- `A non trivial introduction `_ by mathematicalcoffee which + also explains some of the mandatory and optional bits and pieces. +- The closest to a practical `API Reference `_ + I could find. While not perfect it provides a most useful starting point if you know what kind of widget/feature you want + but not where to find it. Or of cause, if you want to know where to find out more about this obscure + line of code you just stumbled upon. +- `Additional information `_ on i18n and multi-file extensions +- Information on current `dbus best practices `_. + This is particular usefull as it mentions that ``Sync`` and ``Remote`` strings are appended to our interface method names. For a full tutorial + please `see refer to this one `_. +- `A comprehensive overview over the shell `_ that cover a wide range from general architecture to + the exact names of various parts of the "gnome shell screen". +- `GNOME's official page on shell development `_ is particularly useful in linking the different bits and pieces (css, tweeners, clutter, ...) togeter. +- `One of the most instructive series/tutorials `_ by Jasper St. Pierre. diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 00000000..25064996 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1 @@ +.. include:: ../HISTORY.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..0d0e386d --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,26 @@ +.. {{ cookiecutter.project_slug }} documentation master file, created by + sphinx-quickstart on Tue Jul 9 22:26:36 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Hamster-Shell-Extension's documentation! +==================================================== + +This documentation tries to help you use, improve and get involved with the +*Hamster-Shell-Extension*. + +As always: please feel encourage to summit an issue or PR if you spot an error +or feel that topics are not covered properly. + +.. toctree:: + :maxdepth: 2 + :hidden: + + readme + contributing + authors + hacking + history + packaging + versioning + styleguide diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..6746ac83 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/docs/packaging.rst b/docs/packaging.rst new file mode 100644 index 00000000..6a466d1f --- /dev/null +++ b/docs/packaging.rst @@ -0,0 +1,68 @@ +Packaging / Release Process +============================ +Here we document the packaging and release process. +This will most likely be of no interest to you, but is done for transparency +and internal documentation. + +Instead of having to go through the process manually we provide convenient make +targets for its individual steps as well as for the whole process (``make +dist``). + +TL;DR +-------- +#. Bump version: ``bumpversion [minor, patch]`` +#. Check and if needed bump Gnome Shell compatibility in ``metadata.json``. +#. Commit your changes and run ``make dist``. +#. Go to ``https://extensions.gnome.org/upload/`` and upload the generated file + ``contact@projecthamster.org.zip``. +#. You can check the review progress at ``https://extensions.gnome.org/review/``. + +Long Version +------------- +GNOME-Shell extensions require at lest an ``extension.js`` as well as a +``metadata.json`` file in their root directory. Besides other auxillary files +we opt to add ``prefs.js`` and a ``stylesheet.css`` as optional files that are +(if used) expected to have those names and be placed at the root directory as +well. +All actual source code is placed in the ``/extensions`` directory of the main +repository. + +File Location / Directory Structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Besides the actual source code, our project makes use of addition non-code +resources. In particular: Icon image files and ``gettext`` translations. All +these resources are organized in a separate ``/data`` directory of the root +repository. +Whilst this structure make for a clean and simple organization of our codebase +to work on, it is unfit to be used as a working *extension*. This requires some +addition packaging. + +Collect and Compile +~~~~~~~~~~~~~~~~~~~~~ +In order for GNOME-Shell to accept our extension properly two additional things +need to happen. + +#. We need to place the content of the ``data`` directory within the ``extension`` + directory (e.g. on the same level as ``extension.js``. +#. We need to compile the GSettings schema as well as gettext ``.po`` files so + as it actually their binary versions ``gschemas.compiled`` and ``*.mo`` files + that we deploy. + +In order not to mess with our existing clean structure, all this collecting and +compiling happens in a dedicated build directory which is not part of the +project repository. + +Packaging +~~~~~~~~~~ +Now that our build directory contains all required files in state expected by +GNOME-shell all that is left to do is to warp it up in a convenient easy to +deploy way. There are two relevant ways to deploy an extension. + +#. Via the official `gnome extensions repository `_. +#. Copying/symlinking the contend of the build directory to + ``~/.local/share/gnome-shell/extension/hamster@projecthamster.wordpreess.com`` + +In order to upload our release to ``extensions.gnome.org`` we wrap the build +directory in a zip file while we also provide a separate tarball of the same +content which can easily be used to copy/symlink its content in the +aforementioned way. diff --git a/docs/readme.rst b/docs/readme.rst new file mode 100644 index 00000000..72a33558 --- /dev/null +++ b/docs/readme.rst @@ -0,0 +1 @@ +.. include:: ../README.rst diff --git a/docs/styleguide.rst b/docs/styleguide.rst new file mode 100644 index 00000000..f96cdf41 --- /dev/null +++ b/docs/styleguide.rst @@ -0,0 +1,87 @@ +Style Guide +============ + +General +-------- +- Try to stick to 79 chars. When this is not enough you may use up to 99 chars. + This is more tolerable for code than for documentation. + +Code-style +-------------- +- Like the *GNOME-Shell* project we follow the `GJS coding style + `_ which in short + is: indent 4 spaces, no tabs, spaces after comma, no space after function + call (Emacs mode line for this: + ``/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */``. +- Imports should be at the top, in two groups, one for standard imports (like + ``imports.lang`` or ``imports.dbus``) and introspection, the other for Shell + API. Within the same group, put everything in alphabetic order. +- Readability trumps almost anything. Readable and approachable code carries + it's weight as a lower contribution-barrier, less bugs and easier + debugging. Having a particular clever, aka dense, alternative is rarely + warranted. +- Favour multiple small specialized (local) functions/methods over big + all-encompassing ones. +- Use well established and maintained high quality 3rd party libraries over own + implementations. +- Use expressive variable names. If you have to trade off verbosity and + expressiveness, go for the later. +- Assigning variables even if they are used only once can be preferable if + expressions become clearer and less dense. + +Documentation +--------------- +- We use `JSDoc syntax and blog tags `_ to document all + our code. +- Headings should capitalise each word. +- Please use ``-`` for unordered lists and ``#.`` for ordered lists unless you + really want to enforce a certain numbering. +- No blank lines after headings. + +Committing and commit messages +------------------------------ +- Commit one change/feature at a time (you can use `tig `_ + to select the changes you want to commit). +- Separate bug fixes from feature changes, bugfixes may need to be backported + to the stable branch. +- Maximum line length is 50 characters for the first line and 72 for all + following lines. +- The first line is a short summary, no trailing period. +- Leave a blank line between the summary and the body of the commit message. +- Explain what you did and add all relevant information to the commit message. +- If an issue exists for your feature/bug/task add it to the end of the commit + message. If your commit implements a feature use the ``closes`` keyword, if + it fixes a bug use ``fixes``. + +Rebasing +-------- +- Try to rebase to keep the commit history linear:: + + $ git pull --rebase + +- If you have uncommitted changes in your working directory use ``git stash`` + to stash the changes while rebasing:: + + $ git stash + $ git pull --rebase + $ git stash pop + +- Before you prepare your Pull Request, please squash (``git rebase -i + HEAD~XXX``) any commits that really belong to one functional unit. It is + perfectly fine (and preferable) if your PR has multiple modular and distinct + commits though. + +- **Do not** rebase already published changesets! + +Pull Requests +------------- +The title of a pull request should contain a summary of the issue it is related +to, as well as the prepended issue id in square brackets. An example would look like +``[#23] Advanced report options``. This way, a link between the PR and the +issue will be created. +Each PR that is not absolutly non-trivial should include at least a very short +description on what is done and why. Just imagine the kind of info you would +look for in you dig it up in 12 month time. + +Every pull request has to be approved by at least one other developer before +merging. diff --git a/docs/versioning.rst b/docs/versioning.rst new file mode 100644 index 00000000..72448810 --- /dev/null +++ b/docs/versioning.rst @@ -0,0 +1,23 @@ +Versioning +=========== +``hamster-shell-extension`` follows the `semantic versioning `_ scheme. +Each release is packaged and uploaded to the +`gnome extensions repository `_. +As this repository does not allow listing of multiple versions of the same +extension we will always provide the latest stable release. If you need a +different version, please check out the master branch on github. We provide +tags for all releases as well as convenient tarballs for manual installation. + +All current (pre 1.0) versions are targeted towards usage with ``hamter-time-tracker`` +(aka ``legacy hamster``). Upcoming support for the rewritten codebase (``hamster-lib`` and +friends) will be indicated by the ``1.0.0`` version. The goal for our work on the ``0.*.*`` +releases is to bring the repository/project up to standards with the rest of the project +in terms of documentation, tests and general infrastructure. + +About Previous Versioning +-------------------------- +Before version 0.10.0 *Hamster-Shell-Extension* used to handle versioning by +carrying a incrementing *build number*. In addition to that, *git tags* were +used on github (as releases) that indicate the (highest compatible) GNOME-Shell +version targeted by this release. Which actually is meta information and should +not be part of the versioning at all. diff --git a/extension.js b/extension.js deleted file mode 100644 index a406f4d9..00000000 --- a/extension.js +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Simple Hamster extension for gnome-shell - * Copyright (c) 2011 Jerome Oufella - * Copyright (c) 2011-2012 Toms Baugis - * Icons Artwork Copyright (c) 2012 Reda Lazri - * - * Portions originate from the gnome-shell source code, Copyright (c) - * its respectives authors. - * This project is released under the GNU GPL License. - * See COPYING for details. - * - */ - -// TODO - investigate usage of third party libs (d3/underscore/whatever) -// otherwise even most primitive operations are hardcore - -const Clutter = imports.gi.Clutter; -const Config = imports.misc.config; -const GLib = imports.gi.GLib; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; -const St = imports.gi.St; -const Shell = imports.gi.Shell; -const Meta = imports.gi.Meta; -const Main = imports.ui.main; -const Mainloop = imports.mainloop; -const Gio = imports.gi.Gio; -const PopupMenu = imports.ui.popupMenu; -const PanelMenu = imports.ui.panelMenu; -const Util = imports.misc.util; -const Gettext = imports.gettext.domain('hamster-shell-extension'); -const _ = Gettext.gettext; -const N_ = function(x) { return x; }; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Convenience = Me.imports.convenience; -const Stuff = Me.imports.stuff; - -// dbus-send --session --type=method_call --print-reply --dest=org.gnome.Hamster /org/gnome/Hamster org.freedesktop.DBus.Introspectable.Introspect -const ApiProxyIface = ' \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -'; - -let ApiProxy = Gio.DBusProxy.makeProxyWrapper(ApiProxyIface); - -// dbus-send --session --type=method_call --print-reply --dest=org.gnome.Hamster.WindowServer /org/gnome/Hamster/WindowServer org.freedesktop.DBus.Introspectable.Introspect -const WindowsProxyIface = ' \ - \ - \ - \ - \ - \ - \ - \ -'; - -let WindowsProxy = Gio.DBusProxy.makeProxyWrapper(WindowsProxyIface); - - - -/* a little box or something */ -function HamsterBox() { - this._init.apply(this, arguments); -} - -HamsterBox.prototype = { - __proto__: PopupMenu.PopupBaseMenuItem.prototype, - - _init: function(itemParams) { - PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {reactive: false}); - - let box = new St.BoxLayout({style_class: 'hamster-box'}); - box.set_vertical(true); - - let label = new St.Label({style_class: 'hamster-box-label'}); - label.set_text(_("What are you doing?")); - box.add(label); - - this._textEntry = new St.Entry({name: 'searchEntry', - can_focus: true, - track_hover: true, - hint_text: _("Enter activity..."), - style_class: "search-entry"}); - this._textEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); - this._textEntry.clutter_text.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent)); - - - box.add(this._textEntry); - - // autocomplete popup - couldn't spark it up just yet - //this._popup = new PopupMenu.PopupComboMenu(this._textEntry); - - label = new St.Label({style_class: 'hamster-box-label'}); - label.set_text(_("Today's activities")); - box.add(label); - - let scrollbox = new St.ScrollView({style_class: 'hamster-scrollbox'}); - this._scrollAdjustment = scrollbox.vscroll.adjustment; - box.add(scrollbox); - - // Since St.Table does not implement StScrollable, we create a - // container object that does. - let container = new St.BoxLayout({}); - container.set_vertical(true); - scrollbox.add_actor(container); - - this.activities = new St.Widget({style_class: 'hamster-activities', - layout_manager: new Clutter.TableLayout(), - reactive: true }); - container.add(this.activities); - - this.summaryLabel = new St.Label({style_class: 'summary-label'}); - box.add(this.summaryLabel); - - - this.actor.add_child(box); - - this.autocompleteActivities = []; - this.runningActivitiesQuery = null; - - this._prevText = ""; - }, - - focus: function() { - Mainloop.timeout_add(20, Lang.bind(this, function() { - // scroll the activities to the bottom - this._scrollAdjustment.value = this._scrollAdjustment.upper; - - // focus the text entry - global.stage.set_key_focus(this._textEntry); - })); - }, - - blur: function() { - global.stage.set_key_focus(null); - }, - - _onEntryActivated: function() { - this.emit('activate'); - this._textEntry.set_text(''); - }, - - - _getActivities: function() { - if (this.runningActivitiesQuery) - return this.autocompleteActivities; - - this.runningActivitiesQuery = true; - this.proxy.GetActivitiesRemote("", Lang.bind(this, function([response], err) { - this.runningActivitiesQuery = false; - this.autocompleteActivities = response; - })); - - return this.autocompleteActivities; - }, - - _onKeyReleaseEvent: function(textItem, evt) { - let symbol = evt.get_key_symbol(); - let text = this._textEntry.get_text().toLowerCase(); - let starttime = ""; - let activitytext = text; - - // Don't include leading times in the activity autocomplete - let match = []; - if ((match = text.match(/^\d\d:\d\d /)) || - (match = text.match(/^-\d+ /))) { - starttime = text.substring(0, match[0].length); - activitytext = text.substring(match[0].length); - } - - // if nothing has changed or we still have selection then that means - // that special keys are at play and we don't attempt to autocomplete - if (activitytext == "" || - this._prevText == text || - this._textEntry.clutter_text.get_selection()) { - return; - } - this._prevText = text; - - // ignore deletions - let ignoreKeys = [Clutter.BackSpace, Clutter.Delete, Clutter.Escape]; - for (var key of ignoreKeys) { - if (symbol == key) - return; - } - - - let allActivities = this._getActivities(); - for (var rec of allActivities) { - let completion = rec[0]; - if (rec[1].length > 0) - completion += "@" + rec[1]; - if (completion.toLowerCase().substring(0, activitytext.length) == activitytext) { - this.prevText = text; - completion = starttime + completion; - - this._textEntry.set_text(completion); - this._textEntry.clutter_text.set_selection(text.length, completion.length); - - this._prevText = completion.toLowerCase(); - - return; - } - } - } -}; - - - -/* Panel button */ -function HamsterExtension(extensionMeta) { - this._init(extensionMeta); -} - -HamsterExtension.prototype = { - __proto__: PanelMenu.Button.prototype, - - _init: function(extensionMeta) { - PanelMenu.Button.prototype._init.call(this, 0.0); - - this.extensionMeta = extensionMeta; - this._proxy = new ApiProxy(Gio.DBus.session, 'org.gnome.Hamster', '/org/gnome/Hamster'); - this._proxy.connectSignal('FactsChanged', Lang.bind(this, this.refresh)); - this._proxy.connectSignal('ActivitiesChanged', Lang.bind(this, this.refreshActivities)); - this._proxy.connectSignal('TagsChanged', Lang.bind(this, this.refresh)); - - - this._windowsProxy = new WindowsProxy(Gio.DBus.session, - "org.gnome.Hamster.WindowServer", - "/org/gnome/Hamster/WindowServer"); - - this._settings = Convenience.getSettings(); - - - this.panelContainer = new St.BoxLayout({style_class: "panel-box"}); - this.actor.add_actor(this.panelContainer); - this.actor.add_style_class_name('panel-status-button'); - - - this.panelLabel = new St.Label({text: _("Loading..."), - y_align: Clutter.ActorAlign.CENTER}); - this.currentActivity = null; - - // panel icon - this._trackingIcon = Gio.icon_new_for_string(this.extensionMeta.path + "/images/hamster-tracking-symbolic.svg"); - this._idleIcon = Gio.icon_new_for_string(this.extensionMeta.path + "/images/hamster-idle-symbolic.svg"); - - this.icon = new St.Icon({gicon: this._trackingIcon, - icon_size: 16, - style_class: "panel-icon"}); - - this.panelContainer.add(this.icon); - this.panelContainer.add(this.panelLabel); - - let item = new HamsterBox(); - item.connect('activate', Lang.bind(this, this._onActivityEntry)); - this.activityEntry = item; - this.activityEntry.proxy = this._proxy; // lazy proxying - - - this.menu.addMenuItem(item); - - // overview - item = new PopupMenu.PopupMenuItem(_("Show Overview")); - item.connect('activate', Lang.bind(this, this._onShowHamsterActivate)); - this.menu.addMenuItem(item); - - // stop tracking - item = new PopupMenu.PopupMenuItem(_("Stop Tracking")); - item.connect('activate', Lang.bind(this, this._onStopTracking)); - this.menu.addMenuItem(item); - - // add new task - item = new PopupMenu.PopupMenuItem(_("Add Earlier Activity")); - item.connect('activate', Lang.bind(this, this._onNewFact)); - this.menu.addMenuItem(item); - - // settings - this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - item = new PopupMenu.PopupMenuItem(_("Tracking Settings")); - item.connect('activate', Lang.bind(this, this._onShowSettingsActivate)); - this.menu.addMenuItem(item); - - - // focus menu upon display - this.menu.connect('open-state-changed', Lang.bind(this, - function(menu, open) { - if (open) { - this.activityEntry.focus(); - } else { - this.activityEntry.blur(); - } - } - )); - - - // load data - this.facts = null; - // refresh the label every 60 secs - this.timeout = GLib.timeout_add_seconds(0, 60, Lang.bind(this, this.refresh)); - this.refresh(); - }, - - show: function() { - this.menu.open(); - }, - - toggle: function() { - this.menu.toggle(); - }, - - refreshActivities: function(proxy, sender) { - this.activityEntry.autocompleteActivities = []; - this.refresh(); - }, - - refresh: function(proxy, sender) { - this._proxy.GetTodaysFactsRemote(Lang.bind(this, this._refresh)); - return true; - }, - - _refresh: function([response], err) { - let facts = []; - - if (err) { - log(err); - } else if (response.length > 0) { - facts = Stuff.fromDbusFacts(response); - } - - this.currentActivity = null; - let fact = null; - if (facts.length) { - fact = facts[facts.length - 1]; - if (!fact.endTime) - this.currentActivity = fact; - } - this.updatePanelDisplay(fact); - - let activities = this.activityEntry.activities; - activities.destroy_all_children(); // remove previous entries - - var i = 0; - let layout = activities.layout_manager; - for (var fact of facts) { - let label; - - label = new St.Label({style_class: 'cell-label'}); - let text = "%02d:%02d - ".format(fact.startTime.getHours(), fact.startTime.getMinutes()); - if (fact.endTime) { - text += "%02d:%02d".format(fact.endTime.getHours(), fact.endTime.getMinutes()); - } - label.set_text(text); - layout.pack(label, 0, i); - - label = new St.Label({style_class: 'cell-label'}); - label.set_text(fact.name + (0 < fact.tags.length ? (" #" + fact.tags.join(", #")) : "")); - layout.pack(label, 1, i); - - label = new St.Label({style_class: 'cell-label'}); - label.set_text(Stuff.formatDurationHuman(fact.delta)); - layout.pack(label, 2, i); - - - let icon; - let button; - - button = new St.Button({style_class: 'clickable cell-button'}); - - icon = new St.Icon({icon_name: "document-open-symbolic", - icon_size: 16}); - - button.set_child(icon); - button.fact = fact; - button.connect('clicked', Lang.bind(this, function(button, event) { - this._windowsProxy.editSync(GLib.Variant.new('i', [button.fact.id])); - this.menu.close(); - })); - layout.pack(button, 3, i); - - - if (!this.currentActivity || - this.currentActivity.name != fact.name || - this.currentActivity.category != fact.category || - this.currentActivity.tags.join(",") != fact.tags.join(",")) { - button = new St.Button({style_class: 'clickable cell-button'}); - - icon = new St.Icon({icon_name: "media-playback-start-symbolic", - icon_size: 16}); - - button.set_child(icon); - button.fact = fact; - - button.connect('clicked', Lang.bind(this, function(button, event) { - let factStr = button.fact.name - + "@" + button.fact.category - + ", " + (button.fact.description); - if (button.fact.tags.length) { - factStr += " #" + button.fact.tags.join(", #"); - } - - this._proxy.AddFactRemote(factStr, 0, 0, false, Lang.bind(this, function(response, err) { - // not interested in the new id - this shuts up the warning - })); - this.menu.close(); - })); - layout.pack(button, 4, i); - } - - i += 1; - } - - let byCategory = {}; - let categories = []; - for (var fact of facts) { - byCategory[fact.category] = (byCategory[fact.category] || 0) + fact.delta; - if (categories.indexOf(fact.category) == -1) - categories.push(fact.category); - } - - let label = ""; - for (var category of categories) { - label += category + ": " + Stuff.formatDurationHours(byCategory[category]) + ", "; - } - label = label.slice(0, label.length - 2); // strip trailing comma - this.activityEntry.summaryLabel.set_text(label); - }, - - - updatePanelDisplay: function(fact) { - // 0 = show label, 1 = show icon + duration, 2 = just icon - let appearance = this._settings.get_int("panel-appearance"); - - - if (appearance === 0) { - this.panelLabel.show(); - this.icon.hide(); - - if (fact && !fact.endTime) { - this.panelLabel.text = "%s %s".format(fact.name, Stuff.formatDuration(fact.delta)); - } else { - this.panelLabel.text = _("No activity"); - } - } else { - this.icon.show(); - if (appearance == 1) - this.panelLabel.hide(); - else - this.panelLabel.show(); - - - // updates panel label. if fact is none, will set panel status to "no activity" - if (fact && !fact.endTime) { - this.panelLabel.text = Stuff.formatDuration(fact.delta); - this.icon.gicon = this._trackingIcon; - } else { - this.panelLabel.text = ""; - this.icon.gicon = this._idleIcon; - } - } - }, - - - _onStopTracking: function() { - let now = new Date(); - let epochSeconds = Date.UTC(now.getFullYear(), - now.getMonth(), - now.getDate(), - now.getHours(), - now.getMinutes(), - now.getSeconds()); - epochSeconds = Math.floor(epochSeconds / 1000); - this._proxy.StopTrackingRemote(GLib.Variant.new('i', [epochSeconds])); - }, - - _onShowHamsterActivate: function() { - this._windowsProxy.overviewSync(); - }, - - _onNewFact: function() { - this._windowsProxy.editSync(GLib.Variant.new('i', [0])); - }, - - _onShowSettingsActivate: function() { - this._windowsProxy.preferencesSync(); - }, - - - _onActivityEntry: function() { - let text = this.activityEntry._textEntry.get_text(); - this._proxy.AddFactRemote(text, 0, 0, false, Lang.bind(this, function(response, err) { - // not interested in the new id - this shuts up the warning - })); - } -}; - - -function ExtensionController(extensionMeta) { - let dateMenu = Main.panel.statusArea.dateMenu; - - return { - extensionMeta: extensionMeta, - extension: null, - settings: null, - placement: 0, - activitiesText: null, - - enable: function() { - this.settings = Convenience.getSettings(); - this.extension = new HamsterExtension(this.extensionMeta); - - this.placement = this.settings.get_int("panel-placement"); - if (this.placement == 1) { - Main.panel.addToStatusArea("hamster", this.extension, 0, "center"); - - Main.panel._centerBox.remove_actor(dateMenu.container); - Main.panel._addToPanelBox('dateMenu', dateMenu, -1, Main.panel._rightBox); - - } else if (this.placement == 2) { - this._activitiesText = Main.panel._leftBox.get_children()[0].get_children()[0].get_children()[0].get_children()[0].get_text(); - Main.panel._leftBox.get_children()[0].get_children()[0].get_children()[0].get_children()[0].set_text(''); - Main.panel.addToStatusArea("hamster", this.extension, 1, "left"); - - } else { - Main.panel.addToStatusArea("hamster", this.extension, 0, "right"); - } - - Main.panel.menuManager.addMenu(this.extension.menu); - - - Main.wm.addKeybinding("show-hamster-dropdown", - this.extension._settings, - Meta.KeyBindingFlags.NONE, - //since Gnome 3.16, Shell.KeyBindingMode is replaced by Shell.ActionMode - Shell.KeyBindingMode ? Shell.KeyBindingMode.ALL : Shell.ActionMode.ALL, - Lang.bind(this.extension, this.extension.toggle) - ); - }, - - disable: function() { - Main.wm.removeKeybinding("show-hamster-dropdown"); - - if (this.placement == 1) { - Main.panel._rightBox.remove_actor(dateMenu.container); - Main.panel._addToPanelBox('dateMenu', dateMenu, Main.sessionMode.panel.center.indexOf('dateMenu'), Main.panel._centerBox); - - } else if (this.placement == 2) { - Main.panel._leftBox.get_children()[0].get_children()[0].get_children()[0].get_children()[0].set_text(this._activitiesText); - } - - Main.panel.menuManager.removeMenu(this.extension.menu); - - GLib.source_remove(this.extension.timeout); - this.extension.actor.destroy(); - this.extension.destroy(); - this.extension = null; - } - }; -} - - -function init(extensionMeta) { - Convenience.initTranslations("hamster-shell-extension"); - return new ExtensionController(extensionMeta); -} diff --git a/extension/extension.js b/extension/extension.js new file mode 100644 index 00000000..cc3dfc6b --- /dev/null +++ b/extension/extension.js @@ -0,0 +1,285 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const GLib = imports.gi.GLib; +const Lang = imports.lang; +const Shell = imports.gi.Shell; +const Meta = imports.gi.Meta; +const Main = imports.ui.main; +const Gio = imports.gi.Gio; + +const Gettext = imports.gettext.domain('hamster-shell-extension'); +const _ = Gettext.gettext; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); +const Convenience = Me.imports.convenience; +const PanelWidget = Me.imports.widgets.panelWidget.PanelWidget; + +// dbus-send --session --type=method_call --print-reply --dest=org.gnome.Hamster /org/gnome/Hamster org.freedesktop.DBus.Introspectable.Introspect +const ApiProxyIface = ['', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '', +].join(''); + +let ApiProxy = Gio.DBusProxy.makeProxyWrapper(ApiProxyIface); + +// dbus-send --session --type=method_call --print-reply --dest=org.gnome.Hamster.WindowServer /org/gnome/Hamster/WindowServer org.freedesktop.DBus.Introspectable.Introspect +const WindowsProxyIface = ['', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '', +].join(''); + + +let WindowsProxy = Gio.DBusProxy.makeProxyWrapper(WindowsProxyIface); + + + + +/** + * Create the controller instance that handles extension context. + * + * This class does not actually handle any widgets/representation itself. It is + * instead in charge of setting up the general infrastructure and to make sure + * that the extension cleans up after itself if it gets deactivated. + * + * @class + */ +function Controller(extensionMeta) { + let dateMenu = Main.panel.statusArea.dateMenu; + + return { + extensionMeta: extensionMeta, + panelWidget: null, + settings: null, + placement: 0, + apiProxy: null, + windowsProxy: null, + // ``shouldEnable`` indicates if the 'magic' enable function has been called or not. + // for details please see: https://github.com/projecthamster/hamster-shell-extension/pull/239 + shouldEnable: false, + + /** + * 'Magic' method, called upon extension launch. + * + * The gnome-shell-extension API grantees that there is always a ``disable`` call in + * between to ``enable`` calls. + * + * Note: + * We only set up our dbus proxies here. In order to be able to do so asynchronously all + * the actual startup code is refered to ``deferred_enable``. + */ + enable: function() { + this.shouldEnable = true; + new ApiProxy(Gio.DBus.session, 'org.gnome.Hamster', '/org/gnome/Hamster', + Lang.bind(this, function(proxy) { + this.apiProxy = proxy; + this.deferred_enable(); + })); + new WindowsProxy(Gio.DBus.session, "org.gnome.Hamster.WindowServer", + "/org/gnome/Hamster/WindowServer", + Lang.bind(this, function(proxy) { + this.windowsProxy = proxy; + this.deferred_enable(); + })); + }, + + deferred_enable: function() { + // Make sure ``enable`` is 'finished' and ``disable`` has not been + // called in between. + if (!this.shouldEnable || !this.apiProxy || !this.windowsProxy) + return; + + this.settings = Convenience.getSettings(); + this.panelWidget = new PanelWidget(this); + this.placement = this.settings.get_int("panel-placement"); + + this._placeWidget(this.placement, this.panelWidget); + + // Callbacks that handle appearing/vanishing dbus services. + function apiProxy_appeared_callback() { + } + + function apiProxy_vanished_callback() { + global.log(_("hamster-shell-extension: 'hamster-service' not running. Shutting down.")); + Main.notify(_("hamster-shell-extension: 'hamster-service' not running. Shutting down.")); + this.disable(); + } + + function windowsProxy_appeared_callback() { + } + + function windowsProxy_vanished_callback() { + global.log(_("hamster-shell-extension: 'hamster-windows-service' not running. Shutting down.")); + Main.notify(_("hamster-shell-extension: 'hamster-windows-service' not running. Shutting down.")); + this.disable(); + } + + // Set-up watchers that watch for required dbus services. + let dbus_watcher = Gio.bus_watch_name(Gio.BusType.SESSION, 'org.gnome.Hamster', + Gio.BusNameWatcherFlags.NONE, apiProxy_appeared_callback.bind(this), + apiProxy_vanished_callback.bind(this)); + + let dbus_watcher_window = Gio.bus_watch_name(Gio.BusType.SESSION, 'org.gnome.Hamster.WindowServer', + Gio.BusNameWatcherFlags.NONE, windowsProxy_appeared_callback.bind(this), + windowsProxy_vanished_callback.bind(this)); + + this.apiProxy.connectSignal('ActivitiesChanged', Lang.bind(this, this.refreshActivities)); + this.activities = this.refreshActivities(); + + Main.panel.menuManager.addMenu(this.panelWidget.menu); + Main.wm.addKeybinding("show-hamster-dropdown", + this.panelWidget._settings, + Meta.KeyBindingFlags.NONE, + // Since Gnome 3.16, Shell.KeyBindingMode is replaced by Shell.ActionMode + Shell.KeyBindingMode ? Shell.KeyBindingMode.ALL : Shell.ActionMode.ALL, + Lang.bind(this.panelWidget, this.panelWidget.toggle) + ); + }, + + disable: function() { + this.shouldEnable = false; + Main.wm.removeKeybinding("show-hamster-dropdown"); + + global.log('Shutting down hamster-shell-extension.'); + this._removeWidget(this.placement); + Main.panel.menuManager.removeMenu(this.panelWidget.menu); + GLib.source_remove(this.panelWidget.timeout); + this.panelWidget.actor.destroy(); + this.panelWidget.destroy(); + this.panelWidget = null; + this.apiProxy = null; + this.windowsProxy = null; + }, + + /** + * Build a new cache of all activities present in the backend. + */ + refreshActivities: function() { + /** + * Return an Array of [Activity.name, Activity.category.name] Arrays. + * + */ + function getActivities(controller) { + if (controller.runningActivitiesQuery) { + return(controller.activities); + } + + this.runningActivitiesQuery = true; + controller.apiProxy.GetActivitiesRemote("", Lang.bind(this, function([response], err) { + controller.runningActivitiesQuery = false; + controller.activities = response; + })); + + global.log('ACTIVITIES HAMSTER: ', controller.activities); + return controller.activities; + } + + let result = getActivities(this); + this.activities = result; + return result; + }, + + /** + * Place the actual extension wi + * get in the right place according to settings. + */ + _placeWidget: function(placement, panelWidget) { + if (placement == 1) { + // 'Replace calendar' + Main.panel.addToStatusArea("hamster", this.panelWidget, 0, "center"); + + Main.panel._centerBox.remove_actor(dateMenu.container); + Main.panel._addToPanelBox('dateMenu', dateMenu, -1, Main.panel._rightBox); + } else if (placement == 2) { + // 'Replace activities' + let activitiesMenu = Main.panel._leftBox.get_children()[0].get_children()[0].get_children()[0].get_children()[0]; + // If our widget replaces the 'Activities' menu in the panel, + // this property stores the original text so we can restore it + // on ``this.disable``. + this._activitiesText = activitiesMenu.get_text(); + activitiesMenu.set_text(''); + Main.panel.addToStatusArea("hamster", this.panelWidget, 1, "left"); + } else { + // 'Default' + Main.panel.addToStatusArea("hamster", this.panelWidget, 0, "right"); + } + }, + + _removeWidget: function(placement) { + if (placement == 1) { + // We replaced the calendar + Main.panel._rightBox.remove_actor(dateMenu.container); + Main.panel._addToPanelBox( + 'dateMenu', + dateMenu, + Main.sessionMode.panel.center.indexOf('dateMenu'), + Main.panel._centerBox + ); + Main.panel._centerBox.remove_actor(this.panelWidget.container); + } else if (placement == 2) { + // We replaced the 'Activities' menu + let activitiesMenu = Main.panel._leftBox.get_children()[0].get_children()[0].get_children()[0].get_children()[0]; + activitiesMenu.set_text(this._activitiesText); + Main.panel._leftBox.remove_actor(this.panelWidget.container); + } else { + Main.panel._rightBox.remove_actor(this.panelWidget.container); + } + }, + }; +} + + +function init(extensionMeta) { + Convenience.initTranslations(); + return new Controller(extensionMeta); +} diff --git a/prefs.js b/extension/prefs.js similarity index 72% rename from prefs.js rename to extension/prefs.js index 6dfbf580..dc4bc23b 100644 --- a/prefs.js +++ b/extension/prefs.js @@ -1,9 +1,33 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const GObject = imports.gi.GObject; const Lang = imports.lang; + const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const Convenience = Me.imports.convenience; @@ -21,7 +45,6 @@ const HamsterSettingsWidget = new GObject.Class({ let vbox, label; - label = new Gtk.Label(); label.set_markup("Positioning"); label.set_alignment(0, 0.5); @@ -39,15 +62,14 @@ const HamsterSettingsWidget = new GObject.Class({ let placementCombo = new Gtk.ComboBox({model: placementOptions}); - let renderer = new Gtk.CellRendererText(); - placementCombo.pack_start(renderer, true); - placementCombo.add_attribute(renderer, 'text', 0); + let placementComboRenderer = new Gtk.CellRendererText(); + placementCombo.pack_start(placementComboRenderer, true); + placementCombo.add_attribute(placementComboRenderer, 'text', 0); placementCombo.connect('changed', Lang.bind(this, this._onPlacementChange)); placementCombo.set_active(this._settings.get_int("panel-placement")); vbox.add(placementCombo); - label = new Gtk.Label({margin_top: 20}); label.set_markup("Appearance in panel"); label.set_alignment(0, 0.5); @@ -65,9 +87,9 @@ const HamsterSettingsWidget = new GObject.Class({ let appearanceCombo = new Gtk.ComboBox({model: appearanceOptions}); - let renderer = new Gtk.CellRendererText(); - appearanceCombo.pack_start(renderer, true); - appearanceCombo.add_attribute(renderer, 'text', 0); + let appearanceComboRenderer = new Gtk.CellRendererText(); + appearanceCombo.pack_start(appearanceComboRenderer, true); + appearanceCombo.add_attribute(appearanceComboRenderer, 'text', 0); appearanceCombo.connect('changed', Lang.bind(this, this._onAppearanceChange)); appearanceCombo.set_active(this._settings.get_int("panel-appearance")); @@ -89,6 +111,10 @@ const HamsterSettingsWidget = new GObject.Class({ vbox.add(new Gtk.Label({label: "Reload gnome shell after updating prefs (alt+f2 > r)", margin_top: 70})); + + let version_text = ExtensionUtils.getCurrentExtension().metadata.version; + let version_label_text = "You are running hamster-shell-extension version " + version_text; + vbox.add(new Gtk.Label({label: version_label_text, margin_top: 10})); }, _onPlacementChange: function(widget) { @@ -131,7 +157,6 @@ const HamsterSettingsWidget = new GObject.Class({ }); function init() { - } function buildPrefsWidget() { diff --git a/extension/stuff.js b/extension/stuff.js new file mode 100644 index 00000000..56c70545 --- /dev/null +++ b/extension/stuff.js @@ -0,0 +1,118 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +// Time formatting helpers + +/* @function formatDuration + * + * Return time-information formatted as '%HH:%MM' + * + * @param {int} - Total amount of seconds to represent. + */ +function formatDuration(total_seconds) { + let hours = total_seconds / 3600; + let remaining_seconds = total_seconds % 3600; + // We only care for "full minutes". + let minutes = remaining_seconds / 60; + + // This string formatting is not part of JS canon but provided by the shell environment. + return "%02d:%02d".format(hours, minutes); +} + +/* @function formatDurationHuman + * + * Return time-information as '%HHh %SSmin' or '%SSmin' (if hours=0). + * + * @param {int} - Total amount of seconds to represent. + */ +function formatDurationHuman(total_seconds) { + let hours = total_seconds / 3600; + let remaining_seconds = total_seconds % 3600; + // We only care for "full minutes". + let minutes = remaining_seconds / 60; + + let result = ''; + + if (hours > 0 || minutes > 0) { + if (hours > 0) { + result += "%dh ".format(hours); + } + + if (minutes > 0) { + result += "%dmin".format(minutes); + } + } else { + result = "Just started"; + } + + return result; +} + +/* @function formatDurationHours + * + * Return time-information as decimal (with one decimal place) amount of hours. + * Example: 'X.Yh' + * + * @param {int} - Total amount of seconds to represent. + */ +function formatDurationHours(seconds) { + // We shift by one decimal place to the left in order to round properly. + let hours = Math.round((seconds/3600)*10); + // Shift right after rounding. + hours = hours / 10; + return '%.1fh'.format(hours); +} + +// Other helper functions + +function fromDbusFact(fact) { + // converts a fact coming from dbus into a usable object + function UTCToLocal(timestamp) { + // TODO - is this really the way?! + let res = new Date(timestamp); + return new Date(res.setUTCMinutes(res.getUTCMinutes() + res.getTimezoneOffset())); + } + + let result = { + name: fact[4], + startTime: UTCToLocal(fact[1]*1000), + endTime: fact[2] != 0 ? UTCToLocal(fact[2]*1000) : null, + description: fact[3], + activityId: fact[5], + category: fact[6], + tags: fact[7], + date: UTCToLocal(fact[8] * 1000), + delta: fact[9], + id: fact[0] + }; + return result; +} + +function fromDbusFacts(facts) { + let res = []; + for (var fact of facts) { + res.push(fromDbusFact(fact)); + } + + return res; +} diff --git a/extension/stylesheet.css b/extension/stylesheet.css new file mode 100644 index 00000000..3208b048 --- /dev/null +++ b/extension/stylesheet.css @@ -0,0 +1,69 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +.hamster-box { + spacing: 15px; + min-width: 25em; +} + +.hamster-box-label { + padding-top: 1em; + text-align: center; +} + +.hamster-activities { + border: 1px solid #333; +} + +.hamster-scrollbox { + max-height: 200px; + overflow-x: none; + overflow-y: auto; +} + +.cell-label { + padding: 3px; + padding-right: 15px; + color: #aaa; +} + +.summary-label { + color: #aaa; + text-align: right; +} + +.cell-button { + padding: 3px 5px; +} + +.clickable:hover { + background-color: #4c4c4c; +} + +.clickable:hover .cell-label { + color: #fff; +} + +.panel-box { + spacing: 5px; +} diff --git a/extension/widgets/categoryTotalsWidget.js b/extension/widgets/categoryTotalsWidget.js new file mode 100644 index 00000000..683fe920 --- /dev/null +++ b/extension/widgets/categoryTotalsWidget.js @@ -0,0 +1,71 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const Lang = imports.lang; +const St = imports.gi.St; +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Stuff = Me.imports.stuff; + + +/** + * Custom Label widget that displays category totals. + */ +var CategoryTotalsWidget = new Lang.Class({ + Name: 'CategoryTotals', + Extends: St.Label, + + _init: function() { + this.parent({style_class: 'summary-label'}); + + }, + + /** + * Recompute values and replace old string with new one based on passed facts. + */ + refresh: function(facts) { + /** + * Construct a string representing category totals. + */ + function getString(facts) { + let byCategory = {}; + let categories = []; + for (let fact of facts) { + byCategory[fact.category] = (byCategory[fact.category] || 0) + fact.delta; + if (categories.indexOf(fact.category) == -1) + categories.push(fact.category); + } + + let string = ""; + for (let category of categories) { + string += category + ": " + Stuff.formatDurationHours(byCategory[category]) + ", "; + } + // strip trailing comma + return string.slice(0, string.length - 2); + } + + this.set_text(getString(facts)); + }, +}); diff --git a/extension/widgets/factsBox.js b/extension/widgets/factsBox.js new file mode 100644 index 00000000..42857f5a --- /dev/null +++ b/extension/widgets/factsBox.js @@ -0,0 +1,110 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const Lang = imports.lang; +const St = imports.gi.St; +const PopupMenu = imports.ui.popupMenu; +const Clutter = imports.gi.Clutter; +const Mainloop = imports.mainloop; +const GLib = imports.gi.GLib; + +const Gettext = imports.gettext.domain('hamster-shell-extension'); +const _ = Gettext.gettext; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Stuff = Me.imports.stuff; +const OngoingFactEntry = Me.imports.widgets.ongoingFactEntry.OngoingFactEntry; +const CategoryTotalsWidget = Me.imports.widgets.categoryTotalsWidget.CategoryTotalsWidget; +const TodaysFactsWidget = Me.imports.widgets.todaysFactsWidget.TodaysFactsWidget; + + +/** + * Create the widget that ``PanelWidget`` will use to dispay the *raw fact entry* as + * well as todays facts. + * @class + */ +var FactsBox = new Lang.Class({ + Name: 'FactsBox', + Extends: PopupMenu.PopupBaseMenuItem, + _init: function(controller, panelWidget) { + this.parent({reactive: false}); + + this._controller = controller; + + // Setup main layout box + let main_box = new St.BoxLayout({style_class: 'hamster-box'}); + main_box.set_vertical(true); + this.actor.add_child(main_box); + + // Setup *ongoing fact* label and widget + let _ongoingFactLabel = new St.Label({style_class: 'hamster-box-label'}); + _ongoingFactLabel.set_text(_("What are you doing?")); + main_box.add(_ongoingFactLabel); + + this.ongoingFactEntry = new OngoingFactEntry(this._controller); + //this.ongoingFactEntry.clutter_text.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent)); + main_box.add(this.ongoingFactEntry); + + let fact_list_label = new St.Label({style_class: 'hamster-box-label'}); + fact_list_label.set_text(_("Today's activities")); + main_box.add(fact_list_label); + + // Scrollbox that will house the list of todays facts + // Since ``St.Table`` does not implement St.Scrollable, we create a + // container object that does. + this.todaysFactsWidget = new TodaysFactsWidget(this._controller, panelWidget); + this._scrollAdjustment = this.todaysFactsWidget.vscroll.adjustment; + main_box.add(this.todaysFactsWidget); + + // Setup category summery + this.summaryLabel = new CategoryTotalsWidget(); + main_box.add(this.summaryLabel); + }, + + // [FIXME] + // The best solution would be to listen for a 'FactsChanged' Signal that carries the new + // facts as payload and just refresh with this. But for now we stick with this + // simpler version. + refresh: function(facts, ongoingFact) { + this.todaysFactsWidget.refresh(facts, ongoingFact); + this.summaryLabel.refresh(facts); + + }, + + /** + * Focus the fact entry and make sure todaysFactsWidget are scrolled to the bottom. + */ + focus: function() { + Mainloop.timeout_add(20, Lang.bind(this, function() { + this._scrollAdjustment.value = this._scrollAdjustment.upper; + global.stage.set_key_focus(this.ongoingFactEntry); + })); + }, + + /** + * Remove any existing focus. + */ + unfocus: function() { + global.stage.set_key_focus(null); + }, +}); diff --git a/extension/widgets/ongoingFactEntry.js b/extension/widgets/ongoingFactEntry.js new file mode 100644 index 00000000..a46948d0 --- /dev/null +++ b/extension/widgets/ongoingFactEntry.js @@ -0,0 +1,189 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const Lang = imports.lang; +const St = imports.gi.St; +const Clutter = imports.gi.Clutter; + +const Gettext = imports.gettext.domain('hamster-shell-extension'); +const _ = Gettext.gettext; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); + + +/** + * Custom Entry widget that allows entering a *raw fact* string for a new ongoing fact. + * + * Besides general layout this widget is also in charge of providing autocomplete functionality. + * @class + * + * + */ +var OngoingFactEntry = new Lang.Class({ + Name: 'OngoingFactEntry', + Extends: St.Entry, + + _init: function(controller) { + this.parent({ + name: 'searchEntry', + can_focus: true, + track_hover: true, + hint_text: _("Enter activity..."), + style_class: "search-entry" + }); + + this._controller = controller; + this._prevText = ''; + // Seems to be populate by GetActivities. + this._autocompleteActivities = []; + this._runningActivitiesQuery = null; + this.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); + this.clutter_text.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent)); + }, + + /** + * Callback for when ``ongoingFactEntry`` gets activated. + * + * Passes the current widget text as 'raw fact' to the dbus interface in order + * to create a new 'ongoing fact'. + * Also resets the text to an empty string afterwards. + * + * @callback FactsBox~_onEntryActivated + */ + _onEntryActivated: function() { + let text = this.get_text(); + this._controller.apiProxy.AddFactRemote(text, 0, 0, false, Lang.bind(this, function(response, error) { + // not interested in the new id - this shuts up the warning + })); + this.set_text(''); + }, + + /** + * Callback triggered after key release. + * + * This is where autocompletion happens. + * + * @callback FactsBox~_onKeyReleaseEvent + */ + _onKeyReleaseEvent: function(textItem, evt) { + /** + * Check if the passed key is on our list of keys to be ignored. + */ + function checkIfIgnoredKey(key) { + let ignoreKeys = [Clutter.BackSpace, Clutter.Delete, Clutter.Escape]; + // Looks like there is realy no ``Array.includes()`` available as + // of now. + let result = ignoreKeys.indexOf(key); + if (result == -1) { + result = false; + } else { + result = true; + } + return result; + } + + let symbol = evt.get_key_symbol(); + // [FIXME] + // To limit the scope of the PR we did not refactor this bit too much. + // We should however take another look at the Instance attributes and + // see what is really needed and if their naming is apropriate. + let text = this.get_text().toLowerCase(); + let starttime = ""; + let activitytext = text; + + // [FIXME] + // Should be a separate function. + // + // Don't include leading times in the activity autocomplete + // [FIXME] Even if the parsing is not flawed (which it most likly is, + // the variable names are certainly off. + // [FIXME] + // If we allow the whole host of raw fact parsing we need to extend our + // regex. + let match = []; + if ((match = text.match(/^\d\d:\d\d /)) || + (match = text.match(/^-\d+ /))) { + starttime = text.substring(0, match[0].length); + activitytext = text.substring(match[0].length); + } + + // [FIXME] + // Should be a separate local function. + // + // If nothing has changed or we still have selection then that means + // that special keys are at play and we don't attempt to autocomplete + if (activitytext == "" || + this._prevText == text || + this.clutter_text.get_selection()) { + return; + } + this._prevText = text; + + if (checkIfIgnoredKey(symbol)) { return; } + + // [FIXME] + // Investigate if we can move this into a dedicated 'autocomplete' function. + // + // Autocomplete + // If a key release has been detected: itterate over all activities fetched from the backend + // (via GetActivities aka __get_activities. This is a list of + // (activity.name, activivty.category.name) tuples. + // for each item: If it has a category, construct new composite string 'name@category', + // else just use 'name'. + // + // For each activity we now do the following: + // - Check if the current iteration item string has the current + // entry text as a substring starting from 0 (remember that the iteration item may be + // 'activity@category'. + // + // - If so: We got a hit (if there are multiple, only the first item that triggers the match + // will be considered. Further filtering happens due to continued typing, there is no selection + // or visual feedback that there was more than one hit. + // - Now we build a new 'result string' like this 'starttime iterationitem'. starttime is + // an empty string at the beginning. + // - Set the iteration item as new entry text + // - Select the bit after actually typed text to the end of iteration item. This should + // result in removal of the "unmatched bits" once user continues typing + // - Store a normalized (lower) version of the iteration item (and now new text) in a local + // tmo variable. This is used to check against the entry text after new button releases + // to see if things have changed. + // + // If not, do nothing. + for (let activity of this._controller.activities) { + let name = activity[0]; + let category = activity[1]; + let activityString = name; + if (category.length > 0) { + activityString = name + "@" + category; + } + // We got a hit. + if (activityString.toLowerCase().substring(0, activitytext.length) == activitytext) { + let completion = starttime + activityString; + this.set_text(completion); + this.get_clutter_text().set_selection(text.length, completion.length); + + this._prevText = completion.toLowerCase(); + } + } + }, +}); diff --git a/extension/widgets/panelWidget.js b/extension/widgets/panelWidget.js new file mode 100644 index 00000000..d43833d9 --- /dev/null +++ b/extension/widgets/panelWidget.js @@ -0,0 +1,326 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const Lang = imports.lang; +const Gio = imports.gi.Gio; +const Clutter = imports.gi.Clutter; +const PanelMenu = imports.ui.panelMenu; +const St = imports.gi.St; +const PopupMenu = imports.ui.popupMenu; +const GLib = imports.gi.GLib; + +const Gettext = imports.gettext.domain('hamster-shell-extension'); +const _ = Gettext.gettext; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const FactsBox = Me.imports.widgets.factsBox.FactsBox; +const Stuff = Me.imports.stuff; + +/** + * Class that defines the actual extension widget to be shown in the panel. + * + * ------------------------------------------------------------- + * | Gnome top menu bar | PanelWidget / PanelWidget.panelLabel| + * ------------------------------------------------------------- + * |PanelWidget.menu | + * | | + * |PanelWidget.factsBox | + * | | + * |PanelWidget.overviewMenuItem | + * |PanelWidget.stopTrackingMenuItem | + * |PanelWidget.addNewFactMenuItem | + * |PanelWidget.SettingMenuItem | + * | | + * --------------------------------------- + * + * @class + */ +var PanelWidget = new Lang.Class({ + Name: 'PanelWidget', + Extends: PanelMenu.Button, + + _init: function(controller) { + // [FIXME] + // What is the parameter? + this.parent(0.0); + + this._controller = controller; + // [FIXME] + // Still needed? + this._extensionMeta = controller.extensionMeta; + this._settings = controller.settings; + this._windowsProxy = controller.windowsProxy; + + controller.apiProxy.connectSignal('FactsChanged', Lang.bind(this, this.refresh)); + controller.apiProxy.connectSignal('TagsChanged', Lang.bind(this, this.refresh)); + + + // Setup the main layout container for the part of the extension + // visible in the panel. + let panelContainer = new St.BoxLayout({style_class: "panel-box"}); + this.actor.add_actor(panelContainer); + this.actor.add_style_class_name('panel-status-button'); + + this.panelLabel = new St.Label({ + text: _("Loading..."), + y_align: Clutter.ActorAlign.CENTER + }); + + // If we want to switch icons, we actually keep the same instance and + // just swap the actual image. + // [FIXME] + // Is there no path manipulation lib? + this._trackingIcon = Gio.icon_new_for_string(this._extensionMeta.path + "/images/hamster-tracking-symbolic.svg"); + this._idleIcon = Gio.icon_new_for_string(this._extensionMeta.path + "/images/hamster-idle-symbolic.svg"); + this.icon = new St.Icon({gicon: this._trackingIcon, + icon_size: 16, + style_class: "panel-icon"}); + + panelContainer.add(this.icon); + panelContainer.add(this.panelLabel); + + this.factsBox = new FactsBox(controller, this); + this.menu.addMenuItem(this.factsBox); + + // overview + let overviewMenuItem = new PopupMenu.PopupMenuItem(_("Show Overview")); + overviewMenuItem.connect('activate', Lang.bind(this, this._onOpenOverview)); + this.menu.addMenuItem(overviewMenuItem); + + // [FIXME] + // This should only be shown if we have an 'ongoing fact'. + // stop tracking + let stopTrackinMenuItem = new PopupMenu.PopupMenuItem(_("Stop Tracking")); + stopTrackinMenuItem.connect('activate', Lang.bind(this, this._onStopTracking)); + this.menu.addMenuItem(stopTrackinMenuItem); + + // add new task + let addNewFactMenuItem = new PopupMenu.PopupMenuItem(_("Add Earlier Activity")); + addNewFactMenuItem.connect('activate', Lang.bind(this, this._onOpenAddFact)); + this.menu.addMenuItem(addNewFactMenuItem); + + // settings + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + let SettingMenuItem = new PopupMenu.PopupMenuItem(_("Tracking Settings")); + SettingMenuItem.connect('activate', Lang.bind(this, this._onOpenSettings)); + this.menu.addMenuItem(SettingMenuItem); + + // focus menu upon display + this.menu.connect('open-state-changed', Lang.bind(this, + function(menu, open) { + if (open) { + this.factsBox.focus(); + } else { + this.factsBox.unfocus(); + } + } + )); + + // refresh the widget every 60 secs + this.timeout = GLib.timeout_add_seconds(0, 60, Lang.bind(this, this.refresh)); + this.connect('destroy', Lang.bind(this, this._disableRefreshTimer)); + this.refresh(); + }, + + /** + * This is our main 'update/refresh' method. + * Whenever we suspect that things have changed (via dbus signals) + * this should be triggered. + * Upon such triggering, this method should call relevant sub-widget's + * 'refresh' method. + * As such it should do as little work as possible and rather gather all + * required facts etc and pass them to the relevant sub-widget's + * refresh methods. + */ + refresh: function() { + /** + * We need to wrap our actual refresh code in this callback for now as + * I am having major difficulties using a syncronous dbus method call to + * fetch the array of 'todaysFacts'. + */ + function _refresh([response], err) { + /** + * Extract *ongoing fact* from our list of facts. Due to how *legacy + * hamster* works this is the first element in the array returned my + * ``getTodayFacts``. + * + * Returns ``null`` if there is no *ongoing fact*. + */ + function getOngoingFact(facts) { + let result = null; + if (facts.length) { + let lastFact = facts[facts.length - 1]; + if (!lastFact.endTime) { result = lastFact; } + } + return result; + } + + let facts = []; + + // [FIXME] + // This seems a rather naive way to handle potential errors. + if (err) { + log(err); + } else if (response.length > 0) { + facts = Stuff.fromDbusFacts(response); + } + + let ongoingFact = getOngoingFact(facts); + + this.updatePanelDisplay(ongoingFact); + this.factsBox.refresh(facts, ongoingFact); + } + + // [FIXME] + // This should really be a synchronous call fetching the facts. + // Once this is done, the actual code from the callback should follow + // here. + this._controller.apiProxy.GetTodaysFactsRemote(Lang.bind(this, _refresh)); + return GLib.SOURCE_CONTINUE; + }, + + /** + * Open 'popup menu' containing the bulk of the extension widgets. + */ + show: function() { + this.menu.open(); + }, + + /** + * Close/Open the 'popup menu' depending on previous state. + */ + toggle: function() { + this.menu.toggle(); + }, + + + /** + * Update the rendering of the PanelWidget in the panel itself. + * + * Depending on the 'display mode' set in the extensions settings this has + * slightly different consequences. + */ + updatePanelDisplay: function(fact) { + /** + * Return a text string representing the passed fact suitable for the panelLabel. + * + * @param fact The fact to be represented. Be advised. If there is no + * *ongoing fact* this will be ``null``! + */ + function getLabelString(fact) { + let result = _("No activity"); + if (fact) { + result = "%s %s".format(fact.name, Stuff.formatDuration(fact.delta)); + } + return result; + } + /** + * Returns the appropriate icon image depending on ``fact``. + */ + function getIcon(panelWidget) { + let result = panelWidget._idleIcon; + if (fact) { result = panelWidget._trackingIcon; } + return result; + } + + // 0 = show label, 1 = show just icon, 2 = show label and icon + switch (this._settings.get_int("panel-appearance")) { + case 0: + this.panelLabel.set_text(getLabelString(fact)); + this.panelLabel.show(); + this.icon.hide(); + break; + case 1: + this.icon.gicon = getIcon(this); + this.icon.show(); + this.panelLabel.hide(); + break; + case 2: + this.icon.gicon = getIcon(this); + this.icon.show(); + this.panelLabel.set_text(getLabelString(fact)); + this.panelLabel.show(); + break; + } + }, + + /** + * Disable the refresh timer. + * + * @callback panelWidget~_disableRefreshTimer + * + * This method is actually a callback triggered on the destroy + * signal. + */ + _disableRefreshTimer: function() { + GLib.source_remove(this.timeout); + }, + + /** + * Callback to be triggered when an *ongoing fact* is stopped. + * @callback PanelWidget~_onStopTracking + * + * This will get the current time and issue the ``StopTracking`` + * method call to the dbus interface. + */ + _onStopTracking: function() { + let now = new Date(); + let epochSeconds = Date.UTC(now.getFullYear(), + now.getMonth(), + now.getDate(), + now.getHours(), + now.getMinutes(), + now.getSeconds()); + epochSeconds = Math.floor(epochSeconds / 1000); + this._controller.apiProxy.StopTrackingRemote(GLib.Variant.new('i', [epochSeconds])); + }, + + /** + * Callback that triggers opening of the *Overview*-Window. + * + * @callback panelWidget~_onOpenOverview + */ + _onOpenOverview: function() { + this._controller.windowsProxy.overviewSync(); + }, + + /** + * Callback that triggers opening of the *Add Fact*-Window. + * + * @callback panelWidget~_onOpenAddFact + */ + _onOpenAddFact: function() { + this._controller.windowsProxy.editSync(GLib.Variant.new('i', [0])); + }, + + /** + * Callback that triggers opening of the *Add Fact*-Window. + * + * @callback panelWidget~_onOpenSettings + * + * Note: This will open the GUI settings, not the extension settings! + */ + _onOpenSettings: function() { + this._controller.windowsProxy.preferencesSync(); + }, +}); diff --git a/extension/widgets/todaysFactsWidget.js b/extension/widgets/todaysFactsWidget.js new file mode 100644 index 00000000..1ec2de34 --- /dev/null +++ b/extension/widgets/todaysFactsWidget.js @@ -0,0 +1,193 @@ +/* +This file is part of 'hamster-shell-extension'. + +'hamster-shell-extension' is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +'hamster-shell-extension' is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with 'hamster-shell-extension'. If not, see . + +Copyright (c) 2011 Jerome Oufella +Copyright (c) 2011-2012 Toms Baugis +Icons Artwork Copyright (c) 2012 Reda Lazri +Copyright (c) 2016 - 2018 Eric Goller / projecthamster +*/ + + +const Lang = imports.lang; +const St = imports.gi.St; +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Stuff = Me.imports.stuff; + + +/** + * A widget that lists all facts for *today*. + */ +var TodaysFactsWidget = new Lang.Class({ + Name: 'TodaysFactsWidget', + Extends: St.ScrollView, + + _init: function(controller, panelWidget) { + this.parent({style_class: 'hamster-scrollbox'}); + this._controller = controller; + this._panelWidget = panelWidget; + + + this.factsBox = new St.BoxLayout({}); + this.factsBox.set_vertical(true); + this.facts_widget = new St.Widget({ + style_class: 'hamster-activities', + layout_manager: new Clutter.TableLayout(), + reactive: true + }); + this.factsBox.add(this.facts_widget); + this.add_actor(this.factsBox); + + }, + + /** + * Populate the widget with rows representing the passed facts. + */ + populateFactsWidget: function(facts, ongoingFact) { + + /** + * Construct an individual row within the widget - representing a single fact. + */ + function constructRow(fact, ongoingFact, controller, menu) { + /** + * Check if two facts have the same activity. + */ + function checkSameActivity(fact, otherFact) { + // Check if two facts have the same activity. + let result = true; + if (!otherFact || + otherFact.name != fact.name || + otherFact.category != fact.category || + // [FIXME] + // This is wrong, isn't it? Tags 'belong' to facts, not + // activities! We keep it for now and need to address this + // as a single issue. + otherFact.tags.join(",") != fact.tags.join(",")) { + result = false; + } + return result; + } + + /** + * Callback for the 'openEditDialog'-Button. + * + * Opens the edit dialog and closes the extension 'drop down + * bubble' menu. + * + * @callback TodaysFactsWidget~openEditDialog + */ + function onOpenEditDialog(button, event) { + controller.windowsProxy.editSync(GLib.Variant.new('i', [fact.id])); + menu.close(); + } + + /** + * Callback for the 'onContinue'-Button. + * + * Start a new ongoing fact with this facts activity and tags. + * Closes the menu. + * + * @callback TodaysFactsWidget~onContinueButton + */ + function onContinueButton(button, event) { + // [FIXME] + // This probably should be a "serialize" method of the fact object. + let fact = button.fact; + let factStr = fact.name + "@" + fact.category + ", " + (fact.description); + if (fact.tags.length) { + factStr += " #" + fact.tags.join(", #"); + } + + controller.apiProxy.AddFactRemote(factStr, 0, 0, false, Lang.bind(this, function(response, err) { + // not interested in the new id - this shuts up the warning + })); + menu.close(); + } + + // Construct the ``Label`` rendering the start- and endtime information. + let timeLabel = new St.Label({style_class: 'cell-label'}); + let timeString; + let start_string = "%02d:%02d - ".format(fact.startTime.getHours(), fact.startTime.getMinutes()); + if (fact.endTime) { + let end_string = "%02d:%02d".format(fact.endTime.getHours(), fact.endTime.getMinutes()); + timeString = start_string + end_string; + } else { + timeString = start_string; + } + timeLabel.set_text(timeString); + + // Construct the ``Label``rendering the remaining fact info. + let factLabel = new St.Label({style_class: 'cell-label'}); + // [FIXME] + // This needs to be cleaner! + factLabel.set_text(fact.name + (0 < fact.tags.length ? (" #" + fact.tags.join(", #")) : "")); + + // Construct the ``Label rendering the facts duration. + let deltaLabel = new St.Label({style_class: 'cell-label'}); + deltaLabel.set_text(Stuff.formatDurationHuman(fact.delta)); + + // Construct a button that triggers 'edit' dialog. + let editIcon = new St.Icon({icon_name: "document-open-symbolic", icon_size: 16}); + let editButton = new St.Button({style_class: 'clickable cell-button'}); + editButton.set_child(editIcon); + // [FIXME] + // Wouldn't it be cleaner to pass the fact as data payload to the callback binding? + editButton.connect('clicked', Lang.bind(this, onOpenEditDialog)); + + // Construct a 'start previous fact's activity as new' button. + // This is only done if the *ongoing fact* activity is actually + // different from the one we currently process. + let continueButton = null; + if (!checkSameActivity(fact, ongoingFact)) { + + let continueIcon = new St.Icon({icon_name: "media-playback-start-symbolic", icon_size: 16}); + continueButton = new St.Button({style_class: 'clickable cell-button'}); + continueButton.set_child(continueIcon); + continueButton.fact = fact; + continueButton.connect('clicked', Lang.bind(this, onContinueButton)); + } + + //The order of the array will be the order in which they will be added to the row. + let result = [timeLabel, factLabel, deltaLabel, editButton]; + + if (continueButton) { + result.push(continueButton); + } + return result; + + } + + let rowCount = 0; + let layout = this.facts_widget.layout_manager; + for (let fact of facts) { + let rowComponents = constructRow(fact, ongoingFact, this._controller, this._panelWidget.menu); + for (let component of rowComponents) { + layout.pack(component, rowComponents.indexOf(component), rowCount); + } + rowCount += 1; + } + }, + + /** + * Clear the widget and populate it anew. + */ + refresh: function(facts, ongoingFact) { + this.facts_widget.remove_all_children(); + this.populateFactsWidget(facts, ongoingFact); + }, +}); diff --git a/locale/cs/LC_MESSAGES/hamster-shell-extension.mo b/locale/cs/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index 9513bfd3..00000000 Binary files a/locale/cs/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/cs/LC_MESSAGES/hamster-shell-extension.po b/locale/cs/LC_MESSAGES/hamster-shell-extension.po deleted file mode 100644 index 9cc7ddfb..00000000 --- a/locale/cs/LC_MESSAGES/hamster-shell-extension.po +++ /dev/null @@ -1,51 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: hamster-shell-extension\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 16:42+0100\n" -"PO-Revision-Date: 2013-01-13 01:18+0100\n" -"Last-Translator: Martin Filip \n" -"Language: cs\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " -"|| n%100>=20) ? 1 : 2);\n" -"Language-Team: \n" -"X-Generator: Poedit 1.5.4\n" - -#: extension.js:85 -msgid "What are you doing?" -msgstr "Co děláš?" - -#: extension.js:91 -msgid "Enter activity..." -msgstr "Zadej aktivitu..." - -#: extension.js:105 -msgid "Today's activities" -msgstr "Dnešní aktivity" - -#: extension.js:215 -msgid "Loading..." -msgstr "Načítám..." - -#: extension.js:238 -msgid "Show Overview" -msgstr "Zobrazit přehled" - -#: extension.js:243 -msgid "Stop Tracking" -msgstr "Zastavit sledování" - -#: extension.js:249 -msgid "Tracking Settings" -msgstr "Nastavení sledování" - -#: extension.js:398 -msgid "No activity" -msgstr "Žádná aktivita" - -#: extension.js:254 -msgid "Add Earlier Activity" -msgstr "Zadat dřívější aktivity" diff --git a/locale/de/LC_MESSAGES/hamster-shell-extension.mo b/locale/de/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index 5bf0b750..00000000 Binary files a/locale/de/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/de/LC_MESSAGES/hamster-shell-extension.po b/locale/de/LC_MESSAGES/hamster-shell-extension.po deleted file mode 100644 index fa4a8e7f..00000000 --- a/locale/de/LC_MESSAGES/hamster-shell-extension.po +++ /dev/null @@ -1,49 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: hamster-shell-extension\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-06-18 13:27+0200\n" -"PO-Revision-Date: 2014-06-18 13:33+0200\n" -"Last-Translator: \n" -"Language-Team: German\n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: extension.js:98 -msgid "What are you doing?" -msgstr "Was machen Sie gerade?" - -#: extension.js:104 -msgid "Enter activity..." -msgstr "Tätigkeit starten..." - -#: extension.js:115 -msgid "Today's activities" -msgstr "Heutige Tätigkeiten" - -#: extension.js:254 -msgid "Loading..." -msgstr "Lade..." - -#: extension.js:278 -msgid "Show Overview" -msgstr "Übersicht anzeigen" - -#: extension.js:283 -msgid "Stop Tracking" -msgstr "Erfassung anhalten" - -#: extension.js:288 -msgid "Add Earlier Activity" -msgstr "Frühere Tätigkeit hinzufügen" - -#: extension.js:294 -msgid "Tracking Settings" -msgstr "Erfassungseinstellungen" - -#: extension.js:456 -msgid "No activity" -msgstr "Keine Tätigkeit" diff --git a/locale/es/LC_MESSAGES/hamster-shell-extension.mo b/locale/es/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index f0ff71bb..00000000 Binary files a/locale/es/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/fr/LC_MESSAGES/hamster-shell-extension.mo b/locale/fr/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index 74b3cb63..00000000 Binary files a/locale/fr/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/nl/LC_MESSAGES/hamster-shell-extension.mo b/locale/nl/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index 73da3b7c..00000000 Binary files a/locale/nl/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/pl/LC_MESSAGES/hamster-shell-extension.mo b/locale/pl/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index b509a857..00000000 Binary files a/locale/pl/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.mo b/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index 4ac19560..00000000 Binary files a/locale/pt-BR/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/locale/ru/LC_MESSAGES/hamster-shell-extension.mo b/locale/ru/LC_MESSAGES/hamster-shell-extension.mo deleted file mode 100644 index b151a138..00000000 Binary files a/locale/ru/LC_MESSAGES/hamster-shell-extension.mo and /dev/null differ diff --git a/requirements.pip b/requirements.pip new file mode 100644 index 00000000..e783f73c --- /dev/null +++ b/requirements.pip @@ -0,0 +1,8 @@ +# This file lists python packages required by our development toolchain. +# These are NOT dependencies for the actuall package but rather tools to +# ease development. + + +Sphinx==1.4.1 +bumpversion==0.5.3 +doc8==0.7.0 diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled deleted file mode 100644 index 412fe3b5..00000000 Binary files a/schemas/gschemas.compiled and /dev/null differ diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..73137abc --- /dev/null +++ b/setup.cfg @@ -0,0 +1,14 @@ +# This file stores additional config option for various +# development tools. + +[bumpversion] +current_version = 0.10.0 +commit = True +tag = False + +[bumpversion:file:docs/conf.py] + +[bumpversion:file:metadata.json] + +[doc8] +ignore-path = docs/_build diff --git a/stuff.js b/stuff.js deleted file mode 100644 index f93da85d..00000000 --- a/stuff.js +++ /dev/null @@ -1,67 +0,0 @@ -function formatDuration(minutes) { - return "%02d:%02d".format((minutes - minutes % 60) / 60, minutes % 60); -} - -function formatDurationHuman(minutes) { - let hours = (minutes - minutes % 60) / 60; - let mins = minutes % 60; - let res = ""; - - if (hours > 0 || mins > 0) { - if (hours > 0) - res += "%dh ".format(hours); - - if (mins > 0) - res += "%dmin".format(mins); - } else { - res = "Just started"; - } - - return res; -} - -function formatDurationHours(minutes) { - return new Number(minutes / 60.0).toFixed(1) + "h"; -} - -function fromDbusFact(fact) { - // converts a fact coming from dbus into a usable object - function UTCToLocal(timestamp) { - // TODO - is this really the way?! - let res = new Date(timestamp); - return new Date(res.setUTCMinutes(res.getUTCMinutes() + res.getTimezoneOffset())); - } - - return { - name: fact[4], - startTime: UTCToLocal(fact[1]*1000), - endTime: fact[2] != 0 ? UTCToLocal(fact[2]*1000) : null, - description: fact[3], - activityId: fact[5], - category: fact[6], - tags: fact[7], - date: UTCToLocal(fact[8] * 1000), - delta: Math.floor(fact[9] / 60), // minutes - id: fact[0] - }; -}; - -function fromDbusFacts(facts) { - let res = []; - for (var fact of facts) { - res.push(fromDbusFact(fact)); - } - - return res; -}; - - -function parseFactString(input) { - let res = { - "time": null, - "activity": input, - "category": null, - "description": null, - "tags": null, - }; -} diff --git a/stylesheet.css b/stylesheet.css deleted file mode 100644 index cb290c72..00000000 --- a/stylesheet.css +++ /dev/null @@ -1,46 +0,0 @@ -.hamster-box { - spacing: 15px; - min-width: 25em; -} - -.hamster-box-label { - padding-top: 1em; - text-align: center; -} - -.hamster-activities { - border: 1px solid #333; -} - -.hamster-scrollbox { - max-height: 200px; - overflow-x: none; - overflow-y: auto; -} - -.cell-label { - padding: 3px; - padding-right: 15px; - color: #aaa; -} - -.summary-label { - color: #aaa; - text-align: right; -} - -.cell-button { - padding: 3px 5px; -} - -.clickable:hover { - background-color: #4c4c4c; -} - -.clickable:hover .cell-label { - color: #fff; -} - -.panel-box { - spacing: 5px; -}