commit 88ae0b60b81325b838aae2c48b790b81f810b903 Author: Aleix Pol Date: Mon Mar 31 03:48:38 2014 +0200 Initial import from the monolithic kde-workspace. This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the techbase wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://community.kde.org/Frameworks/GitOldHistory If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo kde-workspace, frameworks branch, at commit 049113e719dd2fc4446d054fa1a3aada330094f0 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..bf6d52e066 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,109 @@ +project(plasma-desktop) + +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +set(QT_MIN_VERSION "5.2.0") + +find_package(ECM 0.0.11 REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDECompilerSettings) +include(ECMInstallIcons) +include(FeatureSummary) +include(CheckIncludeFiles) + +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS + Quick + DBus + Widgets + X11Extras + Concurrent +) + +find_package(KF5 CONFIG REQUIRED COMPONENTS + Plasma + DocTools + I18n + KCMUtils + NewStuff + KDE4Support + NotifyConfig + Su + Runner +) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + find_package(KDecorations REQUIRED CONFIG) + find_package(LibKWorkspace CONFIG REQUIRED) + find_package(LibTaskManager CONFIG REQUIRED) + find_package(KWinDBusInterface CONFIG REQUIRED) + find_package(ScreenSaverDBusInterface CONFIG REQUIRED) + find_package(KRunnerAppDBusInterface CONFIG REQUIRED) + find_package(KSMServerDBusInterface CONFIG REQUIRED) +endif() + +find_package(KF5Activities ${KF5_VERSION}) +set_package_properties(KF5Activities PROPERTIES DESCRIPTION "management of Plasma activities" + TYPE OPTIONAL + PURPOSE "Needed by activity related plasmoids and the pager." + ) + +find_package(USB) +set(HAVE_LIBUSB ${LIBUSB_FOUND}) +set_package_properties(USB PROPERTIES DESCRIPTION "User level access to USB devices" + URL "http://libusb.sourceforge.net" + TYPE OPTIONAL + PURPOSE "Provides Logitech mouse support in KControl." + ) + +find_package(Fontconfig) +set_package_properties(Fontconfig PROPERTIES DESCRIPTION "Font access configuration library" + URL "http://www.freedesktop.org/wiki/Software/fontconfig" + TYPE OPTIONAL + PURPOSE "Needed to build font configuration and installation tools" + ) + +find_package(X11) +set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" + URL "http://www.x.org" + TYPE OPTIONAL + PURPOSE "Required for building the X11 based workspace" + ) +if(X11_FOUND) + find_package(XCB REQUIRED COMPONENTS XCB XFIXES DAMAGE COMPOSITE SHAPE SYNC RENDER RANDR KEYSYMS IMAGE SHM XTEST) + set_package_properties(XCB PROPERTIES TYPE REQUIRED) + find_package(XCB COMPONENTS ICCCM) + add_feature_info("XCB-ICCCM" XCB_ICCCM_FOUND "Required for building test applications for KWin") +endif() + +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +include(ConfigureChecks.cmake) +configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) +configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) +configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) + +set(PLASMADESKTOP_VERSION_MAJOR 4) +set(PLASMADESKTOP_VERSION_MINOR 95) +set(PLASMADESKTOP_VERSION_PATCH 0) +set(PLASMADESKTOP_VERSION ${PLASMADESKTOP_VERSION_MAJOR}.${PLASMADESKTOP_VERSION_MINOR}.${PLASMADESKTOP_VERSION_PATCH} ) + +set(GENERIC_LIB_SOVERSION ${PLASMADESKTOP_VERSION_MAJOR}) +set(GENERIC_LIB_VERSION "${PLASMADESKTOP_VERSION_MAJOR}.${PLASMADESKTOP_VERSION_MINOR}.${PLASMADESKTOP_VERSION_PATCH}") + +plasma_install_package(desktoppackage org.kde.desktop plasma/shells shell) + +add_subdirectory(doc) +# add_subdirectory(ktouchpadenabler) +# add_subdirectory(solid-actions-kcm) + +# add_subdirectory(runners) +add_subdirectory(containments) +add_subdirectory(toolboxes) +add_subdirectory(applets) +add_subdirectory(kcms) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) +endif() diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..5185fd3f31 --- /dev/null +++ b/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/COPYING.DOC b/COPYING.DOC new file mode 100644 index 0000000000..4a0fe1c8de --- /dev/null +++ b/COPYING.DOC @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake new file mode 100644 index 0000000000..f0a23bf236 --- /dev/null +++ b/ConfigureChecks.cmake @@ -0,0 +1,54 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ) + +find_program(some_x_program NAMES iceauth xrdb xterm) +if (NOT some_x_program) + set(some_x_program /usr/bin/xrdb) + message(WARNING "Warning: Could not determine X binary directory. Assuming /usr/bin.") +endif (NOT some_x_program) +get_filename_component(proto_xbindir "${some_x_program}" PATH) +get_filename_component(XBINDIR "${proto_xbindir}" ABSOLUTE) +get_filename_component(xrootdir "${XBINDIR}" PATH) +set(XLIBDIR "${xrootdir}/lib/X11") + +check_function_exists(getpassphrase HAVE_GETPASSPHRASE) +check_function_exists(vsyslog HAVE_VSYSLOG) +check_function_exists(statvfs HAVE_STATVFS) + +check_include_files(limits.h HAVE_LIMITS_H) +check_include_files(sys/time.h HAVE_SYS_TIME_H) # ksmserver, ksplashml, sftp +check_include_files(stdint.h HAVE_STDINT_H) # kcontrol/kfontinst +check_include_files("sys/stat.h;sys/vfs.h" HAVE_SYS_VFS_H) # statvfs for plasma/solid +check_include_files("sys/stat.h;sys/statvfs.h" HAVE_SYS_STATVFS_H) # statvfs for plasma/solid +check_include_files(sys/param.h HAVE_SYS_PARAM_H) +check_include_files("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H) +check_include_files("sys/types.h;sys/statfs.h" HAVE_SYS_STATFS_H) +check_include_files(unistd.h HAVE_UNISTD_H) +check_include_files(malloc.h HAVE_MALLOC_H) +check_function_exists(statfs HAVE_STATFS) +macro_bool_to_01(FONTCONFIG_FOUND HAVE_FONTCONFIG) # kcontrol/{fonts,kfontinst} +macro_bool_to_01(FREETYPE_FOUND HAVE_FREETYPE) # kcontrol/fonts +macro_bool_to_01(OPENGL_FOUND HAVE_OPENGL) # kwin +macro_bool_to_01(X11_XShm_FOUND HAVE_XSHM) # kwin, ksplash +macro_bool_to_01(X11_XTest_FOUND HAVE_XTEST) # khotkeys, kxkb, kdm +macro_bool_to_01(X11_Xcomposite_FOUND HAVE_XCOMPOSITE) # kicker, kwin +macro_bool_to_01(X11_Xcursor_FOUND HAVE_XCURSOR) # many uses +macro_bool_to_01(X11_Xdamage_FOUND HAVE_XDAMAGE) # kwin +macro_bool_to_01(X11_Xfixes_FOUND HAVE_XFIXES) # klipper, kicker, kwin +macro_bool_to_01(X11_Xinerama_FOUND HAVE_XINERAMA) +macro_bool_to_01(X11_Xrandr_FOUND HAVE_XRANDR) # kwin +macro_bool_to_01(X11_Xrender_FOUND HAVE_XRENDER) # kcontrol/style, kicker +macro_bool_to_01(X11_xf86misc_FOUND HAVE_XF86MISC) # kdesktop and kcontrol/lock +macro_bool_to_01(X11_dpms_FOUND HAVE_DPMS) # kdesktop +macro_bool_to_01(X11_XSync_FOUND HAVE_XSYNC) # kwin + +set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) + +check_function_exists(getpeereid HAVE_GETPEEREID) # kdesu +check_function_exists(setpriority HAVE_SETPRIORITY) # kscreenlocker + +set(CMAKE_REQUIRED_INCLUDES ${X11_Xrandr_INCLUDE_PATH}/Xrandr.h) +set(CMAKE_REQUIRED_LIBRARIES ${X11_Xrandr_LIB}) +check_function_exists(XRRGetScreenSizeRange XRANDR_1_2_FOUND) +macro_bool_to_01(XRANDR_1_2_FOUND HAS_RANDR_1_2) +check_function_exists(XRRGetScreenResourcesCurrent XRANDR_1_3_FOUND) +macro_bool_to_01(XRANDR_1_3_FOUND HAS_RANDR_1_3) diff --git a/HACKING b/HACKING new file mode 100644 index 0000000000..fcea06b952 --- /dev/null +++ b/HACKING @@ -0,0 +1,21 @@ +Understanding the Code +====================== +Various design documents can be found in the design/ directory and API documentation +generated from the headers is hosted at http://api.kde.org. + +Finding the Team +================ +You can find the Plasma team on: + + * our development mailing list at plasma-devel@kde.org + * our irc channel in #plasma on irc.freenode.net + +Coding Style +============ +New code should follow the kdelibs hacking style, as found here: + + http://techbase.kde.org/Policies/Kdelibs_Coding_Style + +Existing code that does not follow this style should be migrated over during +editting. + diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 0000000000..acf9e85cb8 --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,21 @@ +/** @mainpage Plasma + +Documentation for the Plasma libraries and utilities. +

+- engines + (classes)\n + Engines for Plasma +- applets + (classes)\n + Plasma Applets +- animators + (classes)\n + Plasma Animators + + +*/ + +// DOXYGEN_SET_PROJECT_NAME = Plasma +// DOXYGEN_SET_RECURSIVE = YES +// DOXYGEN_EXCLUDE = engines applets animators +// vim:ts=4:sw=4:expandtab:filetype=doxygen diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt new file mode 100644 index 0000000000..875b426723 --- /dev/null +++ b/applets/CMakeLists.txt @@ -0,0 +1,13 @@ +add_subdirectory(kickoff) +plasma_install_package(trash org.kde.plasma.trash) + +if(NOT WIN32) + # tasks and windowlist depend on libs/taskmanager + add_subdirectory(taskmanager) + plasma_install_package(window-list org.kde.plasma.windowlist) + +if(KF5Activities_FOUND) + add_subdirectory(pager) + add_subdirectory(showActivityManager) +endif() +endif() diff --git a/applets/kickoff/CMakeLists.txt b/applets/kickoff/CMakeLists.txt new file mode 100644 index 0000000000..8d21e41bec --- /dev/null +++ b/applets/kickoff/CMakeLists.txt @@ -0,0 +1,62 @@ + +####################################################################################### +# Package +plasma_install_package(package org.kde.plasma.kickoff) + + +####################################################################################### +# Kickoff Library + +set(libkickoff_SRCS + core/applicationmodel.cpp + core/favoritesmodel.cpp + core/itemhandlers.cpp + core/kickoffabstractmodel.cpp + core/kickoffmodel.cpp + core/kickoffproxymodel.cpp + core/kickoffplugin.cpp + core/krunnermodel.cpp + core/leavemodel.cpp + core/models.cpp + core/recentapplications.cpp + core/recentlyusedmodel.cpp + core/systemmodel.cpp + core/kuserproxy.cpp + core/urlitemlauncher.cpp +) + +qt5_add_dbus_adaptor(libkickoff_SRCS core/org.kde.kickoff.xml core/applicationmodel.h Kickoff::ApplicationModel) +qt5_add_dbus_adaptor(libkickoff_SRCS core/org.kde.kickoff.recent.xml core/recentlyusedmodel.h Kickoff::RecentlyUsedModel) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${plasma-desktop_SOURCE_DIR}) + set(screensaver_xml ${SCREENSAVER_DBUS_INTERFACE}) + set(krunner_xml ${KRUNNERAPP_INTERFACE}) + set(ksmserver_xml ${KSMSERVER_DBUS_INTERFACE}) +else() + set(screensaver_xml ${CMAKE_SOURCE_DIR}/plasma-workspace/ksmserver/screenlocker/dbus/org.freedesktop.ScreenSaver.xml) + set(krunner_xml ${CMAKE_SOURCE_DIR}/plasma-workspace/krunner/dbus/org.kde.krunner.App.xml) + set(ksmserver_xml ${CMAKE_SOURCE_DIR}/plasma-workspace/ksmserver/org.kde.KSMServerInterface.xml) +endif() + +qt5_add_dbus_interface(libkickoff_SRCS ${screensaver_xml} screensaver_interface) +qt5_add_dbus_interface(libkickoff_SRCS ${krunner_xml} krunner_interface) +qt5_add_dbus_interface(libkickoff_SRCS ${ksmserver_xml} ksmserver_interface) + +install(FILES core/qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/kickoff) +add_library(kickoffplugin SHARED ${libkickoff_SRCS}) +target_link_libraries(kickoffplugin + Qt5::Core + Qt5::Qml + Qt5::DBus + KF5::CoreAddons + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::KIOFileWidgets + KF5::Runner + KF5::Solid + KF5::Service + KF5::KDE4Support + PW::KWorkspace) +install(TARGETS kickoffplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/kickoff) + diff --git a/applets/kickoff/DESIGN-GOALS b/applets/kickoff/DESIGN-GOALS new file mode 100644 index 0000000000..4c7adc46bf --- /dev/null +++ b/applets/kickoff/DESIGN-GOALS @@ -0,0 +1,71 @@ + +Project Proposal: Kickoff KDE 4 Rewrite + +This is a re-implementation of the Kickoff menu for KDE 3 originally created +by the OpenSuSE team. + +Overall goals: + +1. Implement the Kickoff user interface using Qt/KDE 4 technology + +2. Make it easy for distributions to modify the menu contents + +3. Make it easy for distributions to add their own branding + +Detailed goals: + +1. Implement the Kickoff user interface using Qt/KDE 4 technology + + -> Clean separation of core and user interface + -> eg. Make good use of Qt 4's model/view classes + -> Use Strigi for search and analysis (or possibly just Xesam interface?) + -> Use Solid for getting data for removable device list + -> Attractive, original, but not overbearing graphical effects + using Qt 4's new painting and animation features. + + To decide on: + + -> Consult the Kickoff/KDE 3 developers and OpenSuSE users about + any desired changes to the user interface. + -> Whether to use Plasma applets and widgets or Qt's widgets and + QAbstractItemView as the base for the view classes. + -> Provision of Plasma data engines for applets to use to query + applications, recent documents, favorites etc. + -> Should facilities be provided for distributions to replace + Strigi with their choice of search engine? + + Ideas to explore in future: + + -> Use Sonnet for spell-checking the user's search query + -> Use Phonon for previews of audio and video content + in the favorites and recently used sections + -> Light KWin integration for interesting effects for revealing, + hiding or rendering the Kickoff launcher on composited '3D' + desktops. + +Goals 2 and 3 are derived from looking at the KDE 3 implementation +of Kickoff and also the way in which distributions customise KDE. +Consulting distribution representatives is important to clarify +these particular goals. + +2. Make it easy for distributions to modify the menu contents + + -> Different distributions may have different tools for system + configuration ( eg. System Settings , YaST , Mandriva's tools ) + -> The available options on the leave page and the way in which + those tools work varies depending on the distribution + +3. Make it easy for distributions to add their own branding + + -> The launcher menu is a highly visible part of the desktop, + distributions are therefore likely to want to brand it with + their own logos, colors, icons etc. + This does not mean infinite theme-ability, but rather a good + out-of-the-box look which distros can easily tweak so that + it is obvious which distribution is being used from looking + at a screenshot, and also to allow distros to make some + obvious cosmetic changes between versions. + + (See the Ubuntu art specs for example which state that each + new version is supposed to have a visual look which is + distinct from the previous version) diff --git a/applets/kickoff/Messages.sh b/applets/kickoff/Messages.sh new file mode 100644 index 0000000000..42f37cc310 --- /dev/null +++ b/applets/kickoff/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_kickoff.pot +rm -f rc.cpp diff --git a/applets/kickoff/STATUS-TODO b/applets/kickoff/STATUS-TODO new file mode 100644 index 0000000000..f837793908 --- /dev/null +++ b/applets/kickoff/STATUS-TODO @@ -0,0 +1,66 @@ + +Current status of Kickoff/KDE 4: +Bugs and missing features (indicated with a '-') +are listed in priority order for each category. +================================================ + +Favorites View + + Works + - Favorites are not remembered correctly between sessions + when Kickoff is run from a Plasma applet + - Missing drag and drop of items from other views into favorites + - Missing drag and drop re-arranging of items + +Applications View + + Works. + + When KDE 3 and KDE 4 versions of an application are available only + the KDE 4 one is shown. + - Oxygen application category icons are not shown next to categories + +My Computer View + + Works. + + Uses Solid to query devices and respond to addition or removal of + devices + - Folders are currently opened in Konqueror. They should open in + Dolphin + +Recently Used (History?) View + + Works + - Missing a suitable icon for the tab + +Leave View + + Log-off, Shutdown and Restart work + - Sleep, Hibernate do not work + - Log-off, Shutdown and Restart show an unnecessary confirmation + dialog which gives users the Log-off/Shutdown/Restart choice again + - Missing icons for the Sleep, Shutdown, Restart actions + +Search View + + Application search by name, generic name or keywords works + - Strigi search doesn't work due to a Strigi problem which Jos' is + presently fixing + * The other search handlers that query Bookmarks, + Contacts etc. should probably be shared with KRunner. This needs + mailing list discussion. + +Presentation + + When used as a Plasma applet the Kickoff window does not + appear and disappear smoothly. (Works okay when standalone + Kickoff application is run) [Also Performance?] + - Missing the custom QTabBar styling to draw the view names + under their icons and draw a pretty gradient + - Missing the "username on computer" text + - Missing the branding logo + + Experimented with multiple columns in the Favorites/My Computer/Recently Used/Leave + views. + +Performance: + - Scrolling up and down in the application view is not as smooth + as it should be in some categories. Perhaps an issue with icons + from certain sources? + - When opening a previously unopened application category there + may be a slight delay and HDD activity causing the scrolling animation + to have few frames (the animation duration is not affected) + - Currently no delayed initialization is performed. The data required + for all tabs is loaded on startup, except the application view where + only the root branch is loaded initially. diff --git a/applets/kickoff/core/applicationmodel.cpp b/applets/kickoff/core/applicationmodel.cpp new file mode 100644 index 0000000000..f1fd904d83 --- /dev/null +++ b/applets/kickoff/core/applicationmodel.cpp @@ -0,0 +1,696 @@ +/* + Copyright 2007 Pino Toscano + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "applicationmodel.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "kickoffadaptor.h" +// Local +#include "models.h" + +#include + +template <> inline +void KConfigGroup::writeEntry(const char *pKey, + const KGlobalSettings::Completion& aValue, + KConfigBase::WriteConfigFlags flags) +{ + writeEntry(pKey, int(aValue), flags); +} + +namespace Kickoff +{ + +class AppNode +{ +public: + AppNode() + : parent(0), + fetched(false), + isDir(false), + isSeparator(false), + subTitleMandatory(false) + { + } + + ~AppNode() + { + qDeleteAll(children); + } + + QList children; + + QIcon icon; + QString iconName; + QString genericName; + QString appName; + QString relPath; + QString desktopEntry; + + AppNode *parent; + DisplayOrder displayOrder; + bool fetched : 1; + bool isDir : 1; + bool isSeparator : 1; + bool subTitleMandatory : 1; +}; + +class ApplicationModelPrivate +{ +public: + ApplicationModelPrivate(ApplicationModel *qq, bool _allowSeparators) + : q(qq), + root(new AppNode()), + duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy), + systemApplicationPolicy(ApplicationModel::ShowApplicationAndSystemPolicy), +// primaryNamePolicy(ApplicationModel::GenericNamePrimary), + displayOrder(NameAfterDescription), + allowSeparators(_allowSeparators), + showRecentlyInstalled(true) + { + systemApplications = Kickoff::systemApplicationList(); + reloadTimer = new QTimer(qq); + reloadTimer->setSingleShot(true); + QObject::connect(reloadTimer, SIGNAL(timeout()), qq, SLOT(delayedReloadMenu())); + } + + ~ApplicationModelPrivate() + { + delete root; + } + + void fillNode(const QString &relPath, AppNode *node); + static QHash iconNameMap(); + + ApplicationModel *q; + QWeakPointer applet; + AppNode *root; + ApplicationModel::DuplicatePolicy duplicatePolicy; + ApplicationModel::SystemApplicationPolicy systemApplicationPolicy; + QStringList systemApplications; + DisplayOrder displayOrder; + bool allowSeparators; + bool showRecentlyInstalled; + QTimer *reloadTimer; + + QStringList newInstalledPrograms; + QHash seenPrograms; +}; + +void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node) +{ + if (_relPath=="new/") { + Q_FOREACH (const QString &it, newInstalledPrograms) { + KService::Ptr p = KService::serviceByStorageId(it); + + if (p->noDisplay()) { + continue; + } + + AppNode *newnode = new AppNode(); + newnode->icon = KIcon(p->icon()); + newnode->appName = p->name(); + newnode->genericName = p->genericName(); + newnode->desktopEntry = p->entryPath(); + newnode->parent = node; + node->children.append(newnode); + } + return; + } + + KServiceGroup::Ptr root = KServiceGroup::group(_relPath); + + if (!root || !root->isValid()) { + return; + } + + const KServiceGroup::List list = root->entries(true /* sorted */, + true /* exclude no display entries */, + allowSeparators /* allow separators */, + true /* sort by generic name */); + + // application name <-> service map for detecting duplicate entries + QHash existingServices; + + // generic name <-> node mapping to determinate duplicate generic names + QHash > genericNames; + + for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { + QString icon; + QString appName; + QString genericName; + QString relPath = _relPath; + QString desktopEntry; + bool isDir = false; + bool isSeparator = false; + const KSycocaEntry::Ptr p = (*it); + + if (p->isType(KST_KService)) { + const KService::Ptr service = p; + + if (service->noDisplay()) { + continue; + } + + icon = service->icon(); + appName = service->name(); + genericName = service->genericName(); + desktopEntry = service->entryPath(); + + // check for duplicates (eg. KDE 3 and KDE 4 versions of application + // both present) + if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy && + existingServices.contains(appName)) { + if (Kickoff::isLaterVersion(existingServices[appName], service)) { + continue; + } else { + // find and remove the existing entry with the same name + for (int i = node->children.count() - 1; i >= 0; --i) { + AppNode *app = node->children.at(i); + if (app->appName == appName && + app->genericName == genericName && + app->iconName == icon) { + app = node->children.takeAt(i); + const QString s = app->genericName.toLower(); + if (genericNames.contains(s)) { + QList list = genericNames[s]; + for (int j = list.count() - 1; j >= 0; --j) { + if(list.at(j) == app) { + list.takeAt(j); + } + } + genericNames[s] = list; + } + delete app; + } + } + } + } + + if (systemApplicationPolicy == ApplicationModel::ShowSystemOnlyPolicy && + systemApplications.contains(service->desktopEntryName())) { + // don't duplicate applications that are configured to appear in the System tab + // in the Applications tab + continue; + } + + existingServices[appName] = service; + } else if (p->isType(KST_KServiceGroup)) { + const KServiceGroup::Ptr serviceGroup = p; + + if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) { + continue; + } + + qDebug() << "Service group" << serviceGroup->entryPath() << serviceGroup->icon() << serviceGroup->relPath() << serviceGroup->directoryEntryPath(); + + icon = serviceGroup->icon(); + if (iconNameMap().contains(icon)) { + icon = iconNameMap().value(icon); + } + + desktopEntry = serviceGroup->entryPath(); + genericName = serviceGroup->caption(); + relPath = serviceGroup->relPath(); + appName = serviceGroup->comment(); + isDir = true; + } else if (p->isType(KST_KServiceSeparator)) { + isSeparator = true; + } else { + qWarning() << "KServiceGroup: Unexpected object in list!"; + continue; + } + + AppNode *newnode = new AppNode(); + newnode->iconName = icon; + newnode->icon = KIcon(icon); + newnode->appName = appName; + newnode->genericName = genericName; + newnode->relPath = relPath; + newnode->desktopEntry = desktopEntry; + newnode->isDir = isDir; + newnode->isSeparator = isSeparator; + newnode->parent = node; + node->children.append(newnode); + + if (p->isType(KST_KService)) { + const QString s = genericName.toLower(); + QList list = genericNames.value(s); + list.append(newnode); + genericNames[s] = list; + } + } + + if (showRecentlyInstalled && _relPath.isEmpty() && !newInstalledPrograms.isEmpty()) { + AppNode *newnode = new AppNode(); + newnode->icon = KIcon("chronometer"); + newnode->appName = i18n("Recently Installed"); + newnode->relPath = "new/"; + newnode->isDir = true; + newnode->parent = node; + node->children.prepend(newnode); + } + + // set the subTitleMandatory field for nodes that do not provide a unique generic + // name what may help us on display to show in such cases also the subtitle to + // provide a hint to the user what the duplicate entries are about. + foreach (const QList &list, genericNames) { + if (list.count() > 1) { + foreach (AppNode* n, list) { + n->subTitleMandatory = true; + } + } + } +} + +ApplicationModel::ApplicationModel(QObject *parent, bool allowSeparators) + : KickoffAbstractModel(parent), + d(new ApplicationModelPrivate(this, allowSeparators)) +{ + QHash roles; + roles[Qt::DisplayRole] = "display"; + roles[Qt::DecorationRole] = "decoration"; + roles[Kickoff::SubTitleRole] = "subtitle"; + roles[Kickoff::UrlRole] = "url"; + roles[Kickoff::GroupNameRole] = "group"; + setRoleNames(roles); + QDBusConnection dbus = QDBusConnection::sessionBus(); + (void)new KickoffAdaptor(this); + QDBusConnection::sessionBus().registerObject("/kickoff", this); + dbus.connect(QString(), "/kickoff", "org.kde.plasma", "reloadMenu", this, SLOT(reloadMenu())); + connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(checkSycocaChange(QStringList))); + + // TODO: remove me again + reloadMenu(); +} + +ApplicationModel::~ApplicationModel() +{ + delete d; +} + +bool ApplicationModel::canFetchMore(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return false; + + AppNode *node = static_cast(parent.internalPointer()); + return node->isDir && !node->fetched; +} + +// void ApplicationModel::setNameDisplayOrder(DisplayOrder displayOrder) +// { +// d->displayOrder = displayOrder; +// } +// +// DisplayOrder ApplicationModel::nameDisplayOrder() const +// { +// return d->displayOrder; +// } + +void ApplicationModel::setShowRecentlyInstalled(bool showRecentlyInstalled) +{ + if (d->showRecentlyInstalled != showRecentlyInstalled) { + d->showRecentlyInstalled = showRecentlyInstalled; + reloadMenu(); + } +} + +bool ApplicationModel::showRecentlyInstalled() const +{ + return d->showRecentlyInstalled; +} + +int ApplicationModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QVariant ApplicationModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + AppNode *node = static_cast(index.internalPointer()); + + switch (role) { + case Qt::DisplayRole: + if (node->genericName.isEmpty()) { + return node->appName; + } else { + return node->genericName; + } + case Kickoff::SubTitleRole: + if (node->appName.isEmpty() || node->appName == node->genericName) { + return QString(); //return empty string to avoid errors in QML assigning null to Text + } else { + return node->appName; + } + case Kickoff::UrlRole: + if (node->isDir) { + return QString::fromLatin1("applications://%1").arg(node->desktopEntry); + } else { + return node->desktopEntry; + } + break; + case Kickoff::SubTitleMandatoryRole: + return node->subTitleMandatory; + break; + case Kickoff::SeparatorRole: + return node->isSeparator; + break; + case Qt::DecorationRole: + return node->icon; + break; + case Kickoff::RelPathRole: + return node->relPath; + break; + case Kickoff::IconNameRole: + return node->iconName; + break; + default: + ; + } + return QVariant(); +} + +void ApplicationModel::fetchMore(const QModelIndex &parent) +{ + if (!parent.isValid()) { + return; + } + + AppNode *node = static_cast(parent.internalPointer()); + if (!node->isDir) { + return; + } + + // FIXME? QML's QQmlAdaptorModel (the "backend" used for VisualDataModel) always hard-resets + // the model when the source model (this one) changes layout, which breaks kickoff's + // navigation as opening a category for the first time calls this and then calls + // layoutChanged() --> QML's VisualDataModel is reset, that in turn invalidates + // VDM's rootIndex and when that happens, Kickoff is switched to the root level. + // Disabling the layout signals makes Kickoff's navigation work properly. + +// emit layoutAboutToBeChanged(QList() << parent); + d->fillNode(node->relPath, node); + node->fetched = true; +// changePersistentIndex(parent, parent); +// emit layoutChanged(QList() << parent); +} + +bool ApplicationModel::hasChildren(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return true; + } + + AppNode *node = static_cast(parent.internalPointer()); + return node->isDir; +} + +QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || section != 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return i18n("All Applications"); + break; + default: + return QVariant(); + } +} + +QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || column != 0) + return QModelIndex(); + + AppNode *node = d->root; + if (parent.isValid()) + node = static_cast(parent.internalPointer()); + + if (row >= node->children.count()) + return QModelIndex(); + else + return createIndex(row, 0, node->children.at(row)); +} + +QModelIndex ApplicationModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + return QModelIndex(); + } + + AppNode *node = static_cast(index.internalPointer()); + if (node->parent->parent) { + int id = node->parent->parent->children.indexOf(node->parent); + + if (id >= 0 && id < node->parent->parent->children.count()) { + return createIndex(id, 0, node->parent); + } + } + + return QModelIndex(); +} + +int ApplicationModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return d->root->children.count(); + } + + AppNode *node = static_cast(parent.internalPointer()); + return node->children.count(); +} + +void ApplicationModel::setDuplicatePolicy(DuplicatePolicy policy) +{ + if (d->duplicatePolicy != policy) { + d->duplicatePolicy = policy; + reloadMenu(); + } +} + +void ApplicationModel::setSystemApplicationPolicy(SystemApplicationPolicy policy) +{ + if (d->systemApplicationPolicy != policy) { + d->systemApplicationPolicy = policy; + reloadMenu(); + } +} + +void ApplicationModel::delayedReloadMenu() +{ + if (!d->reloadTimer->isActive()) { + d->reloadTimer->start(200); + } +} + +void ApplicationModel::reloadMenu() +{ + beginResetModel(); + delete d->root; + d->root = new AppNode(); + createNewProgramList(); + d->fillNode(QString(), d->root); + endResetModel(); +} + +void ApplicationModel::checkSycocaChange(const QStringList &changes) +{ + if (changes.contains("services") || changes.contains("apps")) { + reloadMenu(); + } +} + +ApplicationModel::DuplicatePolicy ApplicationModel::duplicatePolicy() const +{ + return d->duplicatePolicy; +} + +ApplicationModel::SystemApplicationPolicy ApplicationModel::systemApplicationPolicy() const +{ + return d->systemApplicationPolicy; +} + +void ApplicationModel::setApplet(Plasma::Applet *applet) +{ + if (d->applet.data() != applet) { + d->applet = applet; + createNewProgramList(); + } +} + +void ApplicationModel::createNewProgramList() +{ + if (!d->applet) { + return; + } + + d->newInstalledPrograms.clear(); + if (!d->showRecentlyInstalled) { + return; + } + + //FIXME + KConfigGroup kickoffrc = d->applet.data()->config(); + foreach (const QString &it, kickoffrc.keyList()) { + d->seenPrograms.insert(it, QDate::fromString(kickoffrc.readEntry(it), Qt::ISODate)); + } + + bool initialize = (d->seenPrograms.isEmpty()); + + bool seenProgramsChanged = createNewProgramListForPath(QString()); + + if (initialize) { + // on first start, set all entries' dates to empty (means: they are not new) + for (QHash::Iterator it = d->seenPrograms.begin(); it != d->seenPrograms.end(); ++it) + *it = QDate(); + + d->newInstalledPrograms.clear(); + } + + if (seenProgramsChanged) { + for (QHash::Iterator it = d->seenPrograms.begin(); it != d->seenPrograms.end(); ++it) { + kickoffrc.writeEntry(it.key(), it.value().toString(Qt::ISODate)); + } + kickoffrc.sync(); + } +} + +bool ApplicationModel::createNewProgramListForPath(const QString &relPath) +{ + bool seenProgramsChanged = false; + + KServiceGroup::Ptr group = KServiceGroup::group(relPath); + if (!group || !group->isValid()) { + return false; + } + + const KServiceGroup::List list = group->entries(); + + KServiceGroup::List::ConstIterator it = list.begin(); + for (; it != list.end(); ++it) { + KSycocaEntry::Ptr e = (*it); + + if (e->isType(KST_KServiceGroup)) { + KServiceGroup::Ptr g(e); + if (!g->noDisplay()) { + if (createNewProgramListForPath(g->relPath())) + seenProgramsChanged = true; + } + } else if (e->isType(KST_KService)) { + KService::Ptr s(e); + if (s->isApplication() && !s->noDisplay()) { + QString shortStorageId = s->storageId().remove(".desktop"); + QHash::Iterator it_find = d->seenPrograms.find(shortStorageId); + if (it_find == d->seenPrograms.end()) { + seenProgramsChanged = true; + d->seenPrograms.insert(shortStorageId, QDate::currentDate()); + if (!d->newInstalledPrograms.contains(s->storageId())) { + d->newInstalledPrograms += s->storageId(); + } + } + else { + QDate date = it_find.value(); + if (date.isValid()) { + if (date.daysTo(QDate::currentDate()) < 3) { + if (!d->newInstalledPrograms.contains(s->storageId())) { + d->newInstalledPrograms += s->storageId(); + } + } + else { + seenProgramsChanged = true; + (*it_find) = QDate(); // this entry is not new anymore + } + } + } + } + } + } + return seenProgramsChanged; +} + +/** + * FIXME This is a temporary workaround to map the icon names found + * in the desktop directory files (from /usr/share/desktop-directories) + * into the Oxygen icon names. (Only applies if the Gnome menu files + * are also installed) + * + * This list was compiled from Kubuntu 7.04 with the gnome-menus + * package present. + * + * This needs to be discussed on kde-core-devel and fixed + */ +QHash ApplicationModelPrivate::iconNameMap() +{ + static QHash map; + if (map.isEmpty()) { + map.insert("gnome-util", "applications-accessories"); + // accessibility Oxygen icon was missing when this list was compiled + map.insert("accessibility-directory", "applications-other"); + map.insert("gnome-devel", "applications-development"); + map.insert("package_edutainment", "applications-education"); + map.insert("gnome-joystick", "applications-games"); + map.insert("gnome-graphics", "applications-graphics"); + map.insert("gnome-globe", "applications-internet"); + map.insert("gnome-multimedia", "applications-multimedia"); + map.insert("gnome-applications", "applications-office"); + map.insert("gnome-system", "applications-system"); + } + return map; +} + +} // namespace Kickoff + + +#include "applicationmodel.moc" diff --git a/applets/kickoff/core/applicationmodel.h b/applets/kickoff/core/applicationmodel.h new file mode 100644 index 0000000000..ad5b4cb034 --- /dev/null +++ b/applets/kickoff/core/applicationmodel.h @@ -0,0 +1,131 @@ +/* + Copyright 2007 Pino Toscano + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef APPLICATIONMODEL_H +#define APPLICATIONMODEL_H + +#include "kickoffabstractmodel.h" +#include "models.h" + +namespace Plasma +{ + class Applet; +} // namespace Plasma + +namespace Kickoff +{ + +class ApplicationModelPrivate; + +/** + * ApplicationModel provides a tree model containing all of the user's installed graphical programs. + * The applications are arranged into categories, based on the information in their .desktop files. + */ +class ApplicationModel : public KickoffAbstractModel +{ + Q_OBJECT + +public: + ApplicationModel(QObject *parent = 0, bool allowSeparators = false); + virtual ~ApplicationModel(); + + /** + * This enum describes the policy for + * handling duplicate applications (that is, + * two applications with the same name in the same group) + */ + enum DuplicatePolicy { + /** Display duplicate entries. */ + ShowDuplicatesPolicy, + /** + * Show only the entry for the most recent + * version of the application. + * + * Currently only a crude heuristic to determine whether the + * application is from KDE 3 or KDE 4 is used to determine + * recent-ness. + * + * eg. If MyGame/KDE 3 and MyGame/KDE 4 are found + * show only MyGame/KDE 4 + */ + ShowLatestOnlyPolicy + }; + + /** + * This enum describes the policy for + * handling applications that are configured to appear + * in the System tab. + */ + enum SystemApplicationPolicy { + /** Display entries in Applications tab and System tab. */ + ShowApplicationAndSystemPolicy, + /** Display entry only in System tab. */ + ShowSystemOnlyPolicy + }; + + /** + * Sets the policy for handling duplicate applications. + * See DuplicatePolicy + */ + void setDuplicatePolicy(DuplicatePolicy policy); + /** See setDuplicatePolicy() */ + DuplicatePolicy duplicatePolicy() const; + + /** + * Sets the policy for handling System applications. + * See SystemApplicationPolicy + */ + void setSystemApplicationPolicy(SystemApplicationPolicy policy); + /** See setSystemApplicationPolicy() */ + SystemApplicationPolicy systemApplicationPolicy() const; + + // reimplemented from QAbstractItemModel + virtual bool canFetchMore(const QModelIndex &parent) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual void fetchMore(const QModelIndex &parent); + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + + void setApplet(Plasma::Applet *applet); + void setShowRecentlyInstalled(bool showRecentlyInstalled); + bool showRecentlyInstalled() const; + +public Q_SLOTS: + void reloadMenu(); + void delayedReloadMenu(); + void checkSycocaChange(const QStringList &changes); + +private: + friend class ApplicationModelPrivate; + ApplicationModelPrivate *const d; + + void createNewProgramList(); + bool createNewProgramListForPath(const QString &relPath); + + Q_DISABLE_COPY(ApplicationModel) +}; + +} + +#endif diff --git a/applets/kickoff/core/favoritesmodel.cpp b/applets/kickoff/core/favoritesmodel.cpp new file mode 100644 index 0000000000..5f5e98adec --- /dev/null +++ b/applets/kickoff/core/favoritesmodel.cpp @@ -0,0 +1,376 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//Own +#include "favoritesmodel.h" +#include "krunnermodel.h" + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + + + +using namespace Kickoff; + +class FavoritesModel::Private +{ +public: + Private(FavoritesModel *parent) + : q(parent), + displayOrder(NameAfterDescription) + { + init(); + } + + void init() + { + } + + void addFavoriteItem(const QString& url) + { + QStandardItem *item = StandardItemFactory::createItemForUrl(url, displayOrder); + item->setData(i18n("Favorites"), Kickoff::GroupNameRole); + q->appendRow(item); + } + + void moveFavoriteItem(int startRow, int destRow) + { + if (destRow == startRow) { + return; + } + + QStandardItem *item = q->takeItem(startRow); + q->removeRow(startRow); + q->insertRow(destRow, item); + } + + void removeFavoriteItem(const QString& url) + { + Q_UNUSED(url) + QModelIndexList matches = q->match(q->index(0, 0), UrlRole, + url, -1, + Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap | Qt::MatchRecursive)); + + qDebug() << "Removing item matches" << matches; + + foreach (const QModelIndex& index, matches) { + QStandardItem *item = q->itemFromIndex(index); + if (item->parent()) { + item->parent()->removeRow(item->row()); + } else { + qDeleteAll(q->takeRow(item->row())); + } + } + } + + static void loadFavorites() + { + globalFavoriteList.clear(); + globalFavoriteSet.clear(); + + KConfigGroup favoritesGroup = componentData().config()->group("Favorites"); + QList favoriteList = favoritesGroup.readEntry("FavoriteURLs", QList()); + if (favoriteList.isEmpty()) { + favoriteList = defaultFavorites(); + } + + foreach (const QString &favorite, favoriteList) { + FavoritesModel::add(favorite); + } + } + + static QList defaultFavorites() + { + QList applications; + applications << "konqbrowser" << "KMail2" << "kdesystemsettings" << "dolphin" << "konsole" << "kate" << "kwrite" << "kinfocenter"; + + QList desktopFiles; + + foreach (const QString& application, applications) { + KService::Ptr service = KService::serviceByStorageId(application + ".desktop"); + if (service) { + desktopFiles << service->entryPath(); + } + } + + return desktopFiles; + } + + static void saveFavorites() + { + KConfigGroup favoritesGroup = componentData().config()->group("Favorites"); + favoritesGroup.writeEntry("FavoriteURLs", globalFavoriteList); + favoritesGroup.config()->sync(); + } + + static QList globalFavoriteList; + static QSet globalFavoriteSet; + static QSet models; + + FavoritesModel * const q; + DisplayOrder displayOrder; +}; + +QList FavoritesModel::Private::globalFavoriteList; +QSet FavoritesModel::Private::globalFavoriteSet; +QSet FavoritesModel::Private::models; + +FavoritesModel::FavoritesModel(QObject *parent) + : KickoffModel(parent) + , d(new Private(this)) +{ + Private::models << this; + if (Private::models.count() == 1 && Private::globalFavoriteList.isEmpty()) { + Private::loadFavorites(); + } else { + foreach (const QString &url, Private::globalFavoriteList) { + d->addFavoriteItem(url); + } + } + +} + +FavoritesModel::~FavoritesModel() +{ + Private::models.remove(this); + + if (Private::models.isEmpty()) { + Private::saveFavorites(); + } + + delete d; +} + +void FavoritesModel::add(const QString& url) +{ + QString handledUrl; + KService::Ptr service = Kickoff::serviceForUrl(url); + if (service) { + handledUrl = service->entryPath(); + } else { + handledUrl = url; + } + + Private::globalFavoriteList << handledUrl; + Private::globalFavoriteSet << handledUrl; + + foreach (FavoritesModel* model, Private::models) { + model->d->addFavoriteItem(handledUrl); + } + + // save after each add in case we crash + Private::saveFavorites(); +} + +void FavoritesModel::move(int startRow, int destRow) +{ + // just move the item +// Private::globalFavoriteList.move(startRow, destRow); + + foreach (FavoritesModel* model, Private::models) { + qDebug() << "Move : " << startRow << destRow << model->rowCount(); + if (destRow >= model->rowCount()) { + destRow = model->rowCount() - 1; + } + if (startRow >= model->rowCount()) { + startRow = model->rowCount() - 1; + } + qDebug() << "Move : " << startRow << destRow << model->rowCount(); + Private::globalFavoriteList.move(startRow, destRow); + model->d->moveFavoriteItem(startRow, destRow); + } + + // save after each add in case we crash + Private::saveFavorites(); +} + +void FavoritesModel::remove(const QString& url) +{ + Private::globalFavoriteList.removeAll(url); + Private::globalFavoriteSet.remove(url); + + foreach (FavoritesModel* model, Private::models) { + model->d->removeFavoriteItem(url); + } + + // save after each remove in case of crash or other mishaps + Private::saveFavorites(); +} + +bool FavoritesModel::isFavorite(const QString& url) +{ + QString handledUrl; + KService::Ptr service = Kickoff::serviceForUrl(url); + if (service) { + handledUrl = service->entryPath(); + } else { + handledUrl = url; + } + + return Private::globalFavoriteSet.contains(handledUrl); +} + +int FavoritesModel::numberOfFavorites() +{ + foreach (FavoritesModel* model, Private::models) { + return model->d->q->rowCount() - 1; + } + + return 0; +} + +void FavoritesModel::sortFavorites(Qt::SortOrder order) +{ + if(Private::models.isEmpty()) { + return; + } + + foreach (FavoritesModel *model, Private::models) { + model->d->q->sort(0, order); + } + + Private::globalFavoriteList.clear(); + + FavoritesModel *model = *Private::models.begin(); + QStandardItem *childData; + for (int i = 0; i <= numberOfFavorites(); i++) { + childData = model->d->q->item(i, 0); + Private::globalFavoriteList.append(childData->data(Kickoff::UrlRole).toString()); + } + Private::saveFavorites(); +} + +void FavoritesModel::sortFavoritesAscending() +{ + sortFavorites(Qt::AscendingOrder); +} + +void FavoritesModel::sortFavoritesDescending() +{ + sortFavorites(Qt::DescendingOrder); +} + +bool FavoritesModel::dropMimeData(const QString& text, const QVariantList& urls, + int row, int column) +{ + qDebug() << "Dropping into row: " << row << urls; + // FIXME: pass DropAction by qml + Qt::DropAction action = Qt::MoveAction; + if (action == Qt::IgnoreAction) { + return true; + } + + if (column > 0 || row > numberOfFavorites()) { + qDebug() << "Exit " << numberOfFavorites(); + return false; + } + + if (action == Qt::MoveAction) { + QModelIndex modelIndex; + QStandardItem *startItem; + int startRow = -1; + int destRow = row; + qDebug() << "Moving ..." << startRow << destRow; + + // look for the favorite that was dragged + bool conv = false; + startRow = text.toInt(&conv, 10); + + if (!conv) { + bool dropped = false; + foreach (const QVariant &varUrl, urls) { + if (!varUrl.value().isValid()) { + continue; + } + const QString path = varUrl.value().toLocalFile(); + if (!KDesktopFile::isDesktopFile(path)) { + continue; + } + KDesktopFile dFile(path); + if (dFile.hasApplicationType() && !dFile.noDisplay()) { + FavoritesModel::add(path); + dropped = true; + } + } + qDebug() << " now move the item to it's new location " << startRow << destRow; + return dropped; + } + + if (destRow < 0) { + qDebug() << "Returning here" << startRow << destRow; + return false; + } + + // now move the item to it's new location + FavoritesModel::move(startRow, destRow); + + return true; + } + + return true; +} + +QVariant FavoritesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || section != 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return i18nc("@title:column", "Favorites"); + break; + default: + return QVariant(); + } +} + +void FavoritesModel::setNameDisplayOrder(DisplayOrder displayOrder) +{ + if (d->displayOrder == displayOrder) { + return; + } + + d->displayOrder = displayOrder; + + foreach (FavoritesModel* model, Private::models) { + model->clear(); + model->d->init(); + } + + Private::loadFavorites(); +} + +DisplayOrder FavoritesModel::nameDisplayOrder() const +{ + return d->displayOrder; +} + +#include "favoritesmodel.moc" diff --git a/applets/kickoff/core/favoritesmodel.h b/applets/kickoff/core/favoritesmodel.h new file mode 100644 index 0000000000..27a0626524 --- /dev/null +++ b/applets/kickoff/core/favoritesmodel.h @@ -0,0 +1,72 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef FAVORITESMODEL_H +#define FAVORITESMODEL_H + +#include "kickoffmodel.h" +#include "models.h" + +namespace Kickoff +{ + +/** + * A model which provides an ordered list of 'favorite' items chosen by the user. + * The items may represent documents, folders, applications, devices or anything else + * identified by a URL. + * + * The information persists between sessions. + */ +class FavoritesModel : public KickoffModel +{ + Q_OBJECT + +public: + FavoritesModel(QObject *parent = NULL); + virtual ~FavoritesModel(); + + /** Add a new item for @p url to the user's favorites list. */ + Q_INVOKABLE static void add(const QString& url); + /** Remove the item associated with @p url from the user's favorites list. */ + Q_INVOKABLE static void remove(const QString& url); + /** Returns true if @p url is in the list of the user's favorite URLs. */ + Q_INVOKABLE static void move(int startRow, int destRow); + static int numberOfFavorites(); + static void sortFavorites(Qt::SortOrder order); + Q_INVOKABLE static bool isFavorite(const QString& url); + + using QAbstractItemModel::dropMimeData; + Q_INVOKABLE virtual bool dropMimeData(const QString& text, const QVariantList& urls, + int row, int column); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + void setNameDisplayOrder(DisplayOrder displayOrder); + DisplayOrder nameDisplayOrder() const; + +public Q_SLOTS: + void sortFavoritesAscending(); + void sortFavoritesDescending(); + +private: + class Private; + Private * const d; +}; + +} + +#endif // FAVORITESMODEL_H diff --git a/applets/kickoff/core/itemhandlers.cpp b/applets/kickoff/core/itemhandlers.cpp new file mode 100644 index 0000000000..b461e1cabb --- /dev/null +++ b/applets/kickoff/core/itemhandlers.cpp @@ -0,0 +1,191 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "itemhandlers.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include + +// KDE Base +#include + +// Local +#include "recentapplications.h" + +// DBus +#include "krunner_interface.h" +#include "screensaver_interface.h" +#include "ksmserver_interface.h" +#include +#include + +using namespace Kickoff; + +bool ServiceItemHandler::openUrl(const KUrl& url) +{ + int result = KToolInvocation::startServiceByDesktopPath(url.pathOrUrl(), QStringList(), 0, 0, 0, "", true); + + if (result == 0) { + KService::Ptr service = KService::serviceByDesktopPath(url.pathOrUrl()); + + if (service) { + RecentApplications::self()->add(service); + } else { + qWarning() << "Failed to find service for" << url; + return false; + } + } + + return result == 0; +} +LeaveItemHandler::LeaveItemHandler(QObject *parent) + : QObject (parent) +{ +} + +bool LeaveItemHandler::openUrl(const KUrl& url) +{ + m_logoutAction = url.path().remove('/'); + + if (m_logoutAction == "sleep") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(suspendRAM())); + return true; + } else if (m_logoutAction == "hibernate") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(suspendDisk())); + return true; + } else if (m_logoutAction == "lock") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(lock())); + return true; + } else if (m_logoutAction == "switch") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(switchUser())); + return true; + } else if (m_logoutAction == "logout" || m_logoutAction == "logoutonly" || + m_logoutAction == "restart" || m_logoutAction == "shutdown") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(logout())); + return true; + } else if (m_logoutAction == "savesession") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(saveSession())); + return true; + } else if (m_logoutAction == "standby") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(standby())); + return true; + } else if (m_logoutAction == "suspendram") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(suspendRAM())); + return true; + } else if (m_logoutAction == "suspenddisk") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(suspendDisk())); + return true; + } else if (m_logoutAction == "run") { + // decouple dbus call, otherwise we'll run into a dead-lock + QTimer::singleShot(0, this, SLOT(runCommand())); + return true; + } + + return false; +} + +void LeaveItemHandler::runCommand() +{ + if (KAuthorized::authorize("run_command")) { + QString interface("org.kde.krunner"); + org::kde::krunner::App krunner(interface, "/App", QDBusConnection::sessionBus()); + krunner.display(); + } +} + +void LeaveItemHandler::logout() +{ + KWorkSpace::ShutdownConfirm confirm = KWorkSpace::ShutdownConfirmDefault; + KWorkSpace::ShutdownType type = KWorkSpace::ShutdownTypeNone; + + if (m_logoutAction == "logout" || m_logoutAction == "logoutonly") { + type = KWorkSpace::ShutdownTypeNone; + } else if (m_logoutAction == "lock") { + qDebug() << "Locking screen"; + } else if (m_logoutAction == "switch") { + qDebug() << "Switching user"; + } else if (m_logoutAction == "restart") { + type = KWorkSpace::ShutdownTypeReboot; + } else if (m_logoutAction == "shutdown") { + type = KWorkSpace::ShutdownTypeHalt; + } + + KWorkSpace::requestShutDown(confirm, type); +} + +void LeaveItemHandler::lock() +{ + QString interface("org.freedesktop.ScreenSaver"); + org::freedesktop::ScreenSaver screensaver(interface, "/ScreenSaver", QDBusConnection::sessionBus()); + screensaver.Lock(); +} + +void LeaveItemHandler::switchUser() +{ + QString interface("org.kde.krunner"); + org::kde::krunner::App krunner(interface, "/App", QDBusConnection::sessionBus()); + krunner.switchUser(); +} + +void LeaveItemHandler::saveSession() +{ + QString interface("org.kde.ksmserver"); + + org::kde::KSMServerInterface ksmserver(interface, "/KSMServer", QDBusConnection::sessionBus()); + if (ksmserver.isValid()) { + ksmserver.saveCurrentSession(); + } +} + +void LeaveItemHandler::standby() +{ + // FIXME: Use standby from KDE Power Management System's interface + suspendRAM(); +} + +void LeaveItemHandler::suspendRAM() +{ + Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState, 0, 0); +} + +void LeaveItemHandler::suspendDisk() +{ + Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState, 0, 0); +} + +#include "itemhandlers.moc" diff --git a/applets/kickoff/core/itemhandlers.h b/applets/kickoff/core/itemhandlers.h new file mode 100644 index 0000000000..8f054901d8 --- /dev/null +++ b/applets/kickoff/core/itemhandlers.h @@ -0,0 +1,59 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ITEMHANDLERS_H +#define ITEMHANDLERS_H + +#include + +#include "urlitemlauncher.h" + +namespace Kickoff +{ + +class ServiceItemHandler : public UrlItemHandler +{ +public: + virtual bool openUrl(const KUrl& url); +}; +class LeaveItemHandler : public QObject, public UrlItemHandler +{ + Q_OBJECT +public: + explicit LeaveItemHandler(QObject *parent = 0); + virtual bool openUrl(const KUrl& url); + +private Q_SLOTS: + void runCommand(); + void logout(); + void lock(); + void switchUser(); + void saveSession(); + void standby(); + void suspendRAM(); + void suspendDisk(); + +private: + QString m_logoutAction; +}; + +} + +#endif // ITEMHANDLERS_H + diff --git a/applets/kickoff/core/kickoffabstractmodel.cpp b/applets/kickoff/core/kickoffabstractmodel.cpp new file mode 100644 index 0000000000..7c51b0f58b --- /dev/null +++ b/applets/kickoff/core/kickoffabstractmodel.cpp @@ -0,0 +1,92 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "kickoffabstractmodel.h" + +// Qt +#include + +// KDE +#include +#include + +// Local +#include "models.h" + +using namespace Kickoff; + +KickoffAbstractModel::KickoffAbstractModel(QObject *parent) + : QAbstractItemModel(parent) +{} + +KickoffAbstractModel::~KickoffAbstractModel() +{} + +Qt::ItemFlags KickoffAbstractModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); + + if (index.isValid()) { + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + } else { + return 0; + } +} + +QMimeData *KickoffAbstractModel::mimeData(const QModelIndexList &indexes) const +{ + KUrl::List urls; + QByteArray itemData; + + foreach(const QModelIndex &index, indexes) { + KUrl itemUrl = KUrl(data(index, UrlRole).toString()); + if (itemUrl.isValid()) { + urls << itemUrl; + } + } + + QMimeData *mimeData = new QMimeData(); + + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + + return mimeData; +} + +QStringList KickoffAbstractModel::mimeTypes() const +{ + QStringList types; + types << QLatin1String("text/uri-list"); + return types; +} + +Qt::DropActions KickoffAbstractModel::supportedDropActions() const +{ + return 0; +} + +Qt::DropActions KickoffAbstractModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + +#include "kickoffabstractmodel.moc" + diff --git a/applets/kickoff/core/kickoffabstractmodel.h b/applets/kickoff/core/kickoffabstractmodel.h new file mode 100644 index 0000000000..be5ce219a6 --- /dev/null +++ b/applets/kickoff/core/kickoffabstractmodel.h @@ -0,0 +1,53 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KICKOFFABSTRACTMODEL_H +#define KICKOFFABSTRACTMODEL_H + +// Qt +#include + +// KDE + +namespace Kickoff +{ + +/** + * Base model for Kickoff models based on QAbstractItemModel, enables drag and drop support + */ +class KickoffAbstractModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + /** Construct a new KickoffModel with the specified parent. */ + KickoffAbstractModel(QObject *parent = 0); + virtual ~KickoffAbstractModel(); + + //Reimplementations + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; + virtual Qt::DropActions supportedDragActions() const; +}; + +} + +#endif // KICKOFFABSTRACTMODEL_H diff --git a/applets/kickoff/core/kickoffmodel.cpp b/applets/kickoff/core/kickoffmodel.cpp new file mode 100644 index 0000000000..86a9b4db52 --- /dev/null +++ b/applets/kickoff/core/kickoffmodel.cpp @@ -0,0 +1,99 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "kickoffmodel.h" + +// Qt +#include + +// KDE +#include +#include + +// Local +#include "models.h" + +using namespace Kickoff; + +KickoffModel::KickoffModel(QObject *parent) + : QStandardItemModel(parent) +{ + QHash roles; + roles[Qt::DisplayRole] = "display"; + roles[Qt::DecorationRole] = "decoration"; + roles[Kickoff::SubTitleRole] = "subtitle"; + roles[Kickoff::UrlRole] = "url"; + roles[GroupNameRole] = "group"; + setRoleNames(roles); +} + +KickoffModel::~KickoffModel() +{} + +Qt::ItemFlags KickoffModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QStandardItemModel::flags(index); + + if (index.isValid()) { + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + } else { + return 0; + } +} + +QMimeData *KickoffModel::mimeData(const QModelIndexList &indexes) const +{ + KUrl::List urls; + + foreach (const QModelIndex &index, indexes) { + KUrl itemUrl = KUrl(data(index, UrlRole).toString()); + if (itemUrl.isValid()) { + urls << itemUrl; + } + } + + QMimeData *mimeData = new QMimeData(); + + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + + return mimeData; +} + +QStringList KickoffModel::mimeTypes() const +{ + QStringList types; + types << QLatin1String("text/uri-list"); + return types; +} + +Qt::DropActions KickoffModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +Qt::DropActions KickoffModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + +#include "kickoffmodel.moc" + diff --git a/applets/kickoff/core/kickoffmodel.h b/applets/kickoff/core/kickoffmodel.h new file mode 100644 index 0000000000..dd91f9be6d --- /dev/null +++ b/applets/kickoff/core/kickoffmodel.h @@ -0,0 +1,55 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KICKOFFMODEL_H +#define KICKOFFMODEL_H + +// Qt +#include + +// KDE + +namespace Kickoff +{ + +/** + * Base model for Kickoff models based on QStandardItemModel, enables drag and drop support + */ +class KickoffModel : public QStandardItemModel +{ + Q_OBJECT + +public: + /** Construct a new KickoffModel with the specified parent. */ + KickoffModel(QObject *parent = 0); + virtual ~KickoffModel(); + + //Reimplementations + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; + virtual Qt::DropActions supportedDragActions() const; + +private: +}; + +} + +#endif // KICKOFFMODEL_H diff --git a/applets/kickoff/core/kickoffplugin.cpp b/applets/kickoff/core/kickoffplugin.cpp new file mode 100644 index 0000000000..69945e994e --- /dev/null +++ b/applets/kickoff/core/kickoffplugin.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "kickoffplugin.h" +#include "applicationmodel.h" +#include "favoritesmodel.h" +#include "krunnermodel.h" +#include "leavemodel.h" +#include "recentlyusedmodel.h" +#include "systemmodel.h" +#include "kuserproxy.h" +#include "urlitemlauncher.h" + +#include + +void KickoffPlugin::registerTypes (const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.kickoff")); + qmlRegisterType(uri, 0, 1, "ApplicationModel"); + qmlRegisterType(uri, 0, 1, "FavoritesModel"); + qmlRegisterType(uri, 0, 1, "KRunnerModel"); + qmlRegisterType(uri, 0, 1, "LeaveModel"); + qmlRegisterType(uri, 0, 1, "RecentlyUsedModel"); + qmlRegisterType(uri, 0, 1, "SystemModel"); + qmlRegisterType(uri, 0, 1, "Launcher"); + qmlRegisterType(uri, 0, 1, "KUser"); +} + +#include "kickoffplugin.moc" diff --git a/applets/kickoff/core/kickoffplugin.h b/applets/kickoff/core/kickoffplugin.h new file mode 100644 index 0000000000..de839b3931 --- /dev/null +++ b/applets/kickoff/core/kickoffplugin.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef KICKOFFPLUGIN_H +#define KICKOFFPLUGIN_H + +#include +#include + + +class KickoffPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") +public: + virtual void registerTypes(const char *uri); +}; + +#endif // KICKOFFPLUGIN_H diff --git a/applets/kickoff/core/kickoffproxymodel.cpp b/applets/kickoff/core/kickoffproxymodel.cpp new file mode 100644 index 0000000000..4976bda9a8 --- /dev/null +++ b/applets/kickoff/core/kickoffproxymodel.cpp @@ -0,0 +1,92 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "kickoffproxymodel.h" + +// Qt +#include + +// KDE +#include +#include + +// Local +#include "models.h" + +using namespace Kickoff; + +KickoffProxyModel::KickoffProxyModel(QObject *parent) + : QAbstractProxyModel(parent) +{} + +KickoffProxyModel::~KickoffProxyModel() +{} + +Qt::ItemFlags KickoffProxyModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractProxyModel::flags(index); + + if (index.isValid()) { + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + } else { + return 0; + } +} + +QMimeData *KickoffProxyModel::mimeData(const QModelIndexList &indexes) const +{ + KUrl::List urls; + QByteArray itemData; + + foreach(const QModelIndex &index, indexes) { + KUrl itemUrl = KUrl(data(index, UrlRole).toString()); + if (itemUrl.isValid()) { + urls << itemUrl; + } + } + + QMimeData *mimeData = new QMimeData(); + + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + + return mimeData; +} + +QStringList KickoffProxyModel::mimeTypes() const +{ + QStringList types; + types << QLatin1String("text/uri-list"); + return types; +} + +Qt::DropActions KickoffProxyModel::supportedDropActions() const +{ + return 0; +} + +Qt::DropActions KickoffProxyModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + +#include "kickoffproxymodel.moc" + diff --git a/applets/kickoff/core/kickoffproxymodel.h b/applets/kickoff/core/kickoffproxymodel.h new file mode 100644 index 0000000000..f1af64041e --- /dev/null +++ b/applets/kickoff/core/kickoffproxymodel.h @@ -0,0 +1,53 @@ +/* + Copyright 2008 Marco Martin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KICKOFFPROXYMODEL_H +#define KICKOFFPROXYMODEL_H + +// Qt +#include + +// KDE + +namespace Kickoff +{ + +/** + * Base model for Kickoff models based on QAbstractProxyModel, enables drag and drop support + */ +class KickoffProxyModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + /** Construct a new KickoffModel with the specified parent. */ + KickoffProxyModel(QObject *parent = 0); + virtual ~KickoffProxyModel(); + + //Reimplementations + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; + virtual Qt::DropActions supportedDragActions() const; +}; + +} + +#endif // KICKOFFPROXYMODEL_H diff --git a/applets/kickoff/core/krunnermodel.cpp b/applets/kickoff/core/krunnermodel.cpp new file mode 100644 index 0000000000..db2adab23c --- /dev/null +++ b/applets/kickoff/core/krunnermodel.cpp @@ -0,0 +1,289 @@ +/* + Copyright 2009 Ivan Cukic + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "krunnermodel.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +//#include +//#include +#include +#include + + +// Local +#include "recentapplications.h" + +#define DELAY_TIME 50 + +using namespace Kickoff; + +Plasma::RunnerManager * _runnerManager = NULL; +Plasma::RunnerManager * runnerManager() { + if (_runnerManager == NULL) { + KConfigGroup conf = + componentData().config()->group("KRunner"); + + conf.writeEntry("loadAll", false); + _runnerManager = new Plasma::RunnerManager(conf); + + // initializing allowed runners + QStringList allowed; + allowed + << "places" + << "shell" + << "services" + << "bookmarks" + << "recentdocuments" + << "locations" + << "baloosearch"; + + _runnerManager->setAllowedRunners(allowed); + + conf.sync(); + + // runner: "konquerorsessions" "Konqueror Sessions" + // runner: "desktopsessions" "Desktop Sessions" + // runner: "places" "Places" + // runner: "windows" "Windows" + // runner: "plasma-desktop" "Plasma Desktop Shell" + // runner: "services" "Applications" + // runner: "PowerDevil" "PowerDevil" + // runner: "browserhistory" "Web Browser History" + // runner: "shell" "Command Line" + // runner: "katesessions" "Kate Sessions" + // runner: "locations" "Locations" + // runner: "webshortcuts" "Web Shortcuts" + // runner: "kabccontacts" "Contacts" + // runner: "konsolesessions" "Konsole Sessions" + // runner: "recentdocuments" "Recent Documents" + // runner: "calculator" "Calculator" + // runner: "amarok" "Amarok" + // runner: "wikipedia" "Wikipedia" + // runner: "kopete" "Kopete Contact Runner" + // runner: "bookmarks" "Bookmarks" + // runner: "unitconverter" "Unit Converter" + } + return _runnerManager; +} + +KService::Ptr Kickoff::serviceForUrl(const KUrl & url) +{ + QString runner = url.host(); + QString id = url.path(); + + if (id.startsWith(QLatin1Char('/'))) { + id = id.remove(0, 1); + } + + if (runner != QLatin1String("services")) { + return KService::Ptr(NULL); + } + + // URL path example: services_kde4-kate.desktop + // or: services_firefox.desktop + id.remove("services_"); + + return KService::serviceByStorageId(id); +} + + +class ResultItem { +public: + ResultItem() + { + } + + ResultItem(QIcon _icon, QString _name, QString _description, QVariant _data) + : icon(_icon), + name(_name), + description(_description), + data(_data) + { + } + + QIcon icon; + QString name, description; + QVariant data; +}; + +bool KRunnerItemHandler::openUrl(const KUrl& url) +{ + QString runner = url.host(); + QString id = url.path(); + if (id.startsWith(QLatin1Char('/'))) { + id = id.remove(0, 1); + } + + // Since krunner:// urls can't be added to recent applications, + // we find the local .desktop entry. + + KService::Ptr service = serviceForUrl(url); + if (service) { + RecentApplications::self()->add(service); + } else { + qWarning() << "Failed to find service for" << url; + } + + runnerManager()->run(id); + return true; +} + +class KRunnerModel::Private { +public: + Private() + { + } + + ~Private() + { + } + + QBasicTimer searchDelay; + QString searchQuery; + DisplayOrder displayOrder; +}; + +KRunnerModel::KRunnerModel(QObject *parent) + : KickoffModel(parent) + , d(new Private()) +{ + connect(runnerManager(), + SIGNAL(matchesChanged(QList)), + this, + SLOT(matchesChanged(QList))); + + UrlItemLauncher::addGlobalHandler(UrlItemLauncher::ProtocolHandler, "krunner", new KRunnerItemHandler); +} + +KRunnerModel::~KRunnerModel() +{ + delete d; +} + +void KRunnerModel::setQuery(const QString& query) +{ + runnerManager()->reset(); + clear(); + + d->searchQuery = query.trimmed(); + + if (d->searchQuery.isEmpty()) { + return; + } + + d->searchDelay.start(DELAY_TIME, this); +} + +void KRunnerModel::timerEvent(QTimerEvent * event) +{ + KickoffModel::timerEvent(event); + + if (event->timerId() == d->searchDelay.timerId()) { + d->searchDelay.stop(); + runnerManager()->launchQuery(d->searchQuery); + }; +} + +void KRunnerModel::setNameDisplayOrder(DisplayOrder displayOrder) +{ + d->displayOrder = displayOrder; +} + +DisplayOrder KRunnerModel::nameDisplayOrder() const +{ + return d->displayOrder; +} + +void KRunnerModel::matchesChanged(const QList< Plasma::QueryMatch > & m) +{ + QList< Plasma::QueryMatch > matches = m; + + qSort(matches.begin(), matches.end()); + + clear(); + + while (matches.size()) { + Plasma::QueryMatch match = matches.takeLast(); + qDebug() << "matches " << QString("krunner://") + match.runner()->id() + "/" + match.id(); + appendRow( + StandardItemFactory::createItem( + match.icon(), + match.text(), + match.subtext(), + QString("krunner://") + match.runner()->id() + "/" + match.id() + ) + ); + } +} + +Qt::ItemFlags KRunnerModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = KickoffModel::flags(index); + + if (index.isValid()) { + KUrl url = data(index, UrlRole).toString(); + QString host = url.host(); + if (host != "services") { + flags &= ~ ( Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled ); + } + } else { + flags = 0; + } + + return flags; +} + +QMimeData * KRunnerModel::mimeData(const QModelIndexList &indexes) const +{ + KUrl::List urls; + + foreach (const QModelIndex & index, indexes) { + KUrl url = data(index, UrlRole).toString(); + + KService::Ptr service = serviceForUrl(url); + + if (service) { + urls << KUrl(service->entryPath()); + } + } + + QMimeData *mimeData = new QMimeData(); + + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + + return mimeData; + +} + +#include "krunnermodel.moc" diff --git a/applets/kickoff/core/krunnermodel.h b/applets/kickoff/core/krunnermodel.h new file mode 100644 index 0000000000..3916829b1a --- /dev/null +++ b/applets/kickoff/core/krunnermodel.h @@ -0,0 +1,76 @@ +/* + Copyright 2009 Ivan Cukic + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRUNNERMODEL_H +#define KRUNNERMODEL_H + +#include "models.h" +#include "kickoffmodel.h" +#include "urlitemlauncher.h" + +namespace Plasma { + class QueryMatch; +} + +namespace Kickoff { + + KService::Ptr serviceForUrl(const KUrl & url); + +class KRunnerItemHandler : public UrlItemHandler { +public: + virtual bool openUrl(const KUrl& url); +}; + +class KRunnerModel : public KickoffModel +{ + Q_OBJECT + +public: + KRunnerModel(QObject *parent = NULL); + virtual ~KRunnerModel(); + void setNameDisplayOrder(DisplayOrder displayOrder); + DisplayOrder nameDisplayOrder() const; + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + // virtual QStringList mimeTypes() const; + // virtual Qt::DropActions supportedDropActions() const; + // virtual Qt::DropActions supportedDragActions() const; + +private: + void timerEvent(QTimerEvent * event); + +public Q_SLOTS: + void setQuery(const QString& query); + +private Q_SLOTS: + void matchesChanged(const QList< Plasma::QueryMatch > & matches); + +Q_SIGNALS: + void resultsAvailable(); + +private: + class Private; + Private * const d; +}; + +} + +#endif // KRUNNERMODEL_H + diff --git a/applets/kickoff/core/kuserproxy.cpp b/applets/kickoff/core/kuserproxy.cpp new file mode 100644 index 0000000000..50b56fa1f4 --- /dev/null +++ b/applets/kickoff/core/kuserproxy.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright 2013 Marco Martin * + * Copyright 2014 Sebastian Kugler * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include + +#include "kuserproxy.h" +#include +#include +#include + +#include + +#include + +KUserProxy::KUserProxy (QObject *parent) + : QObject(parent) +{ +} + +KUserProxy::~KUserProxy() +{ +} + +QString KUserProxy::fullName() const +{ + return m_user.property(KUser::FullName).toString(); +} + +QString KUserProxy::loginName() const +{ + return m_user.loginName(); +} + +QString KUserProxy::faceIconPath() const +{ + const QString u = m_user.faceIconPath(); + const QFile f(u); + if (f.exists(u)) { + // We need to return a file URL, not a simple path + return QUrl::fromLocalFile(u).toString(); + } + return QString(); +} + +QString KUserProxy::os() +{ + if (m_os.isEmpty()) { + m_os = i18n("Plasma by KDE"); + QFile osfile("/etc/os-release"); + if (osfile.exists()) { + if (!osfile.open(QIODevice::ReadOnly | QIODevice::Text)) { + return QString(); + } + + QTextStream in(&osfile); + while(!in.atEnd()) { + QString line = in.readLine(); + if (line.startsWith("PRETTY_NAME")) { + QStringList fields = line.split("PRETTY_NAME=\""); + if (fields.count() == 2) { + osfile.close(); + QString pretty = fields.at(1); + pretty.chop(1); + m_os = pretty; + return pretty; + } + } + } + osfile.close(); + } + } + return m_os; +} + +QString KUserProxy::host() const +{ + char hostname[256]; + hostname[0] = '\0'; + if (!gethostname(hostname, sizeof(hostname))) { + hostname[sizeof(hostname)-1] = '\0'; + } + return QString(hostname); +} + diff --git a/applets/kickoff/core/kuserproxy.h b/applets/kickoff/core/kuserproxy.h new file mode 100644 index 0000000000..ec77217d39 --- /dev/null +++ b/applets/kickoff/core/kuserproxy.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright 2014 Sebastian Kügler * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef KUSERPROXY_H +#define KUSERPROXY_H + +#include + +#include + +class KUserProxy : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString fullName READ fullName CONSTANT) + Q_PROPERTY(QString loginName READ loginName CONSTANT) + Q_PROPERTY(QString faceIconPath READ faceIconPath CONSTANT) + Q_PROPERTY(QString os READ os CONSTANT) + Q_PROPERTY(QString host READ host CONSTANT) + +public: + KUserProxy(QObject *parent = 0); + ~KUserProxy(); + + QString fullName() const; + QString loginName() const; + QString faceIconPath() const; + QString os(); + QString host() const; + +private: + KUser m_user; + QString m_os; +}; + +#endif //KUSERPROXY_H + diff --git a/applets/kickoff/core/leavemodel.cpp b/applets/kickoff/core/leavemodel.cpp new file mode 100644 index 0000000000..6a21f43022 --- /dev/null +++ b/applets/kickoff/core/leavemodel.cpp @@ -0,0 +1,212 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "leavemodel.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include + +// Local +#include "models.h" +#include "itemhandlers.h" + +using namespace Kickoff; + +class LeaveModel::Private +{ +}; + +QStandardItem* LeaveModel::createStandardItem(const QString& url) +{ + //Q_ASSERT(KUrl(url).scheme() == "leave"); + QStandardItem *item = new QStandardItem(); + const QString basename = QFileInfo(url).baseName(); + if (basename == "logoutonly") { + item->setText(i18n("Log out")); + item->setIcon(KIcon("system-log-out")); + item->setData(i18n("End session"), Kickoff::SubTitleRole); + } else if (basename == "lock") { + item->setText(i18n("Lock")); + item->setIcon(KIcon("system-lock-screen")); + item->setData(i18n("Lock screen"), Kickoff::SubTitleRole); + } else if (basename == "switch") { + item->setText(i18n("Switch user")); + item->setIcon(KIcon("system-switch-user")); + item->setData(i18n("Start a parallel session as a different user"), Kickoff::SubTitleRole); + } else if (basename == "shutdown") { + item->setText(i18n("Shut down")); + item->setIcon(KIcon("system-shutdown")); + item->setData(i18n("Turn off computer"), Kickoff::SubTitleRole); + } else if (basename == "restart") { + item->setText(i18nc("Restart computer", "Restart")); + item->setIcon(KIcon("system-reboot")); + item->setData(i18n("Restart computer"), Kickoff::SubTitleRole); + } else if (basename == "savesession") { + item->setText(i18n("Save Session")); + item->setIcon(KIcon("document-save")); + item->setData(i18n("Save current session for next login"), Kickoff::SubTitleRole); + } else if (basename == "standby") { + item->setText(i18nc("Puts the system on standby", "Standby")); + item->setIcon(KIcon("system-suspend")); + item->setData(i18n("Pause without logging out"), Kickoff::SubTitleRole); + } else if (basename == "suspenddisk") { + item->setText(i18n("Hibernate")); + item->setIcon(KIcon("system-suspend-hibernate")); + item->setData(i18n("Suspend to disk"), Kickoff::SubTitleRole); + } else if (basename == "suspendram") { + item->setText(i18n("Sleep")); + item->setIcon(KIcon("system-suspend")); + item->setData(i18n("Suspend to RAM"), Kickoff::SubTitleRole); + } else { + item->setText(basename); + item->setData(url, Kickoff::SubTitleRole); + } + item->setData(url, Kickoff::UrlRole); + return item; +} + +LeaveModel::LeaveModel(QObject *parent) + : QStandardItemModel(parent) + , d(0) +{ + QHash roles; + roles[Qt::DisplayRole] = "display"; + roles[Qt::DecorationRole] = "decoration"; + roles[Kickoff::SubTitleRole] = "subtitle"; + roles[Kickoff::UrlRole] = "url"; + roles[GroupNameRole] = "group"; + setRoleNames(roles); + updateModel(); + Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ProtocolHandler, "leave", new Kickoff::LeaveItemHandler); +} + +QVariant LeaveModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || section != 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return i18n("Leave"); + break; + default: + return QVariant(); + } +} + +void LeaveModel::updateModel() +{ + clear(); + + // Session Options + const QString session = i18n("Session"); + + // Logout + const bool canLogout = KAuthorized::authorizeKAction("logout") && KAuthorized::authorize("logout"); + if (canLogout) { + QStandardItem *logoutOption = createStandardItem("leave:/logoutonly"); + logoutOption->setData(session, Kickoff::GroupNameRole); + appendRow(logoutOption); + } + + // Lock + if (KAuthorized::authorizeKAction("lock_screen")) { + QStandardItem *lockOption = createStandardItem("leave:/lock"); + lockOption->setData(session, Kickoff::GroupNameRole); + appendRow(lockOption); + } + + // Save Session + if (canLogout) { + KConfigGroup c(KSharedConfig::openConfig("ksmserverrc", KConfig::NoGlobals), "General"); + if (c.readEntry("loginMode") == "restoreSavedSession") { + QStandardItem *saveSessionOption = createStandardItem("leave:/savesession"); + saveSessionOption->setData(session, Kickoff::GroupNameRole); + appendRow(saveSessionOption); + } + } + + // Switch User + if (KDisplayManager().isSwitchable() && KAuthorized::authorize(QLatin1String("switch_user"))) { + QStandardItem *switchUserOption = createStandardItem("leave:/switch"); + switchUserOption->setData(session, Kickoff::GroupNameRole); + appendRow(switchUserOption); + } + + // System Options + +//FIXME: the proper fix is to implement the KWorkSpace methods for Windows +#ifndef Q_WS_WIN + const QString system = i18n("System"); + QSet< Solid::PowerManagement::SleepState > spdMethods = Solid::PowerManagement::supportedSleepStates(); + if (spdMethods.contains(Solid::PowerManagement::StandbyState)) { + QStandardItem *standbyOption = createStandardItem("leave:/standby"); + standbyOption->setData(system, Kickoff::GroupNameRole); + appendRow(standbyOption); + } + + if (spdMethods.contains(Solid::PowerManagement::SuspendState)) { + QStandardItem *suspendramOption = createStandardItem("leave:/suspendram"); + suspendramOption->setData(system, Kickoff::GroupNameRole); + appendRow(suspendramOption); + } + + if (spdMethods.contains(Solid::PowerManagement::HibernateState)) { + QStandardItem *suspenddiskOption = createStandardItem("leave:/suspenddisk"); + suspenddiskOption->setData(system, Kickoff::GroupNameRole); + appendRow(suspenddiskOption); + } + + if (canLogout) { + if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeReboot)) { + // Restart + QStandardItem *restartOption = createStandardItem("leave:/restart"); + restartOption->setData(system, Kickoff::GroupNameRole); + appendRow(restartOption); + } + + if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeHalt)) { + // Shutdown + QStandardItem *shutDownOption = createStandardItem("leave:/shutdown"); + shutDownOption->setData(system, Kickoff::GroupNameRole); + appendRow(shutDownOption); + } + } +#endif +} + +LeaveModel::~LeaveModel() +{ + delete d; +} + +#include "leavemodel.moc" + diff --git a/applets/kickoff/core/leavemodel.h b/applets/kickoff/core/leavemodel.h new file mode 100644 index 0000000000..ed933173b6 --- /dev/null +++ b/applets/kickoff/core/leavemodel.h @@ -0,0 +1,48 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef LEAVEMODEL_H +#define LEAVEMODEL_H + +#include + +namespace Kickoff +{ + +class LeaveModel : public QStandardItemModel +{ + Q_OBJECT + +public: + LeaveModel(QObject *parent = NULL); + ~LeaveModel(); + + static QStandardItem* createStandardItem(const QString& url); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void updateModel(); + +private: + class Private; + Private * const d; +}; + +} + +#endif // LEAVEMODEL_H diff --git a/applets/kickoff/core/models.cpp b/applets/kickoff/core/models.cpp new file mode 100644 index 0000000000..b9f435c892 --- /dev/null +++ b/applets/kickoff/core/models.cpp @@ -0,0 +1,214 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "models.h" +#include "leavemodel.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kickoff; + +namespace Kickoff +{ + +Q_GLOBAL_STATIC_WITH_ARGS(KUrl, homeUrl, (QDir::homePath())) +Q_GLOBAL_STATIC_WITH_ARGS(KUrl, remoteUrl, ("remote:/")) +K_GLOBAL_STATIC(StandardItemFactoryData, factoryData) + +StandardItemFactoryData* deviceFactoryData() +{ + return factoryData; +} +} // namespace Kickoff + +QStandardItem *StandardItemFactory::createItemForUrl(const QString& urlString, DisplayOrder displayOrder) +{ + KUrl url(urlString); + + QStandardItem *item = 0; + + // Match files ending with ".desktop" and being local or having a relative + // path. For instance applications that still installs .desktop files at + // /usr/share/applnk, like KVirc 3 + if (urlString.endsWith(QLatin1String(".desktop")) && (url.isLocalFile() || url.isRelative())) { + // .desktop files may be services (type field == 'Application' or 'Service') + // or they may be other types such as links. + // + // first look in the KDE service database to see if this file is a service, + // otherwise represent it as a generic .desktop file + KService::Ptr service = KService::serviceByDesktopPath(url.toLocalFile()); + if (service) { + return createItemForService(service, displayOrder); + } + + item = new QStandardItem; + KDesktopFile desktopFile(url.toLocalFile()); + item->setText(QFileInfo(urlString.mid(0, urlString.lastIndexOf('.'))).completeBaseName()); + item->setIcon(KIcon(desktopFile.readIcon())); + + //FIXME: desktopUrl is a hack around borkage in KRecentDocuments which + // stores a path in the URL field! + KUrl desktopUrl(desktopFile.desktopGroup().readPathEntry("URL", QString())); + if (!desktopUrl.url().isEmpty()) { + item->setData(desktopUrl.url(), Kickoff::UrlRole); + } else { + // desktopUrl.url() is empty if the file doesn't exist so set the + // url role to that which was passed so that the item can still be + // manually removed + item->setData(urlString, Kickoff::UrlRole); + } + + QString subTitle = desktopUrl.isLocalFile() ? desktopUrl.toLocalFile() : desktopUrl.prettyUrl(); + item->setData(subTitle, Kickoff::SubTitleRole); + + setSpecialUrlProperties(desktopUrl, item); + } else if (url.scheme() == "leave") { + item = LeaveModel::createStandardItem(urlString); + } else { + item = new QStandardItem; + const QString subTitle = url.isLocalFile() ? url.toLocalFile() : url.prettyUrl(); + QString basename = QFileInfo(url.prettyUrl()).completeBaseName(); + if (basename.isNull()) { + basename = subTitle; + } + + item->setText(basename); + //FIXME +// item->setIcon(KIcon(KMimeType::iconNameForUrl(url))); + item->setData(url.url(), Kickoff::UrlRole); + item->setData(subTitle, Kickoff::SubTitleRole); + + setSpecialUrlProperties(url, item); + } + + return item; +} + +void StandardItemFactory::setSpecialUrlProperties(const KUrl& url, QStandardItem *item) +{ + // specially handled URLs + if (homeUrl() && url == *homeUrl()) { + item->setText(i18n("Home Folder")); + item->setIcon(KIcon("user-home")); + } else if (remoteUrl() && url == *remoteUrl()) { + item->setText(i18n("Network Folders")); + } +} + +QStandardItem *StandardItemFactory::createItem(const QIcon & icon, const QString & title, + const QString & description, const QString & url) +{ + QStandardItem *appItem = new QStandardItem; + + appItem->setText(title); + appItem->setIcon(icon); + appItem->setData(description, Kickoff::SubTitleRole); + appItem->setData(url, Kickoff::UrlRole); + + return appItem; +} + +QStandardItem *StandardItemFactory::createItemForService(KService::Ptr service, DisplayOrder displayOrder) +{ + QStandardItem *appItem = new QStandardItem; + + QString genericName = service->genericName(); + QString appName = service->name(); + bool nameFirst = displayOrder == NameBeforeDescription; + appItem->setText(nameFirst || genericName.isEmpty() ? appName : genericName); + appItem->setIcon(KIcon(service->icon())); + appItem->setData(service->entryPath(), Kickoff::UrlRole); + + if (nameFirst) { + if (!genericName.isEmpty()) { + appItem->setData(genericName, Kickoff::SubTitleRole); + } + } else if (!genericName.isEmpty()) { + // we only set the subtitle to appname if the generic name is empty because if + // the generic name IS empty, then the app name is used as the title role + // and we don't want it repeated twice. + appItem->setData(appName, Kickoff::SubTitleRole); + } + + return appItem; +} + +bool Kickoff::isLaterVersion(KService::Ptr first , KService::Ptr second) +{ + // a very crude heuristic using the .desktop path names + // which only understands kde3 vs kde4 + bool firstIsKde4 = first->entryPath().contains("kde4"); + bool secondIsKde4 = second->entryPath().contains("kde4"); + + return firstIsKde4 && !secondIsKde4; +} + +QStringList Kickoff::systemApplicationList() +{ + KConfigGroup appsGroup = componentData().config()->group("SystemApplications"); + QStringList apps; + apps << "systemsettings"; + apps = appsGroup.readEntry("DesktopFiles", apps); + return apps; +} + +#if 0 +void Kickoff::swapModelIndexes(QModelIndex& first, QModelIndex& second) +{ + Q_ASSERT(first.isValid()); + Q_ASSERT(second.isValid()); + + QAbstractItemModel *firstModel = const_cast(first.model()); + QAbstractItemModel *secondModel = const_cast(second.model()); + + Q_ASSERT(firstModel && secondModel); + + QMap firstIndexData = firstModel->itemData(first); + QMap secondIndexData = secondModel->itemData(second); + + firstModel->setItemData(first, secondIndexData); + secondModel->setItemData(second, firstIndexData); +} +#endif + +K_GLOBAL_STATIC_WITH_ARGS(KComponentData, kickoffComponent, ("kickoff", QByteArray(), KComponentData::SkipMainComponentRegistration)) +KComponentData Kickoff::componentData() +{ + return *kickoffComponent; +} + + + diff --git a/applets/kickoff/core/models.h b/applets/kickoff/core/models.h new file mode 100644 index 0000000000..05fe321611 --- /dev/null +++ b/applets/kickoff/core/models.h @@ -0,0 +1,140 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MODELS_H +#define MODELS_H + +// Qt + +// KDE +#include +#include + +class QStandardItem; +class KUrl; +class QModelIndex; + +namespace Solid +{ +class Device; +} + +namespace Kickoff +{ +class StandardItemFactoryData +{ +public: + QHash deviceByUrl; +}; + +StandardItemFactoryData* deviceFactoryData(); + +/** + * List of applications contained in the DesktopFiles key in the + * SystemApplications group, used to populate the System model + * @return list of desktop entry names + */ +QStringList systemApplicationList(); + +/** + * Additional data roles for data which the Kickoff models supply with their items + * for use when rendering the items and launching them. + */ +enum DataRole { + /** A sub title to be displayed below the text from the item's Qt::DisplayRole data */ + SubTitleRole = Qt::UserRole + 1, + FirstDataRole = SubTitleRole, + /** The URL to be opened when executing the item. */ + UrlRole = Qt::UserRole + 2, + /** The Solid device identifier for items which represent devices. */ + DeviceUdiRole = Qt::UserRole + 3, + /** The amount of space (in Kilobytes) used for items which represent storage. */ + DiskUsedSpaceRole = Qt::UserRole + 4, + /** The amount of free space (in Kilobytes) for items which represent storage. */ + DiskFreeSpaceRole = Qt::UserRole + 5, + SubTitleMandatoryRole = Qt::UserRole + 6, + /** Is item a separator. **/ + SeparatorRole = Qt::UserRole + 7, + /** relative path of the item */ + RelPathRole = Qt::UserRole + 8, + IconNameRole = Qt::UserRole + 9, + GroupNameRole = IconNameRole, + LastDataRole = Qt::UserRole + 10 +}; + +/** + * This enum describes the policy for displaying + * Name of Application - Description + * Description - Name of Application + */ +enum DisplayOrder { + NameAfterDescription, + NameBeforeDescription +}; + +/** + * Factory for creating QStandardItems with appropriate text, icons, URL + * and other Kickoff-specific information for a given URL or Service. + */ +class StandardItemFactory +{ +public: + static QStandardItem *createItemForUrl(const QString& url, DisplayOrder displayOrder); + static QStandardItem *createItemForService(KService::Ptr service, + DisplayOrder displayOrder); + static QStandardItem *createItem(const QIcon & icon, const QString & title, + const QString & description, const QString & url); + +private: + static void setSpecialUrlProperties(const KUrl& url, QStandardItem *item); +}; + +/** + * Abstract base class for delegates which provide information about a model + * item's state in a particular view. + */ +class ItemStateProvider +{ +public: + virtual ~ItemStateProvider() {} + + /** + * Returns true if a @p index should be drawn in the view or + * false if it should be hidden. + */ + virtual bool isVisible(const QModelIndex& index) const = 0; +}; + +// returns true if 'first' represents a more recent version of +// an application than 'second' +// +// eg. isLaterVersion(myapp_kde4,myapp_kde3) returns true +bool isLaterVersion(KService::Ptr first , KService::Ptr second); + +#if 0 +/** Swaps the data for two indexes in a QAbstractItemModel */ +void swapModelIndexes(QModelIndex& first, QModelIndex& second); +#endif + +// returns the Kickoff component data, this is mainly used +// to access the Kickoff shared config data +KComponentData componentData(); +} + +#endif //MODELS_H diff --git a/applets/kickoff/core/org.kde.kickoff.recent.xml b/applets/kickoff/core/org.kde.kickoff.recent.xml new file mode 100644 index 0000000000..d013f62279 --- /dev/null +++ b/applets/kickoff/core/org.kde.kickoff.recent.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/applets/kickoff/core/org.kde.kickoff.xml b/applets/kickoff/core/org.kde.kickoff.xml new file mode 100644 index 0000000000..b0f5d097ab --- /dev/null +++ b/applets/kickoff/core/org.kde.kickoff.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/applets/kickoff/core/qmldir b/applets/kickoff/core/qmldir new file mode 100644 index 0000000000..b95a24e990 --- /dev/null +++ b/applets/kickoff/core/qmldir @@ -0,0 +1 @@ +plugin kickoffplugin diff --git a/applets/kickoff/core/recentapplications.cpp b/applets/kickoff/core/recentapplications.cpp new file mode 100644 index 0000000000..910868de60 --- /dev/null +++ b/applets/kickoff/core/recentapplications.cpp @@ -0,0 +1,195 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "recentapplications.h" + +// Qt +#include +#include + +// KDE +#include +#include + +#include + +// Local +#include "models.h" + +using namespace Kickoff; + +class RecentApplications::Private +{ +public: + class ServiceInfo; + + Private() : defaultMaxServices(DEFAULT_MAX_SERVICES) { + KConfigGroup recentGroup = componentData().config()->group("RecentlyUsed"); + QList recentApplications = recentGroup.readEntry("Applications", QList()); + defaultMaxServices = maxServices = qMax(0, recentGroup.readEntry("MaxApplications", defaultMaxServices)); + + // TESTING + // the actual last date/time is not currently recorded, instead we just use + // the current date/time and adjust it by one second after each item is added + // to preserve the order of the applications in the list loaded from the KConfig + // source + QDateTime dateTime = QDateTime::currentDateTime(); + foreach(const QString& application, recentApplications) { + ServiceInfo info; + info.storageId = application; + info.startCount = 1; + info.lastStartedTime = dateTime; + addEntry(info.storageId, info); + dateTime = dateTime.addSecs(1); + } + }; + ~Private() { + KConfigGroup recentGroup = componentData().config()->group("RecentlyUsed"); + + QList services = serviceInfo.values(); + qSort(services.begin(), services.end()); + + // TESTING + // only the desktop file used is currently recorded, information such + // as start count and date/time of last used is lost + QList recentApplications; + foreach(const ServiceInfo& info, services) { + recentApplications << info.storageId; + } + + recentGroup.writeEntry("Applications", recentApplications); + recentGroup.config()->sync(); + } + void addEntry(const QString& id, ServiceInfo& info) { + // if this service is already in the list then remove the existing + // queue entry (so that there are no duplicates in the queue) + if (serviceInfo.contains(id)) { + qDebug() << "Duplicate entry added. Removing existing entry from queue."; + serviceQueue.erase(serviceInfo[id].queueIter); + } + + serviceQueue.append(id); + info.queueIter = --serviceQueue.end(); + serviceInfo.insert(id, info); + } + + void removeExpiredEntries() { + // if more than the maximum number of services have been added + // remove the least recently used service + while (serviceQueue.count() > maxServices) { + QString removeId = serviceQueue.takeFirst(); + qDebug() << "More than the maximal " << maxServices << " services added. Removing" << removeId << "from queue."; + serviceInfo.remove(removeId); + emit instance.applicationRemoved(KService::serviceByStorageId(removeId)); + } + } + + class ServiceInfo + { + public: + ServiceInfo() : startCount(0) {} + + QString storageId; + int startCount; + QDateTime lastStartedTime; + QLinkedList::iterator queueIter; + + bool operator<(const ServiceInfo& rhs) const { + return this->lastStartedTime < rhs.lastStartedTime; + } + }; + + static const int DEFAULT_MAX_SERVICES = 5; + int defaultMaxServices, maxServices; + // queue to keep track of the order in which services have been used + // (most recently used at the back) + QLinkedList serviceQueue; + QHash serviceInfo; + RecentApplications instance; +}; +K_GLOBAL_STATIC(RecentApplications::Private, privateSelf) + +RecentApplications *RecentApplications::self() +{ + return &privateSelf->instance; +} + +RecentApplications::RecentApplications() +{ +} + +QList RecentApplications::recentApplications() const +{ + QList services = privateSelf->serviceInfo.values(); + qSort(services.begin(), services.end(), qGreater()); + + QList servicePtrs; + foreach(const Private::ServiceInfo& info, services) { + KService::Ptr s = KService::serviceByStorageId(info.storageId); + + if (s) { + servicePtrs << s; + } + } + return servicePtrs; +} +int RecentApplications::startCount(KService::Ptr service) const +{ + return privateSelf->serviceInfo[service->storageId()].startCount; +} +QDateTime RecentApplications::lastStartedTime(KService::Ptr service) const +{ + return privateSelf->serviceInfo[service->storageId()].lastStartedTime; +} +void RecentApplications::setMaximum(int maximum) +{ + Q_ASSERT(maximum >= 0); + privateSelf->maxServices = maximum; + privateSelf->removeExpiredEntries(); +} +int RecentApplications::maximum() const +{ + return privateSelf->maxServices; +} +int RecentApplications::defaultMaximum() const +{ + return privateSelf->defaultMaxServices; +} +void RecentApplications::add(KService::Ptr service) +{ + Private::ServiceInfo info = privateSelf->serviceInfo.value(service->storageId()); + info.storageId = service->storageId(); + info.startCount++; + info.lastStartedTime = QDateTime::currentDateTime(); + + privateSelf->addEntry(info.storageId, info); + + qDebug() << "Recent app added" << info.storageId << info.startCount; + emit applicationAdded(service, info.startCount); + + privateSelf->removeExpiredEntries(); +} +void RecentApplications::clear() +{ + privateSelf->serviceInfo.clear(); + emit cleared(); +} + +#include "recentapplications.moc" diff --git a/applets/kickoff/core/recentapplications.h b/applets/kickoff/core/recentapplications.h new file mode 100644 index 0000000000..b73d5265c5 --- /dev/null +++ b/applets/kickoff/core/recentapplications.h @@ -0,0 +1,91 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef RECENTAPPLICATIONS_H +#define RECENTAPPLICATIONS_H + +// Qt +#include +#include + +// KDE +#include + +namespace Kickoff +{ + +/** + * Singleton class which can be used to keep track of recently started applications + * in the Kickoff launcher. + * + * RecentApplications information persists between sessions. + */ +class RecentApplications : public QObject +{ + Q_OBJECT +public: + class Private; + friend class Private; + + static RecentApplications *self(); + + /** + * List of service pointers for recently started applications in the order in which + * they were started, with the most recently used application first. + */ + QList recentApplications() const; + /** Returns the number of times an application represented by @p service has been started. */ + int startCount(KService::Ptr service) const; + /** Returns the last time and date with the application represented by @p service was started. */ + QDateTime lastStartedTime(KService::Ptr service) const; + + /** Sets the maximum number of recently used applications to remember. */ + void setMaximum(int max); + /** Returns the maximum number of recently used applications that are remembered. */ + int maximum() const; + /** Returns the default maximum number of recently used applications that are remembered as defined + either in the configfile as "MaxApplications" or via the DEFAULT_MAX_SERVICES macro. */ + int defaultMaximum() const; + +public Q_SLOTS: + /** + * Registers the startup of an application. This should be called whenever a new application + * or service is started. + */ + void add(KService::Ptr service); + /** Clear the list of recent applications. */ + void clear(); + +Q_SIGNALS: + /** Emitted after add() has been called. */ + void applicationAdded(KService::Ptr service, int startCount); + /** Emitted after remove() has been called. */ + void applicationRemoved(KService::Ptr service); + /** Emitted after clear() has been called. */ + void cleared(); + +private: + RecentApplications(); +}; + +} + +#endif // RECENTAPPLICATIONS_H + + diff --git a/applets/kickoff/core/recentlyusedmodel.cpp b/applets/kickoff/core/recentlyusedmodel.cpp new file mode 100644 index 0000000000..eb7abeb285 --- /dev/null +++ b/applets/kickoff/core/recentlyusedmodel.cpp @@ -0,0 +1,282 @@ +/* + Copyright 2007 Robert Knight + Copyright 2011 Martin Gräßlin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "recentlyusedmodel.h" + +// Qt + +// KDE +#include +#include +#include +#include +#include +#include + +// Local +#include "recentapplications.h" +#include "recentadaptor.h" + +using namespace Kickoff; + +class RecentlyUsedModel::Private +{ +public: + Private(RecentlyUsedModel *parent, RecentType recenttype, int maxRecentApps) + : q(parent), + recenttype(recenttype), + maxRecentApps(maxRecentApps >= 0 ? maxRecentApps : Kickoff::RecentApplications::self()->defaultMaximum()), + displayOrder(NameAfterDescription), + applicationTitle(i18n("Applications")), + documentTitle(i18n("Documents")), + recentApplicationCount(0) + { + } + + bool removeExistingItem(const QString& path) { + if (!itemsByPath.contains(path)) { + return false; + } + + QStandardItem *existingItem = itemsByPath[path]; + qDebug() << "Removing existing item" << existingItem; + QModelIndex index = q->indexFromItem(existingItem); + if (!index.isValid()) { + qDebug() << "Now Index for our existing item"; + return false; + } + q->takeRow(index.row()); + itemsByPath.remove(path); + delete existingItem; + return true; + } + + void addRecentApplication(KService::Ptr service, bool append) { + // remove existing item if any + if (removeExistingItem(service->entryPath())) { + --recentApplicationCount; + } + + QStandardItem *appItem = StandardItemFactory::createItemForService(service, displayOrder); + appItem->setData(applicationTitle, Kickoff::GroupNameRole); + itemsByPath.insert(service->entryPath(), appItem); + + if (append) { + q->insertRow(recentApplicationCount, appItem); + } else { + q->insertRow(0, appItem); + } + ++recentApplicationCount; + + while (recentApplicationCount > maxRecentApps) { + --recentApplicationCount; + QList row = q->takeRow(recentApplicationCount); + + //don't leave pending stuff in itemsByPath + if (!row.isEmpty()) { + itemsByPath.remove(row.first()->data(UrlRole).toString()); + qDeleteAll(row.begin(), row.end()); + } + } + } + + void addRecentDocument(const QString& desktopPath, bool append) { + // remove existing item if any + KDesktopFile desktopFile(desktopPath); + KUrl documentUrl = desktopFile.readUrl(); + + removeExistingItem(documentUrl.url()); + + QStandardItem *documentItem = StandardItemFactory::createItemForUrl(desktopPath, displayOrder); + documentItem->setData(true, Kickoff::SubTitleMandatoryRole); + documentItem->setData(documentTitle, Kickoff::GroupNameRole); + itemsByPath.insert(desktopPath, documentItem); + + //qDebug() << "Document item" << documentItem << "text" << documentItem->text() << "url" << documentUrl.url(); + if (append) { + q->appendRow(documentItem); + } else { + q->insertRow(0, documentItem); + } + } + + void loadRecentDocuments() + { + // create branch for documents and add existing items + const QStringList documents = KRecentDocument::recentDocuments(); + foreach(const QString& document, documents) { + addRecentDocument(document, true); + } + } + + void loadRecentApplications() + { + const QList services = RecentApplications::self()->recentApplications(); + for(int i = 0; i < maxRecentApps && i < services.count(); ++i) { + addRecentApplication(services[i], true); + } + } + + RecentlyUsedModel * const q; + RecentType recenttype; + int maxRecentApps; + + QHash itemsByPath; + DisplayOrder displayOrder; + QString applicationTitle; + QString documentTitle; + int recentApplicationCount; +}; + +RecentlyUsedModel::RecentlyUsedModel(QObject *parent, RecentType recenttype, int maxRecentApps) + : KickoffModel(parent), + d(new Private(this, recenttype, maxRecentApps)) +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + (void)new RecentAdaptor(this); + QDBusConnection::sessionBus().registerObject("/kickoff/RecentAppDoc", this); + dbus.connect(QString(), "/kickoff/RecentAppDoc", "org.kde.plasma", "clearRecentDocumentsAndApplications", this, SLOT(clearRecentDocumentsAndApplications())); + + if (recenttype != DocumentsOnly) { + d->loadRecentApplications(); + + // listen for changes to the list of recent applications + connect(RecentApplications::self(), SIGNAL(applicationAdded(KService::Ptr,int)), + this, SLOT(recentApplicationAdded(KService::Ptr,int))); + connect(RecentApplications::self(), SIGNAL(applicationRemoved(KService::Ptr)), + this, SLOT(recentApplicationRemoved(KService::Ptr))); + connect(RecentApplications::self(), SIGNAL(cleared()), + this, SLOT(recentApplicationsCleared())); + } + + if (recenttype != ApplicationsOnly) { + d->loadRecentDocuments(); + + // listen for changes to the list of recent documents + KDirWatch *recentDocWatch = new KDirWatch(this); + recentDocWatch->addDir(KRecentDocument::recentDocumentDirectory(), KDirWatch::WatchFiles); + connect(recentDocWatch, SIGNAL(created(QString)), this, SLOT(recentDocumentAdded(QString))); + connect(recentDocWatch, SIGNAL(deleted(QString)), this, SLOT(recentDocumentRemoved(QString))); + } +} + +RecentlyUsedModel::~RecentlyUsedModel() +{ + delete d; +} + +QVariant RecentlyUsedModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || section != 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + if (d->recenttype == DocumentsAndApplications) { + return i18n("Recently Used"); + } else if (d->recenttype == DocumentsOnly) { + return i18n("Recently Used Documents"); + } else if (d->recenttype == ApplicationsOnly) { + return i18n("Recently Used Applications"); + } + default: + return QVariant(); + } +} + +void RecentlyUsedModel::setNameDisplayOrder(DisplayOrder displayOrder) +{ + if (d->displayOrder == displayOrder) { + return; + } + + d->displayOrder = displayOrder; + + d->itemsByPath.clear(); + clear(); + + if (d->recenttype != DocumentsOnly) { + d->loadRecentApplications(); + } + + if (d->recenttype != ApplicationsOnly) { + d->loadRecentDocuments(); + } +} + +DisplayOrder RecentlyUsedModel::nameDisplayOrder() const +{ + return d->displayOrder; +} + +void RecentlyUsedModel::recentDocumentAdded(const QString& path) +{ + qDebug() << "Recent document added" << path; + d->addRecentDocument(path, false); +} +void RecentlyUsedModel::recentDocumentRemoved(const QString& path) +{ + qDebug() << "Recent document removed" << path; + d->removeExistingItem(path); +} + +void RecentlyUsedModel::recentApplicationAdded(KService::Ptr service, int) +{ + if (service) { + d->addRecentApplication(service, false); + } +} + +void RecentlyUsedModel::recentApplicationRemoved(KService::Ptr service) +{ + if (service) { + d->removeExistingItem(service->entryPath()); + } +} + +void RecentlyUsedModel::recentApplicationsCleared() +{ + while (d->recentApplicationCount != 0) { + delete takeItem(0); + takeRow(0); + --d->recentApplicationCount; + } +} + +void RecentlyUsedModel::clearRecentApplications() +{ + RecentApplications::self()->clear(); +} +void RecentlyUsedModel::clearRecentDocuments() +{ + KRecentDocument::clear(); +} + +void RecentlyUsedModel::clearRecentDocumentsAndApplications() +{ + clearRecentDocuments(); + clearRecentApplications(); +} + + +#include "recentlyusedmodel.moc" + diff --git a/applets/kickoff/core/recentlyusedmodel.h b/applets/kickoff/core/recentlyusedmodel.h new file mode 100644 index 0000000000..44bcc9f091 --- /dev/null +++ b/applets/kickoff/core/recentlyusedmodel.h @@ -0,0 +1,78 @@ +/* + Copyright 2007 Robert Knight + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef RECENTLYUSEDMODEL_H +#define RECENTLYUSEDMODEL_H + +#include "kickoffmodel.h" +#include "models.h" + +// Qt + +// KDE +#include + +namespace Kickoff +{ + +/** + * Model for the Recently Used view which provides a tree of recently used + * applications and documents. + */ +class RecentlyUsedModel : public KickoffModel +{ + Q_OBJECT + +public: + + /** Defines what the model should display. */ + enum RecentType { + DocumentsAndApplications, ///< display recently used documents and applications. + DocumentsOnly, ///< documents only. recently used applications will not be displayed. + ApplicationsOnly ///< applications only, recently used documents will not be displayed. + }; + + /** Construct a new RecentlyUsedModel with the specified parent. */ + explicit RecentlyUsedModel(QObject *parent = 0, RecentType recenttype = DocumentsAndApplications, int maxRecentApps = -1); + virtual ~RecentlyUsedModel(); + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + void setNameDisplayOrder(DisplayOrder displayOrder); + DisplayOrder nameDisplayOrder() const; + +public Q_SLOTS: + void clearRecentApplications(); + void clearRecentDocuments(); + void clearRecentDocumentsAndApplications(); + +private Q_SLOTS: + void recentDocumentAdded(const QString& path); + void recentDocumentRemoved(const QString& path); + void recentApplicationAdded(KService::Ptr, int startCount); + void recentApplicationRemoved(KService::Ptr); + void recentApplicationsCleared(); + +private: + class Private; + Private * const d; +}; + +} + +#endif // RECENTLYUSEDMODEL_H diff --git a/applets/kickoff/core/systemmodel.cpp b/applets/kickoff/core/systemmodel.cpp new file mode 100644 index 0000000000..32caa0abae --- /dev/null +++ b/applets/kickoff/core/systemmodel.cpp @@ -0,0 +1,489 @@ +/* + Copyright 2007 Robert Knight + Copyright 2007 Kevin Ottens + Copyright 2011 Martin Gräßlin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "systemmodel.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local +#include "models.h" +#include "systemmodel.h" + +using namespace Kickoff; + +static const int OFFSET = 100; + +class SystemModel::Private +{ +public: + Private(SystemModel *parent) + : q(parent), + placesModel(new KFilePlacesModel(parent)), + refreshRequested(false) + { + q->setSourceModel(placesModel); + + connect(placesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + q, SLOT(sourceDataChanged(QModelIndex,QModelIndex))); + connect(placesModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + q, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int))); + connect(placesModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, SLOT(sourceRowsInserted(QModelIndex,int,int))); + connect(placesModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int))); + connect(placesModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(sourceRowsRemoved(QModelIndex,int,int))); + + connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), q, SLOT(reloadApplications())); + } + + SystemModel * const q; + KFilePlacesModel *placesModel; + KService::List appsList; + QMap usageByMountpoint; + QWeakPointer usageFinder; + bool refreshRequested; +}; + +SystemModel::SystemModel(QObject *parent) + : KickoffProxyModel(parent) + , d(new Private(this)) +{ + qRegisterMetaType("UsageInfo"); + QHash roles; + roles[Qt::DisplayRole] = "display"; + roles[Qt::DecorationRole] = "decoration"; + roles[Kickoff::SubTitleRole] = "subtitle"; + roles[Kickoff::UrlRole] = "url"; + roles[Kickoff::GroupNameRole] = "group"; + setRoleNames(roles); +} + +SystemModel::~SystemModel() +{ + delete d; +} + +QModelIndex SystemModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) { + return QModelIndex(); + } + + if (d->placesModel->isHidden(sourceIndex)) { + // hidden items not in our model + return QModelIndex(); + } + if (d->placesModel->isDevice(sourceIndex)) { + if (d->placesModel->data(sourceIndex, KFilePlacesModel::FixedDeviceRole).toBool()) { + // fixed devices not in our list + return QModelIndex(); + } + } + int count = d->appsList.size() -1; + if (KAuthorized::authorize("run_command")) { + ++count; + } + for (int i=0; iplacesModel->rowCount(); ++i) { + if (i == sourceIndex.row()) { + break; + } + const QModelIndex index = d->placesModel->index(i, 0); + if (d->placesModel->isHidden(index)) { + continue; + } + if (d->placesModel->isDevice(index)) { + if (!d->placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool()) { + ++count; + } + } else { + // visible place + ++count; + } + } + return index(count, 0); +} + +QModelIndex SystemModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) { + return QModelIndex(); + } + if (proxyIndex.internalId() == 0) { + // application index + return QModelIndex(); + } + return d->placesModel->index(proxyIndex.internalId() - OFFSET, 0); +} + +QModelIndex SystemModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent) + int count = d->appsList.size()-1; + if (KAuthorized::authorize("run_command")) { + ++count; + } + if (row <= count) { + return createIndex(row, column); + } + // find the source + for (int i=0; iplacesModel->rowCount(); ++i) { + const QModelIndex index = d->placesModel->index(i, 0); + if (d->placesModel->isHidden(index)) { + continue; + } + if (d->placesModel->isDevice(index)) { + if (!d->placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool()) { + ++count; + } + } else { + // visible place + ++count; + } + if (row == count) { + return createIndex(row, column, i + OFFSET); + } + } + return QModelIndex(); +} + +QModelIndex SystemModel::parent(const QModelIndex &item) const +{ + Q_UNUSED(item) + return QModelIndex(); +} + +int SystemModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + int count = d->appsList.size(); + if (KAuthorized::authorize("run_command")) { + ++count; + } + for (int i=0; iplacesModel->rowCount(); ++i) { + const QModelIndex index = d->placesModel->index(i, 0); + if (d->placesModel->isHidden(index)) { + continue; + } + if (d->placesModel->isDevice(index)) { + if (!d->placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool()) { + ++count; + } + } else { + // visible place + ++count; + } + } + + return count; +} + +int SystemModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; +} + +QVariant SystemModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (index.internalId() == 0) { + if (role == GroupNameRole) { + return i18n("Applications"); + } + if (d->appsList.count() < index.row()) { + return QVariant(); + } else if (d->appsList.count() == index.row()) { + // "Run Command" + switch (role) { + case Qt::DisplayRole: + return i18n("Run Command..."); + case Qt::DecorationRole: + return KIcon("system-run"); + case SubTitleRole: + return i18n("Run a command or a search query"); + case UrlRole: + return "run:/"; + default: + return QVariant(); + } + } + + KService::Ptr service = d->appsList[index.row()]; + + switch (role) { + case Qt::DisplayRole: + return service->name(); + case Qt::DecorationRole: + return KIcon(service->icon()); + case SubTitleRole: + return service->genericName(); + case UrlRole: + return service->entryPath(); + default: + return QVariant(); + } + } + + if (role == UrlRole) { + return d->placesModel->url(mapToSource(index)).url(); + } + if (role == SubTitleRole) { + QModelIndex sourceIndex = mapToSource(index); + + if (d->placesModel->isDevice(sourceIndex)) { + Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex); + Solid::StorageAccess *access = dev.as(); + + if (access) { + return access->filePath(); + } + } else { + KUrl url = d->placesModel->url(sourceIndex); + return url.isLocalFile() ? url.toLocalFile() : url.prettyUrl(); + } + + return QVariant(); + } + if (role == GroupNameRole) { + if (d->placesModel->isDevice(mapToSource(index))) { + return i18n("Removable Storage"); + } else { + return i18n("Places"); + } + } + return d->placesModel->data(mapToSource(index), role); +} + +QVariant SystemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || section != 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return i18n("Computer"); + break; + default: + return QVariant(); + } +} + +UsageFinder::UsageFinder(QObject *parent) + : QThread(parent) +{ + +} + +void UsageFinder::add(int index, const QString &mountPoint) +{ + //NOTE: this is not particularly perfect for a threaded object ;) + // but the assumption here is that add is called before run() + m_toCheck.append(qMakePair(index, mountPoint)); +} + +void UsageFinder::run() +{ + typedef QPair CheckPair; + foreach (CheckPair check, m_toCheck) { + KDiskFreeSpaceInfo freeSpace = KDiskFreeSpaceInfo::freeSpaceInfo(check.second); + if (freeSpace.isValid()) { + UsageInfo info; + info.used = freeSpace.used() / 1024; + info.available = freeSpace.available() / 1024; + emit usageInfo(check.first, check.second, info); + } + } +} + +bool SystemModel::hasChildren(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + // return always false, else the model states that items in appsList are having children + return false; +} + +void SystemModel::refreshUsageInfo() +{ + if (d->usageFinder) { + d->refreshRequested = true; + } else { + QTimer::singleShot(100, this, SLOT(startUsageInfoFetch())); + } +} + +void SystemModel::setUsageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo) +{ + QModelIndex sourceIndex = d->placesModel->index(index, 0); + if (sourceIndex.isValid()) { + d->usageByMountpoint[mountPoint] = usageInfo; + QModelIndex index = mapFromSource(sourceIndex); + emit dataChanged(index, index); + } +} + +void SystemModel::stopRefreshingUsageInfo() +{ + d->refreshRequested = false; +} + +void SystemModel::usageFinderFinished() +{ + if (d->refreshRequested) { + d->refreshRequested = false; + QTimer::singleShot(100, this, SLOT(startUsageInfoFetch())); + } +} + +void SystemModel::startUsageInfoFetch() +{ + if (d->usageFinder) { + return; + } + + UsageFinder *usageFinder = new UsageFinder(this); + d->usageFinder = usageFinder; + connect(usageFinder, SIGNAL(finished()), + this, SLOT(usageFinderFinished())); + connect(usageFinder, SIGNAL(finished()), + usageFinder, SLOT(deleteLater())); + connect(usageFinder, SIGNAL(usageInfo(int,QString,UsageInfo)), + this, SLOT(setUsageInfo(int,QString,UsageInfo))); + + bool hasDevices = false; + + for (int i = 0; i < d->placesModel->rowCount(); ++i) { + QModelIndex sourceIndex = d->placesModel->index(i, 0); + if (d->placesModel->isDevice(sourceIndex)) { + Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex); + Solid::StorageAccess *access = dev.as(); + + if (access && !access->filePath().isEmpty()) { + usageFinder->add(i, access->filePath()); + hasDevices = true; + } + } + } + + if (hasDevices) { + usageFinder->start(); + } else { + delete usageFinder; + } +} + +void SystemModel::reloadApplications() +{ + const QStringList apps = Kickoff::systemApplicationList(); + d->appsList.clear(); + + foreach (const QString &app, apps) { + KService::Ptr service = KService::serviceByStorageId(app); + + if (service) { + d->appsList << service; + } + } +} + +void Kickoff::SystemModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end) +{ + if (start.parent().isValid()) return; + + QModelIndex ourStart = QModelIndex(); + QModelIndex ourEnd = QModelIndex(); + // find our start + for (int i=start.row(); i<=end.row(); ++i) { + const QModelIndex ourIndex = mapFromSource(d->placesModel->index(i, 0)); + if (ourIndex.isValid()) { + ourStart = ourIndex; + break; + } + } + if (!ourStart.isValid()) { + return; + } + // find our end + for (int i=end.row(); i>=start.row(); --i) { + const QModelIndex ourIndex = mapFromSource(d->placesModel->index(i, 0)); + if (ourIndex.isValid()) { + ourEnd = ourIndex; + break; + } + } + if (ourEnd.isValid()) { + emit dataChanged(ourStart, ourEnd); + } +} + +void Kickoff::SystemModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + // TODO: can we update without resetting the model? + beginResetModel(); +} + +void Kickoff::SystemModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int /*end*/) +{ + Q_UNUSED(parent) + // TODO: can we update without resetting the model? + endResetModel(); +} + +void Kickoff::SystemModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + // TODO: can we update without resetting the model? + beginResetModel(); +} + +void Kickoff::SystemModel::sourceRowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/) +{ + Q_UNUSED(parent) + // TODO: can we update without resetting the model? + endResetModel(); +} + +#include "systemmodel.moc" diff --git a/applets/kickoff/core/systemmodel.h b/applets/kickoff/core/systemmodel.h new file mode 100644 index 0000000000..5da49ad371 --- /dev/null +++ b/applets/kickoff/core/systemmodel.h @@ -0,0 +1,103 @@ +/* + Copyright 2007 Robert Knight + Copyright 2007 Kevin Ottens + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SYSTEMMODEL_H +#define SYSTEMMODEL_H + +#include "kickoffproxymodel.h" + +#include + +namespace Kickoff +{ + +struct UsageInfo { + UsageInfo() + : used(0), + available(0) + {} + + quint64 used; + quint64 available; +}; + +class UsageFinder : public QThread +{ + Q_OBJECT + +public: + UsageFinder(QObject *parent); + void add(int index, const QString &mountPoint); + +Q_SIGNALS: + void usageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo); + +protected: + void run(); + +private: + QList > m_toCheck; +}; + +/** + * Model which provides a tree of items for important system setup tools (eg. System Settings) , + * folders (eg. the user's home folder and the local network) and fixed and removable storage. + */ +class SystemModel : public KickoffProxyModel +{ + Q_OBJECT + +public: + /** Constructs a new SystemModel with the specified parent. */ + SystemModel(QObject *parent = 0); + virtual ~SystemModel(); + + virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &item) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; + void refreshUsageInfo(); + void stopRefreshingUsageInfo(); + +private Q_SLOTS: + void startUsageInfoFetch(); + void reloadApplications(); + void sourceDataChanged(const QModelIndex &start, const QModelIndex &end); + void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + void setUsageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo); + void usageFinderFinished(); + +private: + class Private; + Private * const d; +}; + +} + +#endif // SYSTEMMODEL_H + diff --git a/applets/kickoff/core/urlitemlauncher.cpp b/applets/kickoff/core/urlitemlauncher.cpp new file mode 100644 index 0000000000..2ed074709c --- /dev/null +++ b/applets/kickoff/core/urlitemlauncher.cpp @@ -0,0 +1,164 @@ +/* + Copyright 2007 Robert Knight + Copyright 2007 Kevin Ottens + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "urlitemlauncher.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include + +// Local +#include "models.h" + +// DBus +#include "krunner_interface.h" + +using namespace Kickoff; + +class HandlerInfo +{ +public: + HandlerInfo() : type(UrlItemLauncher::ProtocolHandler) , handler(0) {} + UrlItemLauncher::HandlerType type; + UrlItemHandler *handler; +}; + +class GenericItemHandler : public UrlItemHandler +{ +public: + virtual bool openUrl(const KUrl& url) { + if (url.protocol() == "run" && KAuthorized::authorize("run_command")) { + QString interface("org.kde.krunner"); + org::kde::krunner::App krunner(interface, "/App", QDBusConnection::sessionBus()); + krunner.display(); + return true; + } + + new KRun(url, 0); + return true; + } +}; + +class UrlItemLauncher::Private +{ +public: + static QHash globalHandlers; + static GenericItemHandler genericHandler; + + static bool openUrl(const QString &urlString) { + qDebug() << "Opening item with URL" << urlString; + + KUrl url(urlString); + HandlerInfo protocolHandler = globalHandlers[url.scheme()]; + if (protocolHandler.type == ProtocolHandler && protocolHandler.handler != 0) { + return protocolHandler.handler->openUrl(url); + } + + QString extension = QFileInfo(url.path()).suffix(); + HandlerInfo extensionHandler = globalHandlers[extension]; + if (extensionHandler.type == ExtensionHandler && extensionHandler.handler != 0) { + return extensionHandler.handler->openUrl(url); + } + + return genericHandler.openUrl(url); + } +}; + +QHash UrlItemLauncher::Private::globalHandlers; +GenericItemHandler UrlItemLauncher::Private::genericHandler; + +UrlItemLauncher::UrlItemLauncher(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +UrlItemLauncher::~UrlItemLauncher() +{ + delete d; +} + +bool UrlItemLauncher::openItem(const QModelIndex& index) +{ + QString urlString = index.data(UrlRole).value(); + if (urlString.isEmpty()) { + QString udi = index.data(DeviceUdiRole).toString(); + if (!udi.isEmpty()) { + Solid::Device device(udi); + Solid::StorageAccess *access = device.as(); + + if (access && !access->isAccessible()) { + connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(onSetupDone(Solid::ErrorType,QVariant,QString))); + access->setup(); + return true; + } + } + + qDebug() << "Item" << index.data(Qt::DisplayRole) << "has no URL to open."; + return false; + } + + return Private::openUrl(urlString); +} + +bool UrlItemLauncher::openUrl(const QString& url) +{ + return Private::openUrl(url); +} + +void UrlItemLauncher::onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) +{ + Q_UNUSED(errorData); + + if (error != Solid::NoError) { + return; + } + + Solid::Device device(udi); + Solid::StorageAccess *access = device.as(); + + Q_ASSERT(access); + + QString urlString = "file://" + access->filePath(); + Private::openUrl(urlString); +} + +// FIXME: the handlers are leaked, as they are added with each new Kickoff instance, +// but never deleted. +void UrlItemLauncher::addGlobalHandler(HandlerType type, const QString& name, UrlItemHandler *handler) +{ + HandlerInfo info; + info.type = type; + info.handler = handler; + Private::globalHandlers.insert(name, info); +} + + +#include "urlitemlauncher.moc" diff --git a/applets/kickoff/core/urlitemlauncher.h b/applets/kickoff/core/urlitemlauncher.h new file mode 100644 index 0000000000..05c414d08e --- /dev/null +++ b/applets/kickoff/core/urlitemlauncher.h @@ -0,0 +1,87 @@ +/* + Copyright 2007 Robert Knight + Copyright 2007 Kevin Ottens + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef URLITEMLAUNCHER_H +#define URLITEMLAUNCHER_H + +#include +#include + +class QModelIndex; +class KUrl; + +namespace Kickoff +{ + +/** + * UrlItemHandler is an abstract base class for handlers which can open particular + * types of URL. + * + * @see UrlItemLauncher + */ +class UrlItemHandler +{ +public: + virtual ~UrlItemHandler() {} + virtual bool openUrl(const KUrl& url) = 0; +}; + +/** + * UrlItemLauncher provides facilities to open a item from a Kickoff model based on its UrlRole + * data. + * + * By default, a UrlItemLauncher opens all URLs using the KRun class. Additional handlers can be created + * to handle URLs with particular protocols or extensions differently. Handlers can be + * registered using the static addGlobalHandler() method. + */ +class UrlItemLauncher : public QObject +{ + Q_OBJECT + +public: + UrlItemLauncher(QObject *parent = 0); + virtual ~UrlItemLauncher(); + + enum HandlerType { + ProtocolHandler, + ExtensionHandler + }; + static void addGlobalHandler(HandlerType type, + const QString& name, + UrlItemHandler *handler); + +public Q_SLOTS: + /** Open the specified @p index from a Kickoff model. */ + bool openItem(const QModelIndex& index); + /** Open the specified @p url */ + bool openUrl(const QString& url); + +private Q_SLOTS: + void onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi); + +private: + class Private; + Private * const d; +}; + +} + +#endif // URLITEMLAUNCHER_H + diff --git a/applets/kickoff/package/contents/config/config.qml b/applets/kickoff/package/contents/config/config.qml new file mode 100644 index 0000000000..74ec528281 --- /dev/null +++ b/applets/kickoff/package/contents/config/config.qml @@ -0,0 +1,29 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: "Appearance" + icon: "preferences-desktop-color" + source: "configGeneral.qml" + } +} diff --git a/applets/kickoff/package/contents/config/main.xml b/applets/kickoff/package/contents/config/main.xml new file mode 100644 index 0000000000..3a859e84a1 --- /dev/null +++ b/applets/kickoff/package/contents/config/main.xml @@ -0,0 +1,20 @@ + + + + + + + true + + + false + + + false + + + + diff --git a/applets/kickoff/package/contents/ui/ApplicationsView.qml b/applets/kickoff/package/contents/ui/ApplicationsView.qml new file mode 100644 index 0000000000..156aee6e03 --- /dev/null +++ b/applets/kickoff/package/contents/ui/ApplicationsView.qml @@ -0,0 +1,217 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Gregor Taetzner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.kickoff 0.1 as Kickoff + +Item { + id: appViewContainer + + anchors.fill: parent + + objectName: "ApplicationsView" + + function decrementCurrentIndex() { + applicationsView.decrementCurrentIndex(); + } + + function incrementCurrentIndex() { + applicationsView.incrementCurrentIndex(); + } + + function activateCurrentIndex(start) { + if (!applicationsView.currentItem.modelChildren) + if (!start) + return; + + appViewScrollArea.state = "OutgoingLeft"; + } + + function deactivateCurrentIndex() { + if (crumbModel.count > 0) { // this is not the case when switching from the "Applications" to the "Favorites" tab using the "Left" key + breadcrumbsElement.children[crumbModel.count-1].clickCrumb(); + appViewScrollArea.state = "OutgoingRight"; + return true; + } + return false; + } + + ContextMenu { + id: contextMenu + } + + Item { + id: crumbContainer + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: childrenRect.height + + Flickable { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: breadcrumbsElement.height + + contentWidth: breadcrumbsElement.width + contentX: contentWidth - width + + PlasmaComponents.ButtonRow { + id: breadcrumbsElement + + exclusive: false + + Breadcrumb { + id: rootBreadcrumb + root: true + text: i18n("All Applications") + depth: 0 + } + Repeater { + model: ListModel { + id: crumbModel + // Array of the category indexes + property var indexes: [] + } + + Breadcrumb { + root: false + text: model.text + } + } + } + } // Flickable + } // crumbContainer + + PlasmaExtras.ScrollArea { + id: appViewScrollArea + + anchors { + top: crumbContainer.bottom + bottom: parent.bottom + rightMargin: -units.largeSpacing + leftMargin: -units.largeSpacing + } + + + width: parent.width + + function moveRight() { + state = ""; + applicationsView.currentItem.activate(); + } + + function moveLeft() { + state = ""; + // newModelIndex set by clicked breadcrumb + applicationsView.model.rootIndex = applicationsView.newModelIndex; + } + + ListView { + id: applicationsView + + property variant newModelIndex + + focus: true + keyNavigationWraps: true + boundsBehavior: Flickable.StopAtBounds + highlight: PlasmaComponents.Highlight {} + + model: VisualDataModel { + id: vmodel + + delegate: KickoffItem { + id: kickoffItem + + appView: true + + PlasmaCore.SvgItem { + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + rightMargin: y + } + height: units.iconSizes.small + width: height + + svg: arrowSvg + elementId: "go-next" + visible: hasModelChildren + } + } + + model: Kickoff.ApplicationModel {} + } // VisualDataModel + + function addBreadcrumb(categoryIndex, title) { + crumbModel.append({"text": title, "depth": crumbModel.count+1}) + crumbModel.indexes.push(categoryIndex); + } + + PlasmaCore.Svg { + id: arrowSvg + + imagePath: "toolbar-icons/go" + } + } // applicationsView + + states: [ + State { + name: "OutgoingLeft" + PropertyChanges { + target: appViewScrollArea + x: -parent.width + opacity: 0.0 + } + }, + State { + name: "OutgoingRight" + PropertyChanges { + target: appViewScrollArea + x: parent.width + opacity: 0.0 + } + } + ] + + transitions: [ + Transition { + to: "OutgoingLeft" + SequentialAnimation { + NumberAnimation { properties: "x,opacity"; easing.type: Easing.InQuad; duration: units.longDuration } + ScriptAction { script: appViewScrollArea.moveRight() } + } + }, + Transition { + to: "OutgoingRight" + SequentialAnimation { + NumberAnimation { properties: "x,opacity"; easing.type: Easing.InQuad; duration: units.longDuration } + ScriptAction { script: appViewScrollArea.moveLeft() } + } + } + ] + } // appViewScrollArea +} // appViewContainer diff --git a/applets/kickoff/package/contents/ui/BaseView.qml b/applets/kickoff/package/contents/ui/BaseView.qml new file mode 100644 index 0000000000..02c392ed00 --- /dev/null +++ b/applets/kickoff/package/contents/ui/BaseView.qml @@ -0,0 +1,65 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.draganddrop 2.0 + + +Item { + property alias model: kickoffListView.model + + function decrementCurrentIndex() { + kickoffListView.decrementCurrentIndex(); + } + + function incrementCurrentIndex() { + kickoffListView.incrementCurrentIndex(); + } + + function activateCurrentIndex() { + kickoffListView.currentItem.activate(); + } + + PlasmaExtras.ScrollArea { + anchors.fill: parent + + ListView { + id: kickoffListView + + anchors.fill: parent + + interactive: contentHeight > height + boundsBehavior: Flickable.StopAtBounds + currentIndex: -1 + keyNavigationWraps: true + highlight: PlasmaComponents.Highlight {} + + delegate: KickoffItem {} + + section { + property: "group" + criteria: ViewSection.FullString + delegate: SectionDelegate {} + } + } + } +} diff --git a/applets/kickoff/package/contents/ui/Breadcrumb.qml b/applets/kickoff/package/contents/ui/Breadcrumb.qml new file mode 100644 index 0000000000..96d99d2ad0 --- /dev/null +++ b/applets/kickoff/package/contents/ui/Breadcrumb.qml @@ -0,0 +1,74 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: crumbRoot + + height: crumb.implicitHeight + width: crumb.implicitWidth + arrowSvg.width + + property string text + property bool root: false + property int depth: model.depth + + function clickCrumb() { + crumb.clicked(); + } + + PlasmaComponents.ToolButton { + id: crumb + + anchors.left: arrowSvg.right + + text: crumbRoot.text + enabled: crumbRoot.depth < crumbModel.count + + onClicked: { + // Remove all the breadcrumbs in front of the clicked one + while (crumbModel.count > 0 && crumbRoot.depth < crumbModel.get(crumbModel.count-1).depth) { + crumbModel.remove(crumbModel.count-1) + crumbModel.indexes.pop() + } + + if (crumbRoot.root) { + applicationsView.newModelIndex = 0; + } else { + applicationsView.newModelIndex = crumbModel.indexes[index]; + } + appViewScrollArea.state = "OutgoingRight"; + } + } + + PlasmaCore.SvgItem { + id: arrowSvg + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + height: crumbRoot.height / 2 + width: visible ? height : 0 + + svg: PlasmaCore.Svg { + imagePath: "toolbar-icons/go" + } + elementId: "go-next" + visible: !crumbRoot.root + } +} // crumbRoot \ No newline at end of file diff --git a/applets/kickoff/package/contents/ui/ContextMenu.qml b/applets/kickoff/package/contents/ui/ContextMenu.qml new file mode 100644 index 0000000000..8101dd9477 --- /dev/null +++ b/applets/kickoff/package/contents/ui/ContextMenu.qml @@ -0,0 +1,85 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 + +PlasmaComponents.ContextMenu { + id: contextMenu + + property string title + property variant model + property bool isFavorite: favoritesModel.isFavorite(contextMenu.model.url) + property bool isApp: contextMenu.model.url.indexOf(".desktop") !== -1 + + function openAt(title, model, x, y) { + contextMenu.title = title + contextMenu.model = model + + open(x, y) + } + + /* + * context menu items + */ + PlasmaComponents.MenuItem { + id: titleMenuItem + + text: contextMenu.title + icon: contextMenu.model.decoration + checkable: false + enabled: false + } + PlasmaComponents.MenuItem { + id: titleSeparator + + separator: true + } + PlasmaComponents.MenuItem { + id: addToFavorites + + text: contextMenu.isFavorite ? i18n("Remove From Favorites") : i18n("Add To Favorites") + icon: contextMenu.isFavorite ? "list-remove" : "bookmark-new" + visible: contextMenu.isApp + + onClicked: { + // FIXME: apparently contextMenu.model is empty + if (contextMenu.isFavorite) { + favoritesModel.remove(contextMenu.model.url); + } else { + favoritesModel.add(contextMenu.model.url); + } + } + } + PlasmaComponents.MenuItem { + id: uninstallApp + + text: i18n("Uninstall") + visible: packagekitSource.data["Status"] !== undefined && contextMenu.isApp + + onClicked: { + var service = packagekitSource.serviceForSource("Status") + var operation = service.operationDescription("uninstallApplication") + operation.Url = contextMenu.model.url; + var job = service.startOperationCall(operation) + } + } +} // contextMenu + diff --git a/applets/kickoff/package/contents/ui/FavoritesView.qml b/applets/kickoff/package/contents/ui/FavoritesView.qml new file mode 100644 index 0000000000..15dd4175e6 --- /dev/null +++ b/applets/kickoff/package/contents/ui/FavoritesView.qml @@ -0,0 +1,169 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.draganddrop 2.0 + +import org.kde.plasma.private.kickoff 0.1 as Kickoff + + +Item { + anchors.fill: parent + + objectName: "FavoritesView" + + function decrementCurrentIndex() { + kickoffListView.decrementCurrentIndex(); + } + + function incrementCurrentIndex() { + kickoffListView.incrementCurrentIndex(); + } + + function activateCurrentIndex() { + kickoffListView.currentItem.activate(); + } + + ContextMenu { + id: contextMenu + + PlasmaComponents.MenuItem { + id: actionsSeparator + + separator: true + } + PlasmaComponents.MenuItem { + id: sortFavoritesAscending + + text: i18n("Sort Alphabetically (A to Z)") + icon: "view-sort-ascending" + + onClicked: { + favoritesModel.sortFavoritesAscending(); + } + } + PlasmaComponents.MenuItem { + id: sortFavoritesDescending + + text: i18n("Sort Alphabetically (Z to A)") + icon: "view-sort-descending" + + onClicked: { + favoritesModel.sortFavoritesDescending(); + } + } + } + DropArea { + property string dragUrl: "" + property Item dragItem: null + property int startRow: -1 + property int itemHeight: units.gridUnit * 3 + + + anchors.fill: scrollArea + + function syncTarget(event) { + kickoffListView.currentIndex = kickoffListView.indexAt(event.x, event.y + kickoffListView.contentY + itemHeight) + 1 + if (kickoffListView.currentIndex === -1) { + if (event.y < itemHeight/2) { + kickoffListView.currentIndex = 0 + } + } + if (kickoffListView.currentItem != null && event.y + itemHeight < kickoffListView.currentItem.y) { + dropTarget.y = (kickoffListView.currentIndex) * itemHeight - kickoffListView.contentY + } else { + dropTarget.y = (kickoffListView.count ) * itemHeight + } + } + + onDrop: { + var row = dropTarget.y / itemHeight; + print("Dropping into row : " + startRow + " " + row + " " + (event.y + kickoffListView.contentY) + " " + dragUrl); + row = Math.max(0, row) + row = Math.round(row) + //kickoffListView.model.dropMimeData(event.mimeData.text, [dragUrl], row, 0); + kickoffListView.model.move(startRow, row); + dropTarget.visible = false; + } + onDragEnter: { +// print("Drag enter"); + dragUrl = kickoffListView.currentItem.url; + startRow = kickoffListView.currentIndex; + syncTarget(event); +// print("Dragging " + dragUrl + " from row " + startRow); + dropTarget.visible = true; + } + onDragMove: syncTarget(event); + + onDragLeave: { + dropTarget.visible = false; + } + + Rectangle { + id: dropTarget + + width: parent.width + height: Math.max(2, units.smallSpacing) + + visible: false + color: theme.highlightColor + } + } + + PlasmaExtras.ScrollArea { + id: scrollArea + + anchors.fill: parent + + ListView { + id: kickoffListView + + anchors.fill: parent + currentIndex: -1 + keyNavigationWraps: true + interactive: contentHeight > height + /* + delegate: KQuickControlsAddons.MouseEventListener { + hoverEnabled: true + onContainsMouseChanged: { + if (containsMouse) { + kickoffListView.currentIndex = index; + } + } + KickoffItem { id: koitem; anchors.fill: parent; } + } + */ + delegate: KickoffItem {} + highlight: PlasmaComponents.Highlight {} + + model: favoritesModel + + section { + property: "group" + criteria: ViewSection.FullString + //delegate: SectionDelegate {} + } + } + } + +} diff --git a/applets/kickoff/package/contents/ui/Footer.qml b/applets/kickoff/package/contents/ui/Footer.qml new file mode 100644 index 0000000000..dcc762fd3c --- /dev/null +++ b/applets/kickoff/package/contents/ui/Footer.qml @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 Gregor Taetzner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.kquickcontrolsaddons 2.0 + +Item { + id: footer + + height: statusLabel.height + + PlasmaComponents.Label { + id: statusLabel + //text: kickoff.footerText // FIXME: where does this come from? Is it still needed? + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + } + } + + PlasmaCore.SvgItem { + id: brandingButton + svg { + PlasmaCore.Svg { + imagePath: "widgets/branding" + } + } + elementId: "brilliant" + height: Math.min(implicitHeight, parent.height) //don't be bigger than the text or the logo size + width: implicitWidth * (height / implicitHeight) + + anchors { + verticalCenter: parent.verticalCenter + left: statusLabel.right + leftMargin: 4 + } + } +} diff --git a/applets/kickoff/package/contents/ui/Header.qml b/applets/kickoff/package/contents/ui/Header.qml new file mode 100644 index 0000000000..595bd51734 --- /dev/null +++ b/applets/kickoff/package/contents/ui/Header.qml @@ -0,0 +1,225 @@ +/* + * Copyright 2014 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +import org.kde.plasma.private.kickoff 0.1 as Kickoff + +Item { + id: header + + height: units.gridUnit * 5 + + property alias query: queryField.text + property Item input: queryField + + Kickoff.KUser { + id: kuser + } + + state: (header.query != "") ? "query" : "hint" + + Timer { + id: labelTimer + interval: 5000 + running: header.state === "hint" && plasmoid.expanded && (header.query === "") + onTriggered: { + if (header.state == "info") { + header.state = "hint"; + } else if (header.state == "hint") { + header.state = "info"; + } + } + } + + Rectangle { color: "orange"; visible: root.debug; anchors.fill: parent; opacity: 0.3 } + + Image { + id: faceIcon + source: kuser.faceIconPath + visible: source != "" + + width: units.gridUnit * 3 + height: width + + anchors { + top: parent.top + left: parent.left + topMargin: units.gridUnit + leftMargin: units.gridUnit + } + + Rectangle { color: "green"; opacity: 0.3; anchors.fill: parent; visible: root.debug; } + } + + PlasmaCore.IconItem { + source: "user-identity" + visible: faceIcon.source == "" + width: units.gridUnit * 3 + height: width + anchors { + top: faceIcon.top + right: faceIcon.right + rightMargin: -units.gridUnit/2 + } + } + + PlasmaExtras.Heading { + id: nameLabel + + level: 2 + text: kuser.fullName + elide: Text.ElideRight + verticalAlignment: Text.AlignTop + height: paintedHeight + + anchors { + left: faceIcon.right + top: faceIcon.top + right: parent.right + leftMargin: units.gridUnit + rightMargin: units.gridUnit + } + } + + Item { + id: searchWidget + + property int animationDuration: units.longDuration + property real normalOpacity: .6 + property int yOffset: height / 2 + + height: infoLabel.height + anchors { + left: nameLabel.left + top: nameLabel.bottom + right: nameLabel.right + } + + PlasmaComponents.Label { + id: infoLabel + anchors { + left: parent.left + right: parent.right + } + verticalAlignment: Text.AlignBottom + text: kuser.os != "" ? i18n("%2@%3 (%1)", kuser.os, kuser.loginName, kuser.host) : i18n("%1@%2", kuser.loginName, kuser.host) + elide: Text.ElideRight + + Behavior on opacity { NumberAnimation { duration: searchWidget.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on y { NumberAnimation { duration: searchWidget.animationDuration; easing.type: Easing.InOutQuad; } } + } + + PlasmaComponents.Label { + id: hintLabel + anchors { + left: parent.left + right: parent.right + } + verticalAlignment: Text.AlignBottom + text: i18n("Type to search...") + Behavior on opacity { NumberAnimation { duration: searchWidget.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on y { NumberAnimation { duration: searchWidget.animationDuration; easing.type: Easing.InOutQuad; } } + } + + PlasmaComponents.TextField { + id: queryField + anchors.fill: parent + clearButtonShown: true + Behavior on opacity { NumberAnimation { duration: searchWidget.animationDuration / 4 } } + + onTextChanged: { + if (root.state != "Search") { + root.previousState = root.state; + root.state = "Search"; + } + if (text == "") { + root.state = root.previousState; + root.forceActiveFocus(); + header.state = "info"; + } else { + header.state = "query"; + } + } + } + + Connections { + target: plasmoid + onExpandedChanged: { + if (!expanded) { + header.query = ""; + } + } + } + } + + states: [ + State { + name: "info" + PropertyChanges { + target: infoLabel + opacity: searchWidget.normalOpacity + y: 0 + } + PropertyChanges { + target: hintLabel + opacity: 0 + y: searchWidget.yOffset + } + PropertyChanges { + target: queryField + opacity: 0 + } + }, + State { + name: "hint" + PropertyChanges { + target: infoLabel + y: -searchWidget.yOffset + opacity: 0 + } + PropertyChanges { + target: hintLabel + y: 0 + opacity: searchWidget.normalOpacity + } + PropertyChanges { + target: queryField + opacity: 0 + } + }, + State { + name: "query" + PropertyChanges { + target: infoLabel + opacity: 0 + } + PropertyChanges { + target: hintLabel + opacity: 0 + } + PropertyChanges { + target: queryField + opacity: 1 + } + } + ] // states +} diff --git a/applets/kickoff/package/contents/ui/Kickoff.qml b/applets/kickoff/package/contents/ui/Kickoff.qml new file mode 100644 index 0000000000..28db6b0b28 --- /dev/null +++ b/applets/kickoff/package/contents/ui/Kickoff.qml @@ -0,0 +1,404 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Gregor Taetzner + Copyright (C) 2012 Marco Martin + Copyright (C) 2013 David Edmundson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.plasmoid 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.kquickcontrolsaddons 2.0 + +Item { + + Plasmoid.switchWidth: units.gridUnit * 200 + Plasmoid.switchHeight: units.gridUnit * 300 + + + Plasmoid.fullRepresentation: Item { + id: root + Layout.minimumWidth: units.gridUnit * 26 + Layout.minimumHeight: units.gridUnit * 33 + + property string previousState + property bool switchTabsOnHover: plasmoid.configuration.switchTabsOnHover + property bool showAppsByName: plasmoid.configuration.showAppsByName + property Item currentView: mainStack.currentTab.decrementCurrentIndex ? mainStack.currentTab : mainStack.currentTab.item + + property int pad: units.gridUnit + property bool debug: false + +// implicitWidth: root.minimumWidth +// implicitHeight: root.minimumHeight + + state: "Normal" + focus: true + + PlasmaCore.DataSource { + id: packagekitSource + engine: "packagekit" + connectedSources: ["Status"] + } + + Kickoff.Launcher { + id: launcher + } + + PlasmaCore.Svg { + id: lineSvg + imagePath: "widgets/line" + } + + Timer { + id: clickTimer + + property Item pendingButton + + interval: 250 + + onTriggered: pendingButton.clicked() + } + + Header { + id: header + + anchors { + top: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return undefined; + default: + return parent.top; + } + } + bottom: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return parent.bottom; + default: + return undefined; + } + } + left: parent.left + right: parent.right + } + } + + PlasmaComponents.TabGroup { + id: mainStack + + anchors { + top: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + return tabBar.bottom; + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return parent.top; + default: + return header.bottom; + } + } + bottom: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return header.top; + default: + return tabBar.top; + } + } + left: plasmoid.location == PlasmaCore.Types.LeftEdge ? tabBar.right : parent.left + right: plasmoid.location == PlasmaCore.Types.RightEdge ? tabBar.left : parent.right + leftMargin: units.largeSpacing + rightMargin: units.largeSpacing + } + + //pages + FavoritesView { + id: favoritesPage + } + PlasmaExtras.ConditionalLoader { + id: applicationsPage + when: mainStack.currentTab == applicationsPage + source: Qt.resolvedUrl("ApplicationsView.qml") + } + PlasmaExtras.ConditionalLoader { + id: systemPage + when: mainStack.currentTab == systemPage + source: Qt.resolvedUrl("SystemView.qml") + } + PlasmaExtras.ConditionalLoader { + id: recentlyUsedPage + when: mainStack.currentTab == recentlyUsedPage + source: Qt.resolvedUrl("RecentlyUsedView.qml") + } + PlasmaExtras.ConditionalLoader { + id: leavePage + when: mainStack.currentTab == leavePage + source: Qt.resolvedUrl("LeaveView.qml") + } + PlasmaExtras.ConditionalLoader { + id: searchPage + when: root.state == "Search" + //when: mainStack.currentTab == searchPage || root.state == "Search" + source: Qt.resolvedUrl("SearchView.qml") + } + } // mainStack + + Kickoff.FavoritesModel { + id: favoritesModel + } + + PlasmaComponents.TabBar { + id: tabBar + + width: { + if (!visible) { + return 0; + } + + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return units.gridUnit * 5; + default: + return 0; + } + } + + height: { + if (!visible) { + return 0; + } + + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return 0; + default: + return units.gridUnit * 5; + } + } + + Behavior on width { NumberAnimation { duration: units.longDuration; easing.type: Easing.InQuad; } } + Behavior on height { NumberAnimation { duration: units.longDuration; easing.type: Easing.InQuad; } } + + anchors { + top: { + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return parent.top; + default: + return undefined; + } + } + bottom: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + return undefined; + case PlasmaCore.Types.LeftEdge: + case PlasmaCore.Types.RightEdge: + return header.top; + default: + return parent.bottom; + } + } + left: { + switch (plasmoid.location) { + case PlasmaCore.Types.RightEdge: + return undefined; + default: + return parent.left; + } + } + right: { + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + return undefined; + default: + return parent.right; + } + } + } + + tabPosition: { + switch (plasmoid.location) { + case PlasmaCore.Types.TopEdge: + return Qt.TopEdge; + case PlasmaCore.Types.LeftEdge: + return Qt.LeftEdge; + case PlasmaCore.Types.RightEdge: + return Qt.RightEdge; + default: + return Qt.BottomEdge; + } + } + + currentTab: bookmarkButton + onCurrentTabChanged: root.forceActiveFocus(); + + KickoffButton { + id: bookmarkButton + tab: favoritesPage + iconSource: "bookmarks" + text: i18n("Favorites") + } + KickoffButton { + id: applicationButton + tab: applicationsPage + iconSource: "applications-other" + text: i18n("Applications") + } + KickoffButton { + id: computerButton + tab: systemPage + iconSource: "computer" // TODO: could also be computer-laptop + text: i18n("Computer") + } + KickoffButton { + id: usedButton + tab: recentlyUsedPage + iconSource: "document-open-recent" + text: i18n("Recently Used") + } + KickoffButton { + id: leaveButton + tab: leavePage + iconSource: "system-shutdown" + text: i18n("Leave") + } + } // tabBar + + Keys.forwardTo: [tabBar.layout] + + Keys.onPressed: { + + if (mainStack.currentTab == applicationsPage) { + if (event.key != Qt.Key_Tab) { + root.state = "Applications"; + } + } + + switch(event.key) { + case Qt.Key_Up: { + currentView.decrementCurrentIndex(); + event.accepted = true; + break; + } + case Qt.Key_Down: { + currentView.incrementCurrentIndex(); + event.accepted = true; + break; + } + case Qt.Key_Left: { + if (header.input.focus) { + break; + } + if (!currentView.deactivateCurrentIndex()) { + // FIXME move to the previous tab immediately + root.state = "Normal" + } + event.accepted = true; + break; + } + case Qt.Key_Right: { + if (header.input.focus) { + break; + } + currentView.activateCurrentIndex(); + event.accepted = true; + break; + } + case Qt.Key_Tab: { + root.state == "Applications" ? root.state = "Normal" : root.state = "Applications"; + event.accepted = true; + break; + } + case Qt.Key_Enter: + case Qt.Key_Return: { + currentView.activateCurrentIndex(1); + event.accepted = true; + break; + } + case Qt.Key_Escape: { + if (header.query == "") { + plasmoid.expanded = false; + } else { + header.query = ""; + } + event.accepted = true; + break; + } + default: { // forward key to searchView + if (event.text != "" && !header.input.focus) { + if (event.text == "v" && event.modifiers & Qt.ControlModifier) { + header.input.paste(); + } else { + header.query = ""; + header.query += event.text; + } + header.input.forceActiveFocus(); + event.accepted = true; + } + } + } + } + + states: [ + State { + name: "Normal" + PropertyChanges { + target: root + Keys.forwardTo: [tabBar.layout] + } + }, + State { + name: "Applications" + PropertyChanges { + target: root + Keys.forwardTo: [root] + } + }, + State { + name: "Search" + PropertyChanges { + target: tabBar + visible: false + } + PropertyChanges { + target: mainStack + currentTab: searchPage + } + } + ] // states + + onStateChanged: { + print("root.state changed to : " + state); + } + } +} // root diff --git a/applets/kickoff/package/contents/ui/KickoffButton.qml b/applets/kickoff/package/contents/ui/KickoffButton.qml new file mode 100644 index 0000000000..57d1ba09b1 --- /dev/null +++ b/applets/kickoff/package/contents/ui/KickoffButton.qml @@ -0,0 +1,73 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.kquickcontrolsaddons 2.0 + +PlasmaComponents.TabButton { + id: button + + property string iconSource + property alias text: labelElement.text + + implicitHeight: iconElement.height + labelElement.implicitHeight + iconElement.anchors.topMargin + labelElement.anchors.topMargin + labelElement.anchors.bottomMargin + + MouseArea { + enabled: root.switchTabsOnHover + anchors.fill: parent + hoverEnabled: root.switchTabsOnHover + onEntered: { + clickTimer.pendingButton = button + clickTimer.restart() + } + onExited: clickTimer.running = false + onClicked: button.clicked() + } + + QIconItem { + id: iconElement + + anchors { + topMargin: 5 + top: parent.top + horizontalCenter: parent.horizontalCenter + } + width: units.iconSizes.medium + height: width + + icon: iconSource + } + PlasmaComponents.Label { + id: labelElement + + anchors { + bottom: parent.bottom + top: iconElement.bottom + left: parent.left + right: parent.right + topMargin: 5 + bottomMargin: 5 + } + + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + font.pointSize: theme.smallestFont.pointSize + } +} // button diff --git a/applets/kickoff/package/contents/ui/KickoffItem.qml b/applets/kickoff/package/contents/ui/KickoffItem.qml new file mode 100644 index 0000000000..0a3b01e261 --- /dev/null +++ b/applets/kickoff/package/contents/ui/KickoffItem.qml @@ -0,0 +1,171 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Gregor Taetzner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.draganddrop 2.0 + +Item { + id: listItem + + width: ListView.view.width + height: listItemDelegate.height // + listItemDelegate.anchors.margins*2 + + property bool dropEnabled: false + property bool modelChildren: hasModelChildren + property bool appView: false + property bool isCurrent: listItem.ListView.view.currentIndex === index; + property string url: model["url"] + + function activate() { + if (hasModelChildren) { + listItem.ListView.view.addBreadcrumb(listItem.ListView.view.model.modelIndex(index), display); + listItem.ListView.view.model.rootIndex = listItem.ListView.view.model.modelIndex(index); + } else { + launcher.openUrl(model["url"]); + plasmoid.expanded = false; + } + } + + Item { + id: listItemDelegate + + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + //margins: units.gridUnit / 2 + } + //height: Math.max(elementIcon.height + units.gridUnit/2, titleElement.height + subTitleElement.height + units.gridUnit/2) + height: units.gridUnit * 3 + + DragArea { + anchors.fill: parent + supportedActions: Qt.MoveAction | Qt.LinkAction + delegateImage: decoration + + mimeData { + url: "file://" + model["url"] + source: parent + text: index + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + //anchors.margins: -8 + + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onEntered: { + listItem.ListView.view.currentIndex = index; + } + + onClicked: { + if (mouse.button == Qt.LeftButton) { + if (appView) { + appViewScrollArea.state = "OutgoingLeft"; + } + else { + listItem.activate(); + } + } else if (mouse.button == Qt.RightButton) { + // don't show a context menu for container + if (hasModelChildren || typeof(contextMenu) === "undefined") { + return; + } + + contextMenu.visualParent = mouseArea + contextMenu.openAt(titleElement.text, model, mouse.x, mouse.y); + } + } + } + } + + QIconItem { + id: elementIcon + + anchors { + left: parent.left + leftMargin: (units.gridUnit * 3) - units.iconSizes.medium + verticalCenter: parent.verticalCenter + } + width: units.iconSizes.medium + height: width + + icon: decoration + } + PlasmaComponents.Label { + id: titleElement + + anchors { + top: elementIcon.top + left: elementIcon.right + right: parent.right + leftMargin: units.gridUnit + } + height: paintedHeight + // TODO: games should always show the by name! + text: { + if (hasModelChildren) { + return display; + } else { + plasmoid.configuration.showAppsByName || display.length == 0 ? (subtitle == undefined ? display : subtitle) : + display + } + } + } + PlasmaComponents.Label { + id: subTitleElement + + anchors { + left: titleElement.left + right: parent.right + bottom: parent.bottom + top: titleElement.bottom + } + height: paintedHeight + + text: { + if (hasModelChildren) { + return subtitle; + } else { + plasmoid.configuration.showAppsByName || subtitle == undefined ? (display.length != 0 ? display : subtitle) : subtitle; + } + } + opacity: isCurrent ? 0.6 : 0 + Behavior on opacity { NumberAnimation { duration: units.longDuration /2 } } + font.pointSize: theme.smallestFont.pointSize + elide: Text.ElideMiddle + } +/* + PlasmaComponents.Highlight { + id: itemHighlight + anchors { + fill: parent + margins: -(units.gridUnit/2) + } + opacity: mouseArea.containsMouse ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: theme.shortDuration } } + }*/ + } // listItemDelegate +} // listItem diff --git a/applets/kickoff/package/contents/ui/LeaveView.qml b/applets/kickoff/package/contents/ui/LeaveView.qml new file mode 100644 index 0000000000..97fe7f30d2 --- /dev/null +++ b/applets/kickoff/package/contents/ui/LeaveView.qml @@ -0,0 +1,27 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.private.kickoff 0.1 as Kickoff + + +BaseView { + objectName: "LeaveView" + + model: Kickoff.LeaveModel {} +} diff --git a/applets/kickoff/package/contents/ui/RecentlyUsedView.qml b/applets/kickoff/package/contents/ui/RecentlyUsedView.qml new file mode 100644 index 0000000000..ed30c93db0 --- /dev/null +++ b/applets/kickoff/package/contents/ui/RecentlyUsedView.qml @@ -0,0 +1,51 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.private.kickoff 0.1 as Kickoff +import org.kde.plasma.components 2.0 as PlasmaComponents + + +BaseView { + objectName: "RecentlyUsedView" + + model: Kickoff.RecentlyUsedModel { + id: recentlyUsedModel + } + + ContextMenu { + id: contextMenu + + PlasmaComponents.MenuItem { + id: clearRecentApplications + + text: i18n("Clear Recent Applications") + icon: QIcon("edit-clear-history") + onClicked: recentlyUsedModel.clearRecentApplications(); + visible: contextMenu.isApp + } + PlasmaComponents.MenuItem { + id: clearRecentDocuments + + text: i18n("Clear Recent Documents") + icon: QIcon("edit-clear-history") + onClicked: recentlyUsedModel.clearRecentDocuments(); + visible: !contextMenu.isApp + } + } +} diff --git a/applets/kickoff/package/contents/ui/SearchView.qml b/applets/kickoff/package/contents/ui/SearchView.qml new file mode 100644 index 0000000000..7401fa2eb6 --- /dev/null +++ b/applets/kickoff/package/contents/ui/SearchView.qml @@ -0,0 +1,69 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Gregor Taetzner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.kickoff 0.1 as Kickoff +Item { + id: searchViewContainer + + anchors.fill: parent + + objectName: "SearchView" + + function decrementCurrentIndex() { + searchView.decrementCurrentIndex(); + } + + function incrementCurrentIndex() { + searchView.incrementCurrentIndex(); + } + + function activateCurrentIndex() { + searchView.currentItem.activate(); + } + + PlasmaExtras.ScrollArea { + anchors.fill: parent + + ListView { + id: searchView + + anchors.fill: parent + keyNavigationWraps: true + boundsBehavior: Flickable.StopAtBounds + delegate: KickoffItem {} + highlight: PlasmaComponents.Highlight {} + model: Kickoff.KRunnerModel{} + + Connections { + target: header + + onQueryChanged: { + searchView.model.setQuery(header.query) + } + } + } // searchView + } // ScrollArea + + ContextMenu { + id: contextMenu + } +} diff --git a/applets/kickoff/package/contents/ui/SectionDelegate.qml b/applets/kickoff/package/contents/ui/SectionDelegate.qml new file mode 100644 index 0000000000..f32fceebc4 --- /dev/null +++ b/applets/kickoff/package/contents/ui/SectionDelegate.qml @@ -0,0 +1,64 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + + +Item { + id: sectionDelegate + + width: parent.width + height: childrenRect.height + +// PlasmaCore.SvgItem { +// anchors { +// left: parent.left +// right: parent.right +// } +// height: lineSvg.elementSize("horizontal-line").height +// +// visible: sectionDelegate.y > 0 +// svg: lineSvg +// elementId: "horizontal-line" +// } + PlasmaExtras.Heading { + id: sectionHeading + anchors { +// bottomMargin: units.gridUnit / 4 + left: parent.left + right: parent.right + //leftMargin: units.gridUnit + } + + y: units.gridUnit / 4 + //height: paintedHeight + units.gridUnit / 4 + //opacity: 0.6 + level: 4 + text: section + //horizontalAlignment: Text.AlignHCenter + } + Item { + width: parent.width + height: units.gridUnit / 4 + anchors.left: parent.left + anchors.top: sectionHeading.bottom + } +} // sectionDelegate diff --git a/applets/kickoff/package/contents/ui/SystemView.qml b/applets/kickoff/package/contents/ui/SystemView.qml new file mode 100644 index 0000000000..e0093f0669 --- /dev/null +++ b/applets/kickoff/package/contents/ui/SystemView.qml @@ -0,0 +1,26 @@ +/* + Copyright (C) 2011 Martin Gräßlin + Copyright (C) 2012 Marco Martin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +import QtQuick 2.0 +import org.kde.plasma.private.kickoff 0.1 as Kickoff + +BaseView { + objectName: "SystemView" + + model: Kickoff.SystemModel {} +} diff --git a/applets/kickoff/package/contents/ui/configGeneral.qml b/applets/kickoff/package/contents/ui/configGeneral.qml new file mode 100644 index 0000000000..9462005242 --- /dev/null +++ b/applets/kickoff/package/contents/ui/configGeneral.qml @@ -0,0 +1,56 @@ +/* + * Copyright 2013 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls + +//FIXME this causes a crash in Oxygen style +//QtControls.GroupBox { +Item { + +//FIXME i18n is broken currently +//when fixed delete this +function i18n(arg) { + return arg; +} + width: childrenRect.width + height: childrenRect.height + +//FIXME enable when we're back to being a group box +// flat: true +// title: i18n("Appearance") + + property alias cfg_switchTabsOnHover: switchTabsOnHoverCheckbox.checked + property alias cfg_showAppsByName: showApplicationsByNameCheckbox.checked + property alias cfg_showRecentlyInstalled: showRecentlyInstalledCheckbox.checked + + Column { + QtControls.CheckBox { + id: switchTabsOnHoverCheckbox + text: i18n("Switch tabs on hover:") + } + QtControls.CheckBox { + id: showApplicationsByNameCheckbox + text: i18n("Show applications by name:") + } + QtControls.CheckBox { + id: showRecentlyInstalledCheckbox + text: i18n("Show 'Recently Installed':") + } + } +} diff --git a/applets/kickoff/package/metadata.desktop b/applets/kickoff/package/metadata.desktop new file mode 100644 index 0000000000..c867b93fd2 --- /dev/null +++ b/applets/kickoff/package/metadata.desktop @@ -0,0 +1,184 @@ +[Desktop Entry] +Name=Application Launcher +Name[af]=Programlanseerder +Name[ar]=مطلق التطبيقات +Name[ast]=Llanzador d'aplicaciones +Name[be]=Запуск праграмаў +Name[be@latin]=Uklučeńnie aplikacyi +Name[bg]=Стартиране на програми +Name[bn]=অ্যাপলিকেশন লঞ্চার +Name[bn_IN]=অ্যাপ্লিকেশন সঞ্চালনকারী +Name[bs]=pokretač programa +Name[ca]=Llançador d'aplicacions +Name[ca@valencia]=Llançador d'aplicacions +Name[cs]=Spouštěč aplikací +Name[csb]=Zrëszôcz programów +Name[da]=Startmenu +Name[de]=K-Menü +Name[el]=Εκτέλεση εφαρμογών +Name[en_GB]=Application Launcher +Name[eo]=Aplikaĵolanĉilo +Name[es]=Lanzador de aplicaciones +Name[et]=Rakenduste käivitaja +Name[eu]=Aplikazio-abiarazlea +Name[fa]=راه‌انداز برنامه +Name[fi]=Sovelluskäynnistin +Name[fr]=Lanceur d'application +Name[fy]=In applikaashe starter +Name[ga]=Tosaitheoir Feidhmchlár +Name[gl]=Iniciador de programas +Name[gu]=કાર્યક્રમ ચલાવનાર +Name[he]=משגר יישומים +Name[hi]=अनुप्रयोग चालक +Name[hne]=अनुपरयोग चालू करइया +Name[hr]=Pokretač aplikacija +Name[hsb]=Startowar za aplikacije +Name[hu]=Programindító +Name[ia]=Lanceator de application +Name[id]=Peluncur Aplikasi +Name[is]=Forrita-Ræsir +Name[it]=Avviatore di applicazioni +Name[ja]=Kickoff アプリケーションランチャー +Name[kk]=Қолданбаны жегу +Name[km]=កម្មវិធី​ចាប់ផ្ដើម​កម្មវិធី​ +Name[kn]=ಅನ್ವಯ ಪ್ರಕ್ಷೇಪಕ (ಲಾಚರ್) +Name[ko]=프로그램 실행기 +Name[ku]=Deskpêkerê Sepanan +Name[lt]=Programų paleidiklis +Name[lv]=Programmu palaidējs +Name[mai]=अनुप्रयोग चालक +Name[mk]=Стартувач на апликации +Name[ml]=പ്രയോഗവിക്ഷേപിണി +Name[mr]=अनुप्रयोग प्रक्षेपक +Name[nb]=Programstarter +Name[nds]=Programmoproper +Name[ne]=अनुप्रयोक सुरुआतकर्ता +Name[nl]=Programmastarter +Name[nn]=Programstartar +Name[or]=ପ୍ରୟୋଗ ଆରମ୍ଭକର୍ତ୍ତା +Name[pa]=ਐਪਲੀਕੇਸ਼ ਲਾਂਚਰ +Name[pl]=Aktywator programów +Name[pt]=Lançador de Aplicações +Name[pt_BR]=Lançador de aplicativos +Name[ro]=Lansator de aplicații +Name[ru]=Меню запуска приложений +Name[se]=Prográmmaálggaheaddji +Name[si]=යෙදුම් ඇරඹුම +Name[sk]=Spúšťač aplikácií +Name[sl]=Zaganjalnik programov +Name[sr]=покретач програма +Name[sr@ijekavian]=покретач програма +Name[sr@ijekavianlatin]=pokretač programa +Name[sr@latin]=pokretač programa +Name[sv]=Starta program +Name[ta]=பயன்பாடு ஏவி +Name[te]=అనువర్తనం దించునది +Name[tg]=Оғози барномаҳо +Name[th]=ตัวเรียกใช้งานโปรแกรม +Name[tr]=Uygulama Başlatıcısı +Name[ug]=پروگرامما ئىجرا قىلغۇچ +Name[uk]=Інструмент запуску програм +Name[uz]=Dasturlarni ishga tushiruvchi +Name[uz@cyrillic]=Дастурларни ишга туширувчи +Name[vi]=Trình chạy ứng dụng +Name[wa]=Enondeu di programe +Name[x-test]=xxApplication Launcherxx +Name[zh_CN]=程序启动器 +Name[zh_TW]=應用程式啟動器 +Comment=Launcher to start applications +Comment[af]=Lanseerder om programme te loop +Comment[ar]=مطلق لتشغيل التطبيقات +Comment[ast]=Llanzador pa entamar aplicaciones +Comment[be@latin]=Uklučeńnie aplikacyjaŭ +Comment[bg]=Стартиране на програми +Comment[bn]=বিভিন্ন অ্যাপলিকেশন চালু করার জন্য লঞ্চার +Comment[bs]=Pokretač za programe +Comment[ca]=Llançador per a iniciar aplicacions +Comment[ca@valencia]=Llançador per a iniciar aplicacions +Comment[cs]=Aplikace pro spouštění aplikací +Comment[da]=Til at starte programmer +Comment[de]=Ermöglicht den Aufruf verschiedenster Programme +Comment[el]=Εκτέλεση εφαρμογών +Comment[en_GB]=Launcher to start applications +Comment[eo]=Aplikaĵolanĉilo +Comment[es]=Lanzador para iniciar aplicaciones +Comment[et]=Rakenduste käivitaja +Comment[eu]=Aplikazioak abiarazteko abiarazlea +Comment[fi]=Sovellusten käynnistysvalikko +Comment[fr]=Lanceur pour démarrer des applications +Comment[fy]=Starter om applikaasjes útein te setten +Comment[ga]=Ríomhchlár a thosaíonn feidhmchláir +Comment[gl]=Iniciador para iniciar programas +Comment[gu]=કાર્યક્રમો શરૂ કરનાર +Comment[he]=משגר לפתיחת יישומים +Comment[hi]=अनुप्रयोग प्रारंभ करने के लिए चालक +Comment[hne]=अनुपरयोग ल चालू करे बर लांचर +Comment[hr]=Pokretač aplikacija +Comment[hsb]=Startowar, kiž aplikacije wuwjedźe +Comment[hu]=Alkalmazásindító +Comment[ia]=Lanceator pro initiar applicationes +Comment[id]=Peluncur untuk menjalankan aplikasi +Comment[is]=Flýtiræsing forrita +Comment[it]=Avviatore per far partire le applicazioni +Comment[ja]=アプリケーションを起動します +Comment[kk]=Қолданба жеккіші +Comment[km]=កម្មវិធី​ចាប់ផ្ដើម​ត្រូវ​ចាប់ផ្ដើម​កម្មវិធី +Comment[kn]=ಅನ್ವಯಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲು ಒಂದು ಪ್ರಕ್ಷೇಪಕ (ಲಾಂಚರ್) +Comment[ko]=프로그램을 시작하는 실행기 +Comment[lt]=Programų paleidiklis +Comment[lv]=Poga programmu palaišanai +Comment[mk]=Аплет за стартување апликации +Comment[ml]=പ്രയോഗങ്ങള്‍ ആരംഭിക്കുന്നതിനു് സഹായിക്കുന്ന സംവിധാനം +Comment[mr]=अनुप्रयोग सुरु करण्याकरिता प्रक्षेपक +Comment[nb]=Starter programmer som ikke hører til KDE +Comment[nds]=Röppt Programmen op +Comment[ne]=अनुप्रयोग सुरुआत गर्न सुरुआतकर्ता +Comment[nl]=Applet voor het starten van programma's +Comment[nn]=Moderne programstartar +Comment[pa]=ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਉਣ ਲਈ ਲਾਂਚਰ +Comment[pl]=Aktywator do uruchamiania programów +Comment[pt]=Lançador para iniciar aplicações +Comment[pt_BR]=Lançador para iniciar aplicativos +Comment[ro]=Lansator pentru a porni aplicații +Comment[ru]=Современное меню запуска приложений +Comment[se]=Prográmmaálggaheaddji +Comment[si]=යෙදුම් ආරම්භ කිරීමට ආරම්භකය +Comment[sk]=Aplikácia na spúšťanie aplikácií +Comment[sl]=Zaganjalnik za zagon programov +Comment[sr]=Покретач за програме +Comment[sr@ijekavian]=Покретач за програме +Comment[sr@ijekavianlatin]=Pokretač za programe +Comment[sr@latin]=Pokretač za programe +Comment[sv]=Startprogram för att köra program +Comment[ta]=பயன்பாடுகளை ஏவிடத் தேவையான தளம் +Comment[te]=అనువర్తనాలను ప్రారంభించుటకు లాంచర్ +Comment[tg]=Средство для запуска приложений +Comment[th]=ตัวเรียกใช้งานโปรแกรมให้ทำงาน +Comment[tr]=Uygulamaları başlatmak için çalıştırıcı +Comment[ug]=پروگراممىنى قوزغىتىدىغان ئىجراچى(Launcher) +Comment[uk]=Програма для запуску програм +Comment[uz]=Dasturlarni ishga tushiruvchi vosita +Comment[uz@cyrillic]=Дастурларни ишга туширувчи восита +Comment[vi]=Mục chạy để khởi động ứng dụng +Comment[wa]=Enondeu po lancî des programes +Comment[x-test]=xxLauncher to start applicationsxx +Comment[zh_CN]=应用程序的启动器 +Comment[zh_TW]=應用程式啟動器 +Icon=start-here-kde +Type=Service +X-KDE-ServiceTypes=Plasma/Applet,Plasma/PopupApplet + +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/Kickoff.qml + +X-Plasma-RequiredExtensions=LaunchApp + +X-KDE-PluginInfo-Author=Martin Graesslin +X-KDE-PluginInfo-Email=plasma-devel@kde.org +X-KDE-PluginInfo-Name=org.kde.plasma.kickoff +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/Kickoff +X-KDE-PluginInfo-Category=Application Launchers +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/applets/pager/CMakeLists.txt b/applets/pager/CMakeLists.txt new file mode 100644 index 0000000000..6e9ae365c7 --- /dev/null +++ b/applets/pager/CMakeLists.txt @@ -0,0 +1,27 @@ +plasma_install_package(package org.kde.plasma.pager) + +set(pager_SRCS + plugin/model.cpp + plugin/pager.cpp + plugin/pagerplugin.cpp) + +add_library(pagerplugin SHARED ${pager_SRCS}) + +target_link_libraries(pagerplugin + KF5::KIOCore + KF5::WindowSystem + KF5::Plasma + KF5::Activities + KF5::KDE4Support + Qt5::Core + Qt5::Qml + Qt5::Quick + Qt5::DBus + taskmanager) + +if (X11_FOUND) + target_link_libraries(pagerplugin Qt5::X11Extras) +endif() + +install(TARGETS pagerplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/pager) +install(FILES plugin/qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/pager) diff --git a/applets/pager/Messages.sh b/applets/pager/Messages.sh new file mode 100644 index 0000000000..65ed909ad8 --- /dev/null +++ b/applets/pager/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.plasma.pager.pot +rm -f rc.cpp \ No newline at end of file diff --git a/applets/pager/package/contents/code/utils.js b/applets/pager/package/contents/code/utils.js new file mode 100644 index 0000000000..296c97766a --- /dev/null +++ b/applets/pager/package/contents/code/utils.js @@ -0,0 +1,6 @@ +.pragma library + +function contains(item, point) { + return point.x >= item.x && point.x <= item.x + item.width + && point.y >= item.y && point.y <= item.y + item.height; +} diff --git a/applets/pager/package/contents/config/config.qml b/applets/pager/package/contents/config/config.qml new file mode 100644 index 0000000000..edf43ea813 --- /dev/null +++ b/applets/pager/package/contents/config/config.qml @@ -0,0 +1,29 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: i18n("General") + icon: "user-desktop" + source: "configGeneral.qml" + } +} diff --git a/applets/pager/package/contents/config/main.xml b/applets/pager/package/contents/config/main.xml new file mode 100644 index 0000000000..100893380d --- /dev/null +++ b/applets/pager/package/contents/config/main.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + 2 + + + false + + + + + + + + 0 + + + + + + + diff --git a/applets/pager/package/contents/ui/configGeneral.qml b/applets/pager/package/contents/ui/configGeneral.qml new file mode 100644 index 0000000000..d84d29ebdc --- /dev/null +++ b/applets/pager/package/contents/ui/configGeneral.qml @@ -0,0 +1,143 @@ +/* + * Copyright 2013 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 as Layouts + + +//FIXME this causes a crash in Oxygen style +//QtControls.GroupBox { +Item { + + width: childrenRect.width + height: childrenRect.height + +//FIXME enable when we're back to being a group box +// flat: true +// title: i18n("Appearance") + + property int cfg_displayedText + property alias cfg_showWindowIcons: showRecentlyInstalledCheckbox.checked + property int cfg_currentDesktopSelected + + onCfg_displayedTextChanged: { + switch (cfg_displayedText) { + case 0: + displayedTextGroup.current = desktopNumberRadio; + break; + case 1: + displayedTextGroup.current = desktopNameRadio; + break; + default: + case 2: + displayedTextGroup.current = noTextRadio; + break; + } + } + + onCfg_currentDesktopSelectedChanged: { + switch (cfg_currentDesktopSelected) { + case 0: + currentDesktopSelectedGroup.current = doesNothingRadio; + break; + case 1: + currentDesktopSelectedGroup.current = showsDesktopRadio; + break; + default: + case 2: + currentDesktopSelectedGroup.current = showsDashboardRadio; + break; + } + } + + QtControls.ExclusiveGroup { + id: displayedTextGroup + } + QtControls.ExclusiveGroup { + id: currentDesktopSelectedGroup + } + + Layouts.GridLayout { + columns: 2 + QtControls.Label { + text: i18n("Display Text:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + QtControls.RadioButton { + id: desktopNumberRadio + exclusiveGroup: displayedTextGroup + text: i18n("Desktop number") + onCheckedChanged: if (checked) cfg_displayedText = 0; + } + Item { + width: 2 + height: 2 + Layouts.Layout.rowSpan: 2 + } + QtControls.RadioButton { + id: desktopNameRadio + exclusiveGroup: displayedTextGroup + text: i18n("Desktop name") + onCheckedChanged: if (checked) cfg_displayedText = 1; + } + QtControls.RadioButton { + id: noTextRadio + exclusiveGroup: displayedTextGroup + text: i18n("No text") + onCheckedChanged: if (checked) cfg_displayedText = 2; + } + + QtControls.Label { + text: i18n("Display icons:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + QtControls.CheckBox { + id: showRecentlyInstalledCheckbox + } + + QtControls.Label { + text: i18n("Selecting current desktop:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + QtControls.RadioButton { + id: doesNothingRadio + exclusiveGroup: currentDesktopSelectedGroup + text: i18n("Does nothing") + onCheckedChanged: if (checked) cfg_currentDesktopSelected = 0; + } + Item { + width: 2 + height: 2 + Layouts.Layout.rowSpan: 2 + } + QtControls.RadioButton { + id: showsDesktopRadio + exclusiveGroup: currentDesktopSelectedGroup + text: i18n("Shows desktop") + onCheckedChanged: if (checked) cfg_currentDesktopSelected = 1; + } + QtControls.RadioButton { + id: showsDashboardRadio + exclusiveGroup: currentDesktopSelectedGroup + text: i18n("Shows the dashboard") + onCheckedChanged: if (checked) cfg_currentDesktopSelected = 2; + } + } + +} diff --git a/applets/pager/package/contents/ui/main.qml b/applets/pager/package/contents/ui/main.qml new file mode 100644 index 0000000000..a7346e3a51 --- /dev/null +++ b/applets/pager/package/contents/ui/main.qml @@ -0,0 +1,329 @@ +/* + * Copyright 2012 Luís Gabriel Lima + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents +import org.kde.draganddrop 2.0 +import org.kde.plasma.private.pager 2.0 +import "utils.js" as Utils + +Item { + id: root + + property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) + + Layout.minimumWidth: Layout.maximumWidth + Layout.minimumHeight: Layout.maximumHeight + + Layout.maximumWidth: !root.vertical ? pager.preferredSize.width : undefined + Layout.maximumHeight: root.vertical ? pager.preferredSize.height : undefined + + Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation + Layout.fillWidth: root.vertical + Layout.fillHeight: !root.vertical + + property bool dragging: false + property int dragId + + property int dragSwitchDesktopId: -1 + + anchors.fill: parent + //visible: repeater.count > 1 // FIXME: re-enable and fix collapsing + + property color windowActiveOnActiveDesktopColor: theme.textColor + property color windowInactiveOnActiveDesktopColor: theme.textColor + property color windowActiveColor: theme.textColor + property color windowActiveBorderColor: theme.textColor + property color windowInactiveColor: theme.textColor + property color windowInactiveBorderColor: theme.textColor + + function action_addDesktop() { + pager.slotAddDesktop(); + } + + function action_removeDesktop() { + pager.slotRemoveDesktop(); + } + + Connections { + target: theme + onThemeChanged: { + windowActiveOnActiveDesktopColor = theme.textColor + windowActiveOnActiveDesktopColor.a = 0.6 + windowActiveColor = theme.textColor + windowActiveColor.a = 0.5; + windowActiveBorderColor = theme.textColor + //windowActiveBorderColor = 1 + + windowInactiveOnActiveDesktopColor = theme.textColor + windowInactiveOnActiveDesktopColor.a = 0.35; + windowInactiveColor = theme.textColor + windowInactiveColor.a = 0.17; + windowInactiveBorderColor = theme.textColor + windowInactiveBorderColor.a = 0.5 + } + } + Component.onCompleted: { + plasmoid.setAction("addDesktop", i18n("Add Virtual Desktop"), "list-add"); + plasmoid.setAction("removeDesktop", i18n("Remove Virtual Desktop"), "list-remove"); + windowActiveOnActiveDesktopColor = theme.textColor + windowActiveOnActiveDesktopColor.a = 0.6 + windowActiveColor = theme.textColor + windowActiveColor.a = 0.5; + windowActiveBorderColor = theme.textColor + //windowActiveBorderColor = 1 + + windowInactiveOnActiveDesktopColor = theme.textColor + windowInactiveOnActiveDesktopColor.a = 0.35; + windowInactiveColor = theme.textColor + windowInactiveColor.a = 0.17; + windowInactiveBorderColor = theme.textColor + windowInactiveBorderColor.a = 0.5 + } + + Pager { + id: pager + orientation: plasmoid.formFactor == PlasmaCore.Types.Vertical ? Qt.Vertical : Qt.Horizontal + size: Qt.size(root.width, root.height) + showWindowIcons: plasmoid.configuration.showWindowIcons + currentDesktopSelected: plasmoid.configuration.currentDesktopSelected + displayedText: plasmoid.configuration.displayedText + } + + Timer { + id: dragTimer + interval: 1000 + onTriggered: { + if (dragSwitchDesktopId != -1 && dragSwitchDesktopId !== pager.currentDesktop-1) { + pager.changeDesktop(dragSwitchDesktopId); + } + } + } + + Repeater { + id: repeater + model: pager.model + + PlasmaCore.ToolTipArea { + id: desktop + + property int desktopId: index + property string desktopName: model.desktopName ? model.desktopName : "" + property bool active: (desktopId === pager.currentDesktop-1) + + mainText: desktopName + + Component.onCompleted: { + console.log(model.windows) + } + + x: model.x + y: model.y + width: model.width + height: model.height + + PlasmaCore.FrameSvgItem { + anchors.fill: parent + z: 1 // to make sure that the FrameSvg will be placed on top of the windows + imagePath: "widgets/pager" + prefix: (desktopMouseArea.enabled && desktopMouseArea.containsMouse) || (root.dragging && root.dragId == desktopId) ? + "hover" : (desktop.active ? "active" : "normal") + } + + DropArea { + id: droparea + anchors.fill: parent + onDragEnter: { + root.dragSwitchDesktopId = desktop.desktopId; + dragTimer.start(); + } + onDragLeave: { + root.dragSwitchDesktopId = -1; + dragTimer.stop(); + } + onDrop: { + pager.dropMimeData(event.mimeData, desktop.desktopId); + root.dragSwitchDesktopId = -1; + dragTimer.stop(); + } + } + + MouseArea { + id: desktopMouseArea + anchors.fill: parent + onClicked: pager.changeDesktop(desktopId); + } + + Item { + id: clipRect + x: 1 + y: 1 + width: desktop.width - 2 + height: desktop.height - 2 + clip: true + + PlasmaComponents.Label { + id: desktopText + anchors.centerIn: parent + text: pager.displayedText == Pager.Name ? desktop.desktopName + : (pager.displayedText == Pager.Number ? desktop.desktopId+1 : "") + + } + + Repeater { + model: windows + + //update the tooltip whenever we add or remove a window + onCountChanged: { + var tooltipText = i18n("%1 windows", count) + var i; + for (i=0; i < Math.min(count,4); i++) { + tooltipText += "

  • "+ itemAt(i).visibleName +"
  • "; + } + if (i < count) { + tooltipText += "
    " + tooltipText += i18n("and %1 other windows", count-i) + } + + + desktop.subText = tooltipText + } + + Rectangle { + id: windowRect + + property int windowId: model.windowId + property string visibleName: model.visibleName + + /* since we move clipRect with 1, move it back */ + x: model.x - 1 + y: model.y - 1 + width: model.width + height: model.height + color: { + if (desktop.active) { + if (model.active) + return windowActiveOnActiveDesktopColor; + else + return windowInactiveOnActiveDesktopColor; + } else { + if (model.active) + return windowActiveColor; + else + return windowInactiveColor; + } + } + + border.width: 1 + border.color: model.active ? windowActiveBorderColor + : windowInactiveBorderColor + + KQuickControlsAddonsComponents.QPixmapItem { + id: icon + anchors.centerIn: parent + pixmap: model.icon + height: nativeHeight + width: nativeWidth + visible: pager.showWindowIcons && (windowRect.width >= icon.width) && (windowRect.height >= icon.height) + } + + MouseArea { + id: windowMouseArea + anchors.fill: parent + drag.target: windowRect + drag.axis: Drag.XandYAxis + drag.minimumX: -windowRect.width/2 + drag.maximumX: root.width - windowRect.width/2 + drag.minimumY: -windowRect.height/2 + drag.maximumY: root.height - windowRect.height/2 + + // used to save the state of some properties before the dragging + QtObject { + id: saveState + property int x: -1 + property int y: -1 + property variant parent + property int desktop: -1 + property int mouseX: -1 + property int mouseY: -1 + } + + drag.onActiveChanged: { + root.dragging = drag.active; + desktopMouseArea.enabled = !drag.active; + } + + // reparent windowRect to enable the dragging for other desktops + onPressed: { + if (windowRect.parent == root) + return; + + saveState.x = windowRect.x; + saveState.y = windowRect.y + saveState.parent = windowRect.parent; + saveState.desktop = desktop.desktopId; + saveState.mouseX = mouseX; + saveState.mouseY = mouseY; + + var value = root.mapFromItem(clipRect, windowRect.x, windowRect.y); + windowRect.x = value.x; + windowRect.y = value.y + windowRect.parent = root; + } + + onReleased: { + if (root.dragging) { + pager.moveWindow(windowRect.windowId, windowRect.x, windowRect.y, + root.dragId, saveState.desktop); + } else { + // when there is no dragging (just a click), the event is passed + // to the desktop mousearea + desktopMouseArea.clicked(mouse); + } + + windowRect.x = saveState.x; + windowRect.y = saveState.y; + windowRect.parent = saveState.parent; + } + } + + function checkDesktopHover() { + if (!windowMouseArea.drag.active) + return; + + var mouse = root.mapFromItem(windowRect, saveState.mouseX, saveState.mouseY); + for (var i = 0; i < root.children.length; i++) { + var item = root.children[i]; + if (item.desktopId != undefined && Utils.contains(item, mouse)) { + root.dragId = item.desktopId; + return; + } + } + } + + onXChanged: checkDesktopHover(); + onYChanged: checkDesktopHover(); + } + } + } + } + } +} diff --git a/applets/pager/package/metadata.desktop b/applets/pager/package/metadata.desktop new file mode 100644 index 0000000000..fb33932d32 --- /dev/null +++ b/applets/pager/package/metadata.desktop @@ -0,0 +1,170 @@ +[Desktop Entry] +Name=Pager +Name[af]=Pager +Name[ar]=المنادي +Name[ast]=Paxinador +Name[be@latin]=Stały +Name[bg]=Пейджър +Name[bn]=পেজার +Name[bn_IN]=পেজার +Name[bs]=pejdžer +Name[ca]=Paginador +Name[ca@valencia]=Paginador +Name[cs]=Přepínač ploch +Name[csb]=Pager +Name[da]=Skrivebordsvælger +Name[de]=Arbeitsflächen-Umschalter +Name[el]=Πίνακας σελίδων +Name[en_GB]=Pager +Name[eo]=Paĝilo +Name[es]=Paginador +Name[et]=Peiler +Name[eu]=Bilagailua +Name[fa]=پی‌جو +Name[fi]=Sivutin +Name[fr]=Gestionnaire de bureaux +Name[fy]=Semafoan +Name[ga]=Brabhsálaí Leathanach +Name[gl]=Paxinador +Name[gu]=પેજર +Name[hi]=पेजर +Name[hne]=पेजर +Name[hr]=Pager +Name[hsb]=Listowar +Name[hu]=Asztalváltó +Name[ia]=Pager +Name[id]=Pemisah +Name[is]=Flettir +Name[it]=Cambiadesktop +Name[ja]=ページャ +Name[kk]=Ақтарғыш +Name[km]=Pager +Name[kn]=ಪುಟವೀಕ್ಷಕ (ಪೇಜರ್) +Name[ko]=데스크톱 전환기 +Name[ku]=Rûpelker +Name[lt]=Puslapiuotojas +Name[lv]=Lapotājs +Name[mai]=पेजर +Name[mk]=Пејџер +Name[ml]=പേജര്‍ +Name[mr]=पेजर +Name[nb]=Skrivebordsbytter +Name[nds]=Schriefdisch-Översicht +Name[ne]=पेजर +Name[nl]=Pager +Name[nn]=Skrivebordsbytar +Name[oc]=Recebedor d'apèl +Name[or]=ପେଜର +Name[pa]=ਪੇਜ਼ਰ +Name[pl]=Pager +Name[pt]=Paginador +Name[pt_BR]=Paginador +Name[ro]=Paginator +Name[ru]=Переключение рабочих столов +Name[se]=Čállinbeavdemolsojeaddj +Name[si]=පේජරය +Name[sk]=Prepínač plôch +Name[sl]=Pozivnik +Name[sr]=листач +Name[sr@ijekavian]=листач +Name[sr@ijekavianlatin]=listač +Name[sr@latin]=listač +Name[sv]=Skrivbordsvisning +Name[ta]=Pager +Name[te]=పేజర్ +Name[tg]=Пейҷер +Name[th]=สลับหน้าพื้นที่ทำงาน +Name[tr]=Sayfalayıcı +Name[ug]=چاقىرغۇ +Name[uk]=Пейджер +Name[wa]=Pager +Name[x-test]=xxPagerxx +Name[zh_CN]=分页器 +Name[zh_TW]=虛擬桌面管理器 +Comment=Switch between virtual desktops +Comment[ar]=يبدل بين أسطح المكتب الافتراضية +Comment[ast]=Camudar ente escritorios virtuales +Comment[be@latin]=Pieraklučeńnie virtualnych stałoŭ +Comment[bg]=Превключване между работните плотове +Comment[bs]=Prebacujte virtuelne površi +Comment[ca]=Commuta entre escriptoris virtuals +Comment[ca@valencia]=Commuta entre escriptoris virtuals +Comment[cs]=Přepínač mezi virtuálními plochami +Comment[da]=Skift mellem virtuelle skriveborde +Comment[de]=Ermöglicht Ihnen das Wechseln zwischen virtuellen Arbeitsflächen. +Comment[el]=Εναλλαγή μεταξύ εικονικών επιφανειών εργασίας +Comment[en_GB]=Switch between virtual desktops +Comment[eo]=Komuti inter virtualaj labortabloj +Comment[es]=Cambiar entre escritorios virtuales +Comment[et]=Lülitumine virtuaalsete töölaudade vahel +Comment[eu]=Aldatu alegiazko mahaigain batetik bestera +Comment[fi]=Vaihda virtuaalityöpöytää +Comment[fr]=Passe d'un bureau virtuel à l'autre +Comment[fy]=Wikselje tusken firuele buroblêden +Comment[ga]=Athraigh an deasc fhíorúil +Comment[gl]=Troca entre escritorios virtuais +Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ્સની વચ્ચે બદલો +Comment[he]=משמש למעבר בין שולחנות עבודה וירטואלים +Comment[hi]=आभासी डेस्कटॉप्स के बीच स्विच करें +Comment[hne]=आभासी डेस्कटाप मं स्विच करव +Comment[hr]=Mijenjanje virtualnih radnih površina +Comment[hsb]=Mjez wirtuelnymi dźěłowymi powjerchami tam a sem skakać +Comment[hu]=Virtuális asztalok közötti váltásra szolgáló elem +Comment[ia]=Commuta inter scriptorios virtual +Comment[id]=Berpindah di antara desktop virtual +Comment[is]=Skiptir milli sýndarskjáborða +Comment[it]=Passa ad altri desktop virtuali +Comment[ja]=仮想デスクトップを切り替えます +Comment[kk]=Виртуалды үстелдерді ақтару +Comment[km]=ប្ដូរ​រវាង​ផ្ទៃតុ​និម្មិត +Comment[kn]=ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಗಳ ನಡುವೆ ಅಂತರಿಸು +Comment[ko]=가상 데스크톱 사이를 전환합니다 +Comment[lt]=Persijungti tarp virtualių darbastalių +Comment[lv]=Pārslēgties starp virtuālām darbvirsmām +Comment[mk]=Менување меѓу виртуелни раб. површини +Comment[ml]=വര്‍ച്ചുവല്‍ പണിയിടങ്ങള്‍ തമ്മില്‍ മാറുക +Comment[mr]=आभासी डेस्कटॉप बदला +Comment[nb]=Bytt mellom virtuelle skrivebord +Comment[nds]=Twischen Schriefdischen wesseln +Comment[nl]=Schakel tussen virtuele bureaubladen +Comment[nn]=Byt mellom virtuelle skrivebord +Comment[or]=ଆଭାସୀ ଡ଼େସ୍କଟପଗୁଡ଼ିକ ମଧ୍ଯରେ ଅଦଳ ବଦଳ କରନ୍ତୁ +Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪਾਂ ਵਿੱਚ ਸਵਿੱਚ ਕਰੋ +Comment[pl]=Przełączenie między wirtualnymi pulpitami +Comment[pt]=Mudar de ecrã virtual +Comment[pt_BR]=Alterna entre áreas de trabalho virtuais +Comment[ro]=Comută între birouri virtuale +Comment[ru]=Переключение между рабочими столами +Comment[si]=අතත්‍ය වැඩතල අතර මාරුවන්න +Comment[sk]=Prepínanie medzi virtuálnymi plochami +Comment[sl]=Preklapljajte med navideznimi namizji +Comment[sr]=Пребацујте виртуелне површи +Comment[sr@ijekavian]=Пребацујте виртуелне површи +Comment[sr@ijekavianlatin]=Prebacujte virtuelne površi +Comment[sr@latin]=Prebacujte virtuelne površi +Comment[sv]=Byt mellan virtuella skrivbord +Comment[ta]=Switch between virtual desktops +Comment[te]=వర్చ్యువల్ రంగస్థలముల మధ్యన మారుము +Comment[tg]=Переключение между рабочими столами +Comment[th]=สลับไปมาระหว่างพื้นที่ทำงานเสมือน +Comment[tr]=Sanal masaüstleri arasında gezin +Comment[ug]=مەۋھۇم ئۈستەلئۈستىلىرىنى ئالماشتۇر +Comment[uk]=Перемкніть віртуальні стільниці +Comment[wa]=Passer d' on forveyou scribanne a èn ôte +Comment[x-test]=xxSwitch between virtual desktopsxx +Comment[zh_CN]=在虚拟桌面间切换 +Comment[zh_TW]=在虛擬桌面間切換 +Icon=user-desktop +Type=Service +X-Plasma-API=declarativeappletscript +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-MainScript=ui/main.qml +X-KDE-PluginInfo-Author=The Plasma Team +X-KDE-PluginInfo-Email=plasma-devel@kde.org +X-KDE-PluginInfo-Name=org.kde.plasma.pager +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/Pager +X-KDE-PluginInfo-Category=Windows and Tasks +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/applets/pager/plugin/model.cpp b/applets/pager/plugin/model.cpp new file mode 100644 index 0000000000..1b80baa0e4 --- /dev/null +++ b/applets/pager/plugin/model.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2012 Luís Gabriel Lima + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "model.h" + +RectangleModel::RectangleModel(QObject *parent) + : QAbstractListModel(parent) +{ + setRoleNames(roles()); +} + +QHash RectangleModel::roles() const +{ + QHash rectRoles; + rectRoles[WidthRole] = "width"; + rectRoles[HeightRole] = "height"; + rectRoles[XRole] = "x"; + rectRoles[YRole] = "y"; + return rectRoles; +} + +void RectangleModel::clear() +{ + m_rects.clear(); +} + +void RectangleModel::append(const QRectF &rect) +{ + m_rects.append(rect); +} + +QRectF &RectangleModel::rectAt(int index) +{ + return m_rects[index]; +} + +int RectangleModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_rects.count(); +} + +QVariant RectangleModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() > m_rects.count()) + return QVariant(); + + const QRectF &rect = m_rects[index.row()]; + switch(role) { + case WidthRole: + return rect.width(); + case HeightRole: + return rect.height(); + case XRole: + return rect.x(); + case YRole: + return rect.y(); + default: + return QVariant(); + } +} + + +WindowModel::WindowModel(QObject *parent) + : RectangleModel(parent) +{ + setRoleNames(roles()); +} + +QHash WindowModel::roles() const +{ + QHash rectRoles = RectangleModel::roles(); + rectRoles[IdRole] = "windowId"; + rectRoles[ActiveRole] = "active"; + rectRoles[IconRole] = "icon"; + rectRoles[VisibleNameRole] = "visibleName"; + return rectRoles; +} + +void WindowModel::clear() +{ + beginResetModel(); + RectangleModel::clear(); + m_ids.clear(); + m_active.clear(); + m_icons.clear(); + m_visibleNames.clear(); + endResetModel(); +} + +void WindowModel::append(WId windowId, const QRectF &rect, bool active, + const QPixmap &icon, const QString &name) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_ids.append(windowId); + RectangleModel::append(rect); + m_active.append(active); + m_icons.append(icon); + m_visibleNames.append(name); + endInsertRows(); +} + +WId WindowModel::idAt(int index) const +{ + return m_ids[index]; +} + +QString WindowModel::visibleNameAt(int index) const +{ + return m_visibleNames[index]; +} + +QVariant WindowModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount()) + return QVariant(); + + if (role >= RectangleModel::WidthRole && role < IdRole) + return RectangleModel::data(index, role); + + switch (role) { + case IdRole: + return int(m_ids[index.row()]); + case ActiveRole: + return m_active[index.row()]; + case IconRole: + return m_icons[index.row()]; + case VisibleNameRole: + return m_visibleNames[index.row()]; + default: + return QVariant(); + } +} + + +PagerModel::PagerModel(QObject *parent) + : QAbstractListModel(parent) +{ + setRoleNames(roles()); +} + +WindowModel *PagerModel::windowsAt(int index) const +{ + if (index < 0 || index >= m_windows.count()) + return 0; + + return qobject_cast(m_windows[index]); +} + +QHash PagerModel::roles() const +{ + QHash rectRoles = m_desktops.roles(); + rectRoles[WindowsRole] = "windows"; + rectRoles[DesktopNameRole] = "desktopName"; + return rectRoles; +} + +void PagerModel::clearDesktopRects() +{ + beginResetModel(); + m_desktops.clear(); + m_names.clear(); + endResetModel(); +} + +void PagerModel::appendDesktopRect(const QRectF &rect, const QString &name) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_desktops.append(rect); + m_names.append(name); + endInsertRows(); +} + +QRectF& PagerModel::desktopRectAt(int index) +{ + return m_desktops.rectAt(index); +} + +void PagerModel::clearWindowRects() +{ + int removeIndex = -1; + for (int i = 0; i < m_windows.count(); i++) { + windowsAt(i)->clear(); + + if (i >= rowCount()) + removeIndex = (removeIndex == -1) ? i : -1; + } + + if (removeIndex != -1) { + // remove the windows model if the number of desktop has decreased + for (int i = m_windows.count()-1; i >= removeIndex; i--) { + windowsAt(i)->deleteLater(); + m_windows.removeAt(i); + } + } + + // append more windows model if the number of desktop has increased + for (int i = m_windows.count(); i < rowCount(); i++) + m_windows.append(new WindowModel(this)); +} + +void PagerModel::appendWindowRect(int desktopId, WId windowId, const QRectF &rect, + bool active, const QPixmap &icon, const QString &name) +{ + WindowModel *windows = windowsAt(desktopId); + if (!windows) + return; + + windows->append(windowId, rect, active, icon, name); + + QModelIndex i = index(desktopId); + emit dataChanged(i, i); +} + +QVariant PagerModel::data(const QModelIndex &index, int role) const +{ + if (role >= RectangleModel::WidthRole && role < WindowsRole) + return m_desktops.data(index, role); + + if (index.row() < 0 || index.row() >= m_windows.count()) + return QVariant(); + + switch (role) { + case WindowsRole: + return QVariant::fromValue(m_windows[index.row()]); + case DesktopNameRole: + return m_names[index.row()]; + default: + return QVariant(); + } +} + +int PagerModel::rowCount(const QModelIndex &index) const +{ + return m_desktops.rowCount(index); +} diff --git a/applets/pager/plugin/model.h b/applets/pager/plugin/model.h new file mode 100644 index 0000000000..9382247308 --- /dev/null +++ b/applets/pager/plugin/model.h @@ -0,0 +1,112 @@ +/* + * Copyright 2012 Luís Gabriel Lima + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MODEL_H +#define MODEL_H + +#include +#include +#include // For WId +#include + +class RectangleModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum RectangleRoles { + WidthRole = Qt::UserRole + 1, + HeightRole, + XRole, + YRole + }; + + RectangleModel(QObject *parent = 0); + + virtual QHash roles() const; + virtual void clear(); + void append(const QRectF &rect); + QRectF &rectAt(int index); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QList m_rects; +}; + + +class WindowModel : public RectangleModel +{ + Q_OBJECT +public: + enum WindowRole { + IdRole = RectangleModel::YRole + 1, + ActiveRole, + IconRole, + VisibleNameRole + }; + + WindowModel(QObject *parent = 0); + + QHash roles() const; + void clear(); + void append(WId, const QRectF &, bool active, const QPixmap &icon, const QString &name); + WId idAt(int index) const; + QString visibleNameAt(int index) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QList m_ids; + QList m_active; + QList m_icons; + QStringList m_visibleNames; +}; + + +class PagerModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum PagerRoles { + WindowsRole = RectangleModel::YRole + 1, + DesktopNameRole + }; + + PagerModel(QObject *parent = 0); + + QHash roles() const; + + void clearDesktopRects(); + void appendDesktopRect(const QRectF &rect, const QString &name); + QRectF &desktopRectAt(int index); + + void clearWindowRects(); + void appendWindowRect(int desktopId, WId, const QRectF &, bool active, + const QPixmap &icon, const QString &name); + WindowModel *windowsAt(int index) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + +private: + RectangleModel m_desktops; + QList m_windows; + QStringList m_names; +}; + +#endif // MODEL_H diff --git a/applets/pager/plugin/pager.cpp b/applets/pager/plugin/pager.cpp new file mode 100644 index 0000000000..59f13fcd23 --- /dev/null +++ b/applets/pager/plugin/pager.cpp @@ -0,0 +1,601 @@ +/*************************************************************************** + * Copyright (C) 2007 by Daniel Laidig * + * Copyright (C) 2012 by Luís Gabriel Lima * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "pager.h" + +#include + +#include +#include +#include +#include +#if HAVE_X11 +#include +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#if HAVE_X11 +#include +#endif + +#include +#include + +#include + +#include + +const int FAST_UPDATE_DELAY = 100; +const int UPDATE_DELAY = 500; +const int MAXDESKTOPS = 20; +// random(), find a less magic one if you can. -sreich +const qreal MAX_TEXT_WIDTH = 800; + +Pager::Pager(QObject *parent) + : QObject(parent), + m_displayedText(None), + m_currentDesktopSelected(DoNothing), + m_rows(2), + m_columns(0), + m_currentDesktop(0), + m_orientation(Qt::Horizontal), + m_addDesktopAction(0), + m_removeDesktopAction(0), + m_showWindowIcons(false), + m_desktopDown(false), + m_validSizes(false), + m_desktopWidget(QApplication::desktop()) +#if HAVE_X11 + , m_isX11(QX11Info::isPlatformX11()) +#endif +{ + // initialize with a decent default + m_desktopCount = qMax(1, KWindowSystem::numberOfDesktops()); + + m_pagerModel = new PagerModel(this); + + createMenu(); + + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + connect(m_timer, SIGNAL(timeout()), this, SLOT(recalculateWindowRects())); + + connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(currentDesktopChanged(int))); + connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), this, SLOT(startTimerFast())); + connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(startTimerFast())); + connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(startTimerFast())); + connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), this, SLOT(numberOfDesktopsChanged(int))); + connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), this, SLOT(desktopNamesChanged())); + connect(KWindowSystem::self(), SIGNAL(stackingOrderChanged()), this, SLOT(startTimerFast())); + connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,const ulong*)), this, SLOT(windowChanged(WId,const ulong*))); + connect(KWindowSystem::self(), SIGNAL(showingDesktopChanged(bool)), this, SLOT(startTimer())); + connect(m_desktopWidget, SIGNAL(screenCountChanged(int)), SLOT(desktopsSizeChanged())); + connect(m_desktopWidget, SIGNAL(resized(int)), SLOT(desktopsSizeChanged())); + + // connect to KWin's reloadConfig signal to get updates on the desktop layout + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig", + this, SLOT(configChanged())); + + recalculateGridSizes(m_rows); + + setCurrentDesktop(KWindowSystem::currentDesktop()); + + KActivities::Consumer *act = new KActivities::Consumer(this); + connect(act, SIGNAL(currentActivityChanged(QString)), this, SLOT(currentActivityChanged(QString))); + m_currentActivity = act->currentActivity(); +} + +Pager::~Pager() +{ +} + + + +void Pager::setCurrentDesktop(int desktop) +{ + if (m_currentDesktop != desktop) { + m_currentDesktop = desktop; + emit currentDesktopChanged(); + } +} + +void Pager::setShowWindowIcons(bool show) +{ + if (m_showWindowIcons != show) { + m_showWindowIcons = show; + emit showWindowIconsChanged(); + } +} + +Qt::Orientation Pager::orientation() const +{ + return m_orientation; +} + +void Pager::setOrientation(Qt::Orientation orientation) +{ + if (m_orientation == orientation) { + return; + } + + m_orientation = orientation; + emit orientationChanged(); + + // whenever we switch to/from vertical form factor, swap the rows and columns around + if (m_columns != m_rows) { + // pass in columns as the new rows + recalculateGridSizes(m_columns); + recalculateWindowRects(); + } +} + +QSizeF Pager::size() const +{ + return m_size; +} + +QSize Pager::preferredSize() const +{ + return m_preferredSize; +} + +void Pager::setSize(const QSizeF &size) +{ + if (m_size == size) { + return; + } + + m_size = size; + emit sizeChanged(); + + m_validSizes = false; + + startTimer(); +} + +Pager::CurrentDesktopSelected Pager::currentDesktopSelected() const +{ + return m_currentDesktopSelected; +} + +void Pager::setCurrentDesktopSelected(CurrentDesktopSelected cur) +{ + if (m_currentDesktopSelected == cur) { + return; + } + + m_currentDesktopSelected = cur; + emit currentDesktopSelectedChanged(); +} + +Pager::DisplayedText Pager::displayedText() const +{ + return m_displayedText; +} + +void Pager::setDisplayedText(Pager::DisplayedText disp) +{ + if (m_displayedText == disp) { + return; + } + + m_displayedText = disp; + emit displayedTextChanged(); +} + +void Pager::createMenu() +{ +#if HAVE_X11 + m_addDesktopAction = new QAction(SmallIcon("list-add"),i18n("&Add Virtual Desktop"), this); + m_actions.append(m_addDesktopAction); + connect(m_addDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotAddDesktop())); + m_removeDesktopAction = new QAction(SmallIcon("list-remove"),i18n("&Remove Last Virtual Desktop"), this); + m_actions.append(m_removeDesktopAction); + connect(m_removeDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotRemoveDesktop())); + + if (m_desktopCount <= 1) { + m_removeDesktopAction->setEnabled(false); + } else if (m_desktopCount >= MAXDESKTOPS) { + m_addDesktopAction->setEnabled(false); + } +#endif +} + +QList Pager::contextualActions() +{ + return m_actions; +} + +#if HAVE_X11 +void Pager::slotAddDesktop() +{ + if (!m_isX11) { + return; + } + NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); + info.setNumberOfDesktops(info.numberOfDesktops() + 1); +} + +void Pager::slotRemoveDesktop() +{ + if (!m_isX11) { + return; + } + NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); + int desktops = info.numberOfDesktops(); + if (desktops > 1) { + info.setNumberOfDesktops(info.numberOfDesktops() - 1); + } +} +#endif + +void Pager::recalculateGridSizes(int rows) +{ + // recalculate the number of rows and columns in the grid + rows = qBound(1, rows, m_desktopCount); + // avoid weird cases like having 3 rows for 4 desktops, where the last row is unused + int columns = m_desktopCount / rows; + if (m_desktopCount % rows > 0) { + columns++; + } + + rows = m_desktopCount / columns; + if (m_desktopCount % columns > 0) { + rows++; + } + + // update the grid size + m_rows = rows; + m_columns = columns; + + updateSizes(); +} + +void Pager::updateSizes() +{ + const int padding = 2; // Space between miniatures of desktops + const int textMargin = 3; // Space between name of desktop and border + + const qreal leftMargin = 0; + const qreal topMargin = 0; + const qreal rightMargin = 0; + const qreal bottomMargin = 0; + + QRect totalRect; + for (int x = 0; x < m_desktopWidget->screenCount(); x++) { + totalRect |= m_desktopWidget->screenGeometry(x); + } + + const qreal ratio = (qreal) totalRect.width() / + (qreal) totalRect.height(); + + + qreal itemHeight; + qreal itemWidth; + qreal preferredItemHeight; + qreal preferredItemWidth; + + if (orientation() == Qt::Vertical) { + + itemWidth = (m_size.width() - leftMargin - rightMargin - + padding * (m_columns - 1)) / m_columns; + + itemHeight = itemWidth / ratio; + + } else { + // work out the preferred size based on the height of the geometry + preferredItemHeight = (m_size.height() - topMargin - bottomMargin - + padding * (m_rows - 1)) / m_rows; + preferredItemWidth = preferredItemHeight * ratio; + + if (m_displayedText == Name) { + // When containment is in this position we are not limited by low width and we can + // afford increasing width of applet to be able to display every name of desktops + for (int i = 0; i < m_desktopCount; i++) { + QFontMetricsF metrics(KGlobalSettings::taskbarFont()); + QSizeF textSize = metrics.size(Qt::TextSingleLine, KWindowSystem::desktopName(i+1)); + if (textSize.width() + textMargin * 2 > preferredItemWidth) { + preferredItemWidth = textSize.width() + textMargin * 2; + } + } + } + + itemWidth = (m_size.width() - leftMargin - rightMargin - + padding * (m_columns - 1)) / m_columns; + if (itemWidth > preferredItemWidth) { + itemWidth = preferredItemWidth; + } + itemHeight = preferredItemHeight; + if (itemWidth < itemHeight * ratio) { + itemWidth = itemHeight * ratio; + } + } + + m_widthScaleFactor = itemWidth / totalRect.width(); + m_heightScaleFactor = itemHeight / totalRect.height(); + + m_pagerModel->clearDesktopRects(); + + int pw = (int)(leftMargin + (itemWidth + padding) * m_columns + rightMargin); + int ph = (int)(topMargin + (itemHeight + padding) * m_rows + bottomMargin); + + if (orientation() == Qt::Vertical) { + pw = m_size.width(); + } else { + ph = m_size.height(); + } + + m_preferredSize = QSize(pw, ph); + emit preferredSizeChanged(); + + QRectF itemRect(QPointF(leftMargin, topMargin) , QSizeF(itemWidth, itemHeight)); + for (int i = 0; i < m_desktopCount; i++) { + itemRect.moveLeft(leftMargin + (i % m_columns) * (itemWidth + padding)); + itemRect.moveTop(topMargin + (i / m_columns) * (itemHeight + padding)); + + QString name = KWindowSystem::desktopName(i + 1); + m_pagerModel->appendDesktopRect(itemRect, name); + } + + m_validSizes = true; +} + +void Pager::recalculateWindowRects() +{ + if (!m_validSizes) { + updateSizes(); + } + + QList windows = KWindowSystem::stackingOrder(); + m_pagerModel->clearWindowRects(); + +#if HAVE_X11 + foreach (WId window, windows) { + KWindowInfo info = KWindowSystem::windowInfo(window, NET::WMGeometry | NET::WMFrameExtents | + NET::WMWindowType | NET::WMDesktop | + NET::WMState | NET::XAWMState | NET::WMVisibleName); + NET::WindowType type = info.windowType(NET::NormalMask | NET::DialogMask | NET::OverrideMask | + NET::UtilityMask | NET::DesktopMask | NET::DockMask | + NET::TopMenuMask | NET::SplashMask | NET::ToolbarMask | + NET::MenuMask); + + // the reason we don't check for -1 or Net::Unknown here is that legitimate windows, such + // as some java application windows, may not have a type set for them. + // apparently sane defaults on properties is beyond the wisdom of x11. + if (type == NET::Desktop || type == NET::Dock || type == NET::TopMenu || + type == NET::Splash || type == NET::Menu || type == NET::Toolbar || + info.hasState(NET::SkipPager) || info.isMinimized()) { + continue; + } + + //check activity + NETWinInfo netInfo(QX11Info::connection(), window, QX11Info::appRootWindow(), 0, NET::WM2Activities); + QString result(netInfo.activities()); + if (!result.isEmpty() && result != "00000000-0000-0000-0000-000000000000") { + QStringList activities = result.split(','); + if (!activities.contains(m_currentActivity)) { + continue; + } + } + + for (int i = 0; i < m_desktopCount; i++) { + if (!info.isOnDesktop(i+1)) { + continue; + } + + QRectF windowRect = info.frameGeometry(); + + if (KWindowSystem::mapViewport()) { + windowRect = fixViewportPosition(windowRect.toRect()); + } + + windowRect = QRectF(windowRect.x() * m_widthScaleFactor, + windowRect.y() * m_heightScaleFactor, + windowRect.width() * m_widthScaleFactor, + windowRect.height() * m_heightScaleFactor).toRect(); + + bool active = (window == KWindowSystem::activeWindow()); + int windowIconSize = KIconLoader::global()->currentSize(KIconLoader::Small); + int windowRectSize = qMin(windowRect.width(), windowRect.height()); + windowIconSize = qMax(windowIconSize, windowRectSize / 2); + QPixmap icon = KWindowSystem::icon(info.win(), windowIconSize, windowIconSize, true); + m_pagerModel->appendWindowRect(i, window, windowRect, active, icon, info.visibleName()); + } + } +#endif +} + +void Pager::currentDesktopChanged(int desktop) +{ + if (desktop < 1) { + return; // bogus value, don't accept it + } + + setCurrentDesktop(desktop); + m_desktopDown = false; + startTimerFast(); +} + +void Pager::currentActivityChanged(const QString &activity) +{ + m_currentActivity = activity; + startTimerFast(); +} + +void Pager::numberOfDesktopsChanged(int num) +{ + if (num < 1) { + return; // refuse to update to zero desktops + } + +#if HAVE_X11 + m_removeDesktopAction->setEnabled(num > 1); + m_addDesktopAction->setEnabled(num < MAXDESKTOPS); +#endif + + m_desktopCount = num; + + m_pagerModel->clearDesktopRects(); + recalculateGridSizes(m_rows); + recalculateWindowRects(); +} + +void Pager::desktopNamesChanged() +{ + m_pagerModel->clearDesktopRects(); + m_validSizes = false; + startTimer(); +} + +void Pager::windowChanged(WId id, const unsigned long* dirty) +{ + Q_UNUSED(id) + + if (dirty[NETWinInfo::PROTOCOLS] & (NET::WMGeometry | NET::WMDesktop) || + dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities) { + startTimer(); + } +} + +void Pager::desktopsSizeChanged() +{ + m_pagerModel->clearDesktopRects(); + m_validSizes = false; + startTimer(); +} + +void Pager::startTimer() +{ + if (!m_timer->isActive()) { + m_timer->start(UPDATE_DELAY); + } +} + +void Pager::startTimerFast() +{ + if (!m_timer->isActive()) { + m_timer->start(FAST_UPDATE_DELAY); + } +} + +void Pager::moveWindow(int window, double x, double y, int targetDesktop, int sourceDesktop) +{ +#if HAVE_X11 + if (!m_isX11) { + return; + } + WId windowId = (WId) window; + + QPointF dest = QPointF(x, y) - m_pagerModel->desktopRectAt(targetDesktop).topLeft(); + + dest = QPointF(dest.x()/m_widthScaleFactor, dest.y()/m_heightScaleFactor); + + // don't move windows to negative positions + dest = QPointF(qMax(dest.x(), qreal(0.0)), qMax(dest.y(), qreal(0.0))); + + // use _NET_MOVERESIZE_WINDOW rather than plain move, so that the WM knows this is a pager request + NETRootInfo info(QX11Info::connection(), 0); + int flags = (0x20 << 12) | (0x03 << 8) | 1; // from tool, x/y, northwest gravity + + if (!KWindowSystem::mapViewport()) { + KWindowInfo windowInfo = KWindowSystem::windowInfo(windowId, NET::WMDesktop | NET::WMState); + + if (!windowInfo.onAllDesktops()) { + KWindowSystem::setOnDesktop(windowId, targetDesktop+1); + } + + // only move the window if it is not full screen and if it is kept within the same desktop + // moving when dropping between desktop is too annoying due to the small drop area. + if (!(windowInfo.state() & NET::FullScreen) && + (targetDesktop == sourceDesktop || windowInfo.onAllDesktops())) { + QPoint d = dest.toPoint(); + info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); + } + } else { + // setOnDesktop() with viewports is also moving a window, and since it takes a moment + // for the WM to do the move, there's a race condition with figuring out how much to move, + // so do it only as one move + dest += KWindowSystem::desktopToViewport(targetDesktop+1, false); + QPoint d = KWindowSystem::constrainViewportRelativePosition(dest.toPoint()); + info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); + } + m_timer->start(); +#else + Q_UNUSED(window) + Q_UNUSED(x) + Q_UNUSED(y) + Q_UNUSED(targetDesktop) + Q_UNUSED(sourceDesktop) +#endif +} + +void Pager::changeDesktop(int newDesktop) +{ +#if HAVE_X11 + if (!m_isX11) { + return; + } + if (m_currentDesktop == newDesktop+1) { + // toogle the desktop or the dashboard + if (m_currentDesktopSelected == ShowDesktop) { + NETRootInfo info(QX11Info::connection(), 0); + m_desktopDown = !m_desktopDown; + info.setShowingDesktop(m_desktopDown); + } else if (m_currentDesktopSelected == ShowDashboard) { + QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App"); + plasmaApp.call("toggleDashboard"); + } + } else { + KWindowSystem::setCurrentDesktop(newDesktop + 1); + setCurrentDesktop(newDesktop + 1); + } +#else + Q_UNUSED(newDesktop) +#endif +} + +// KWindowSystem does not translate position when mapping viewports +// to virtual desktops (it'd probably break more things than fix), +// so the offscreen coordinates need to be fixed +QRect Pager::fixViewportPosition( const QRect& r ) +{ + QRect desktopGeom = m_desktopWidget->geometry(); + int x = r.center().x() % desktopGeom.width(); + int y = r.center().y() % desktopGeom.height(); + if( x < 0 ) { + x = x + desktopGeom.width(); + } + if( y < 0 ) { + y = y + desktopGeom.height(); + } + return QRect( x - r.width() / 2, y - r.height() / 2, r.width(), r.height()); +} + + + +#include "moc_pager.cpp" diff --git a/applets/pager/plugin/pager.h b/applets/pager/plugin/pager.h new file mode 100644 index 0000000000..7de49dfb9c --- /dev/null +++ b/applets/pager/plugin/pager.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright (C) 2007 by Daniel Laidig * + * Copyright (C) 2012 by Luís Gabriel Lima * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef PAGER_H +#define PAGER_H + +#include +#include + +#include + +#include "model.h" +#include + +class QDesktopWidget; + +class KColorScheme; +class KWindowInfo; +class KCModuleProxy; + +namespace Plasma +{ + class FrameSvg; +} + +class Pager : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject* model READ model CONSTANT) + Q_PROPERTY(int currentDesktop READ currentDesktop NOTIFY currentDesktopChanged) + Q_PROPERTY(bool showWindowIcons READ showWindowIcons WRITE setShowWindowIcons NOTIFY showWindowIconsChanged) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) + Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY sizeChanged) + Q_PROPERTY(QSize preferredSize READ preferredSize NOTIFY preferredSizeChanged) + Q_PROPERTY(Pager::CurrentDesktopSelected currentDesktopSelected READ currentDesktopSelected WRITE setCurrentDesktopSelected NOTIFY currentDesktopSelectedChanged) + Q_PROPERTY(Pager::DisplayedText displayedText READ displayedText WRITE setDisplayedText NOTIFY displayedTextChanged) + + public: + enum CurrentDesktopSelected { + DoNothing = 0, + ShowDesktop, + ShowDashboard + }; + Q_ENUMS(CurrentDesktopSelected) + + enum DisplayedText { + Number = 0, + Name, + None + }; + Q_ENUMS(DisplayedText) + + Pager(QObject *parent = 0); + ~Pager(); + + //void constraintsEvent(Plasma::Constraints); + virtual QList contextualActions(); + + QObject *model() const { return m_pagerModel; } + + int currentDesktop() const { return m_currentDesktop; } + void setCurrentDesktop(int desktop); + + bool showWindowIcons() const { return m_showWindowIcons; } + void setShowWindowIcons(bool show); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + + QSizeF size() const; + void setSize(const QSizeF &size); + + QSize preferredSize() const; + + CurrentDesktopSelected currentDesktopSelected() const; + void setCurrentDesktopSelected(CurrentDesktopSelected cur); + + DisplayedText displayedText() const; + void setDisplayedText(DisplayedText disp); + + Q_INVOKABLE void moveWindow(int, double, double, int, int); + Q_INVOKABLE void changeDesktop(int desktopId); + + Q_SIGNALS: + void currentDesktopChanged(); + void showWindowIconsChanged(); + void orientationChanged(); + void sizeChanged(); + void preferredSizeChanged(); + void currentDesktopSelectedChanged(); + void displayedTextChanged(); + + public Q_SLOTS: + void recalculateGridSizes(int rows); + void updateSizes(); + void recalculateWindowRects(); + + protected Q_SLOTS: + void currentDesktopChanged(int desktop); + void currentActivityChanged(const QString &activity); + void desktopsSizeChanged(); + void numberOfDesktopsChanged(int num); + void desktopNamesChanged(); + void windowChanged(WId id, const unsigned long *dirty); + void startTimer(); + void startTimerFast(); +#if HAVE_X11 + void slotAddDesktop(); + void slotRemoveDesktop(); +#endif + + protected: + void createMenu(); + QRect fixViewportPosition( const QRect& r ); + + private: + + PagerModel *m_pagerModel; + + QTimer* m_timer; + + + DisplayedText m_displayedText; + CurrentDesktopSelected m_currentDesktopSelected; + int m_rows; + int m_columns; + int m_desktopCount; + int m_currentDesktop; + QString m_currentActivity; + qreal m_widthScaleFactor; + qreal m_heightScaleFactor; + QSizeF m_size; + QSize m_preferredSize; + Qt::Orientation m_orientation; + + //list of info about animations for each desktop + QList m_actions; + QAction *m_addDesktopAction; + QAction *m_removeDesktopAction; + + bool m_showWindowIcons : 1; + bool m_desktopDown : 1; + bool m_validSizes : 1; + + QDesktopWidget *m_desktopWidget; +#if HAVE_X11 + bool m_isX11; +#endif + }; + +#endif diff --git a/applets/pager/plugin/pagerplugin.cpp b/applets/pager/plugin/pagerplugin.cpp new file mode 100644 index 0000000000..4f55e84747 --- /dev/null +++ b/applets/pager/plugin/pagerplugin.cpp @@ -0,0 +1,29 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "pagerplugin.h" +#include "pager.h" + +#include + +void KickoffPlugin::registerTypes (const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.pager")); + qmlRegisterType(uri, 2, 0, "Pager"); +} diff --git a/applets/pager/plugin/pagerplugin.h b/applets/pager/plugin/pagerplugin.h new file mode 100644 index 0000000000..09178e4557 --- /dev/null +++ b/applets/pager/plugin/pagerplugin.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef PAGERPLUGIN_H +#define PAGERPLUGIN_H + +#include +#include + + +class KickoffPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") +public: + virtual void registerTypes(const char *uri); +}; + +#endif // PAGERPLUGIN_H diff --git a/applets/pager/plugin/qmldir b/applets/pager/plugin/qmldir new file mode 100644 index 0000000000..a4e5c44b8c --- /dev/null +++ b/applets/pager/plugin/qmldir @@ -0,0 +1 @@ +plugin pagerplugin diff --git a/applets/showActivityManager/CMakeLists.txt b/applets/showActivityManager/CMakeLists.txt new file mode 100644 index 0000000000..71ae11bc79 --- /dev/null +++ b/applets/showActivityManager/CMakeLists.txt @@ -0,0 +1 @@ +plasma_install_package(package org.kde.plasma.showActivityManager) diff --git a/applets/showActivityManager/Messages.sh b/applets/showActivityManager/Messages.sh new file mode 100644 index 0000000000..36cad89025 --- /dev/null +++ b/applets/showActivityManager/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.plasma.showActivityManager.pot +rm -f rc.cpp diff --git a/applets/showActivityManager/package/contents/ui/main.qml b/applets/showActivityManager/package/contents/ui/main.qml new file mode 100644 index 0000000000..defcdf2cb9 --- /dev/null +++ b/applets/showActivityManager/package/contents/ui/main.qml @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Gregor Taetzner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore + +MouseArea { + id: iconContainer + property string activeSource: "Status" + height: 16 + width: 16 + Layout.minimumWidth: 16 + Layout.minimumHeight: 16 + implicitWidth: units.iconSizes["panel"] + implicitHeight: implicitWidth + onClicked: { + var service = dataSource.serviceForSource(activeSource) + var operation = service.operationDescription("toggleActivityManager") + service.startOperationCall(operation) + } + + PlasmaCore.DataSource { + id: dataSource + engine: "org.kde.activities" + connectedSources: [activeSource] + } + + PlasmaCore.ToolTipArea { + id: tooltip + mainText: i18n("Show Activity Manager") + subText: i18n("Click to show the activity manager") + anchors.fill: parent + image: "preferences-activities" + } + + PlasmaCore.IconItem { + id: icon + source: "preferences-activities" + width: parent.width + height: parent.height + } +} + diff --git a/applets/showActivityManager/package/metadata.desktop b/applets/showActivityManager/package/metadata.desktop new file mode 100644 index 0000000000..28c59e60f2 --- /dev/null +++ b/applets/showActivityManager/package/metadata.desktop @@ -0,0 +1,127 @@ +[Desktop Entry] +Name=Activities +Name[ar]=الأنشطة +Name[bg]=Дейности +Name[bs]=motor aktivnosti +Name[ca]=Activitats +Name[ca@valencia]=Activitats +Name[cs]=Aktivity +Name[da]=Aktiviteter +Name[de]=Aktivitäten +Name[el]=Δραστηριότητες +Name[es]=Actividades +Name[et]=Tegevused +Name[eu]=Jarduerak +Name[fa]=فعالیتها +Name[fi]=Aktiviteetit +Name[fr]=Activités +Name[ga]=Gníomhaíochtaí +Name[gl]=Actividades +Name[he]=עילויות +Name[hr]=Aktivnosti +Name[hu]=Aktivitások +Name[ia]=Activitates +Name[is]=Virknistjóri +Name[it]=Attività +Name[ja]=アクティビティ +Name[kk]=Белсенділіктер +Name[km]=សកម្មភាព +Name[ko]=활동 +Name[lt]=Veiklos +Name[lv]=Aktivitātes +Name[mr]=कार्यपध्दती +Name[nb]=Aktiviteter +Name[nds]=Aktiviteten +Name[nl]=Activiteiten +Name[pa]=ਸਰਗਰਮੀਆਂ +Name[pl]=Działania +Name[pt]=Actividades +Name[pt_BR]=Atividades +Name[ro]=Activități +Name[ru]=Комнаты +Name[sk]=Aktivity +Name[sl]=Dejavnosti +Name[sr]=активности +Name[sr@ijekavian]=активности +Name[sr@ijekavianlatin]=aktivnosti +Name[sr@latin]=aktivnosti +Name[sv]=Aktiviteter +Name[tr]=Etkinlikler +Name[ug]=پائالىيەتلەر +Name[uk]=Простори дій +Name[vi]=Hoạt động +Name[wa]=Activités +Name[x-test]=xxActivitiesxx +Name[zh_CN]=活动 +Name[zh_TW]=活動 +Comment=Shows the activity manager +Comment[bg]=Управление на дейностите +Comment[bs]=Prikazuje menadžer aktivnosti +Comment[ca]=Mostra el gestor d'activitats +Comment[ca@valencia]=Mostra el gestor d'activitats +Comment[cs]=Zobrazí správce aktivit +Comment[da]=Vis aktivitetshåndtering +Comment[de]=Zeigt die Aktivitätenverwaltung an +Comment[el]=Εμφανίζει το διαχειριστή δραστηριοτήτων +Comment[es]=Muestra el gestor de actividades +Comment[et]=Tegevuste halduri näitamine +Comment[eu]=Jarduera-kudeatzailea erakusten du +Comment[fi]=Näyttää aktiviteettienhallinnan +Comment[fr]=Affiche le gestionnaire d'activités +Comment[ga]=Taispeáin an bainisteoir gníomhaíochta +Comment[gl]=Mostra o xestor da actividade +Comment[he]=מציג את מנהל פעילויות +Comment[hr]=Prikazuje upravitelja aktivnosti +Comment[hu]=Aktivitáskezelő megjelenítése +Comment[ia]=Monstra le gerente de activitate +Comment[is]=Birtir virknistjóra +Comment[it]=Mostra il gestore delle attività +Comment[kk]=Белсенділік менеджері көрсетеді +Comment[km]=បង្ហាញ​កម្មវិធី​គ្រប់គ្រង​សកម្មភាព​ +Comment[ko]=활동 관리자 보이기 +Comment[lt]=Rodo veiklų tvarkyklę +Comment[lv]=Rāda aktivitāšu pārvaldnieku +Comment[mr]=कार्यपध्दती व्यवस्थापक दर्शवितो +Comment[nb]=Viser aktivitetshåndtereren +Comment[nds]=Wiest den Aktivitetenpleger +Comment[nl]=Toont de activiteitenbeheerder +Comment[pa]=ਸਰਗਰਮੀ ਮੈਨੇਜਰ ਵੇਖਾਉਂਦਾ ਹੈ +Comment[pl]=Pokazuje menadżera działań +Comment[pt]=Mostra o gestor de actividades +Comment[pt_BR]=Mostra o gerenciador de atividades +Comment[ro]=Arată Gestionarul de activități +Comment[ru]=Показывает диспетчер комнат +Comment[sk]=Ukáže správcu aktivít +Comment[sl]=Prikaže upravljalnika dejavnosti +Comment[sr]=Приказује менаџер активности +Comment[sr@ijekavian]=Приказује менаџер активности +Comment[sr@ijekavianlatin]=Prikazuje menadžer aktivnosti +Comment[sr@latin]=Prikazuje menadžer aktivnosti +Comment[sv]=Visar Aktivitetshanteraren +Comment[tr]=Etkinlik yöneticisini gösterir +Comment[ug]=پائالىيەت باشقۇرغۇچنى كۆرسىتىدۇ +Comment[uk]=Показує вікно керування просторами дій +Comment[wa]=Mostrer l' manaedjeu des activités +Comment[x-test]=xxShows the activity managerxx +Comment[zh_CN]=显示活动管理器 +Comment[zh_TW]=顯示活動管理員 +Icon=preferences-activities +Type=Service + +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml + +X-Plasma-Requires-FileDialog=Unused +X-Plasma-Requires-LaunchApp=Unused + +X-KDE-PluginInfo-Author=Gregor Tätzner +X-KDE-PluginInfo-Email=plasma-devel@kde.org +X-KDE-PluginInfo-Website=http://plasma.kde.org/ +X-KDE-PluginInfo-Category=Windows and Tasks +X-KDE-PluginInfo-Name=org.kde.plasma.showActivityManager +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true + +X-KDE-ServiceTypes=Plasma/Applet diff --git a/applets/taskmanager/CMakeLists.txt b/applets/taskmanager/CMakeLists.txt new file mode 100644 index 0000000000..408d8a5bb7 --- /dev/null +++ b/applets/taskmanager/CMakeLists.txt @@ -0,0 +1,26 @@ +plasma_install_package(package org.kde.plasma.taskmanager) + +set(taskmanagerplugin_SRCS + plugin/backend.cpp + plugin/draghelper.cpp + plugin/taskmanagerplugin.cpp + plugin/textlabel.cpp +) + +install(FILES plugin/qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/taskmanager) + +add_library(taskmanagerplugin SHARED ${taskmanagerplugin_SRCS}) + +# FIXME TODO HACK: The last two are temp, see comment in Backend::activateItem(). +target_link_libraries(taskmanagerplugin + PW::LibTaskManager + Qt5::Core + Qt5::Qml + Qt5::Quick + Qt5::Widgets + KF5::KIOCore + KF5::WindowSystem + Qt5::X11Extras + ${X11_LIBRARIES}) + +install(TARGETS taskmanagerplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/private/taskmanager) diff --git a/applets/taskmanager/Messages.sh b/applets/taskmanager/Messages.sh new file mode 100644 index 0000000000..3958567d9f --- /dev/null +++ b/applets/taskmanager/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_tasks.pot +rm -f rc.cpp \ No newline at end of file diff --git a/applets/taskmanager/package/contents/code/layout.js b/applets/taskmanager/package/contents/code/layout.js new file mode 100644 index 0000000000..1e45a0dde4 --- /dev/null +++ b/applets/taskmanager/package/contents/code/layout.js @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +function horizontalMargins() { + return taskFrame.margins.left + taskFrame.margins.right; +} + +function verticalMargins() { + return taskFrame.margins.top + taskFrame.margins.bottom; +} + +function launcherLayoutTasks() { + return Math.round(backend.tasksModel.launcherCount / Math.floor(preferredMinWidth() / launcherWidth())); +} + +function launcherLayoutWidthDiff() { + return (launcherLayoutTasks() * taskWidth()) - (backend.tasksModel.launcherCount * launcherWidth()); +} + +function logicalTaskCount() { + var count = (backend.tasksModel.count - backend.tasksModel.launcherCount) + launcherLayoutTasks(); + + return Math.max(backend.tasksModel.count ? 1 : 0, count); +} + +function maxStripes() { + var length = tasks.vertical ? taskList.width : taskList.height; + var minimum = tasks.vertical ? preferredMinWidth() : preferredMinHeight(); + + return Math.min(plasmoid.configuration.maxStripes, Math.max(1, Math.floor(length / minimum))); +} + +function tasksPerStripe() { + if (plasmoid.configuration.forceStripes) { + return Math.ceil(logicalTaskCount() / maxStripes()); + } else { + var length = tasks.vertical ? taskList.height : taskList.width; + var minimum = tasks.vertical ? preferredMinHeight() : preferredMinWidth(); + + return Math.floor(length / minimum); + } +} + +function calculateStripes() { + var stripes = plasmoid.configuration.forceStripes ? plasmoid.configuration.maxStripes : Math.min(plasmoid.configuration.maxStripes, Math.ceil(logicalTaskCount() / tasksPerStripe())); + + return Math.min(stripes, maxStripes()); +} + +function full() { + return (maxStripes() == calculateStripes()); +} + +function optimumCapacity(width, height) { + var length = tasks.vertical ? height : width - (backend.tasksModel.launcherCount * launcherWidth()); + var maximum = tasks.vertical ? preferredMaxHeight() : preferredMaxWidth(); + + return (Math.ceil(length / maximum) * maxStripes()) + backend.tasksModel.launcherCount; +} + +function layoutWidth() { + if (plasmoid.configuration.forceStripes && !tasks.vertical) { + return Math.min(tasks.width, Math.max(preferredMaxWidth(), tasksPerStripe() * preferredMaxWidth())); + } else { + return tasks.width; + } +} + +function layoutHeight() { + if (plasmoid.configuration.forceStripes && tasks.vertical) { + return Math.min(tasks.height, Math.max(preferredMaxHeight(), tasksPerStripe() * preferredMaxHeight())); + } else { + return tasks.height; + } +} + +function preferredMinWidth() { + if (tasks.vertical) { + return horizontalMargins() + units.iconSizes.small; + } else { + return horizontalMargins() + units.iconSizes.small + 3 + (theme.mSize(theme.defaultFont).width * 12); + } +} + +function preferredMaxWidth() { + return Math.floor(preferredMinWidth() * 1.8); +} + +function preferredMinHeight() { + // TODO FIXME UPSTREAM: Port to proper font metrics for descenders once we have access to them. + return theme.mSize(theme.defaultFont).height + 4; +} + +function preferredMaxHeight() { + return verticalMargins() + Math.min(units.iconSizes.small * 3, theme.mSize(theme.defaultFont).height * 3); +} + +function taskWidth() { + if (tasks.vertical) { + return Math.floor(taskList.width / calculateStripes()); + } else { + if (full() && Math.max(1, logicalTaskCount()) > tasksPerStripe()) { + return Math.floor(taskList.width / Math.ceil(logicalTaskCount() / maxStripes())); + } else { + return Math.min(preferredMaxWidth(), Math.floor(taskList.width / Math.min(logicalTaskCount(), tasksPerStripe()))); + } + } +} + +function taskHeight() { + if (tasks.vertical) { + if (full() && Math.max(1, logicalTaskCount()) > tasksPerStripe()) { + return Math.floor(taskList.height / Math.ceil(logicalTaskCount() / maxStripes())); + } else { + return Math.min(preferredMaxHeight(), Math.floor(taskList.height / Math.min(logicalTaskCount(), tasksPerStripe()))); + } + } else { + return Math.floor(taskList.height / calculateStripes()); + } +} + +function launcherWidth() { + return horizontalMargins() + units.iconSizes.small; +} + +function layout(container) { + var item; + var stripes = calculateStripes(); + var taskCount = backend.tasksModel.count - backend.tasksModel.launcherCount; + var width = taskWidth(); + var adjustedWidth = width; + var height = taskHeight(); + + if (!tasks.vertical && stripes == 1 && taskCount) + { + width = Math.min(width + Math.floor(launcherLayoutWidthDiff() / taskCount), preferredMaxWidth()); + } + + for (var i = 0; i < container.count; ++i) { + item = container.itemAt(i); + item.visible = true; + + adjustedWidth = width; + + if (!tasks.vertical) { + if (item.isLauncher) { + adjustedWidth = launcherWidth(); + } else if (stripes > 1 && i == backend.tasksModel.launcherCount) { + adjustedWidth += launcherLayoutWidthDiff(); + } + } + + item.width = adjustedWidth; + item.height = height; + } +} diff --git a/applets/taskmanager/package/contents/code/tools.js b/applets/taskmanager/package/contents/code/tools.js new file mode 100644 index 0000000000..2ddd75a66e --- /dev/null +++ b/applets/taskmanager/package/contents/code/tools.js @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +function activateNextPrevTask(parentItem, next) { + var taskIdList; + + if (parentItem && parentItem.isGroupParent) { + taskIdList = backend.tasksModel.taskIdList(visualModel.modelIndex(parentItem.itemIndex)); + } else { + taskIdList = backend.tasksModel.taskIdList(); + } + + if (!taskIdList.length) { + return; + } + + var activeTaskId = backend.tasksModel.activeTaskId(); + var target = taskIdList[0]; + + for (var i = 0; i < taskIdList.length; ++i) { + if (taskIdList[i] == activeTaskId) + { + if (next && i < (taskIdList.length - 1)) { + target = taskIdList[i + 1]; + } else if (!next) + { + if (i) { + target = taskIdList[i - 1]; + } else { + target = taskIdList[taskIdList.length - 1]; + } + } + + break; + } + } + + activateItem(target, false); +} + +function insertionIndexAt(sourceIndex, x, y) { + var above = target.childAt(x, y); + + if (above) { + var index = above.itemIndex; + + if (index > sourceIndex) { + ++index; + } + + return index; + } else { + var distance = tasks.vertical ? x : y; + var step = tasks.vertical ? Layout.taskWidth() : Layout.taskHeight(); + var stripe = Math.ceil(distance / step); + + if (stripe == Layout.calculateStripes()) { + return -1; + } else { + return stripe * Layout.tasksPerStripe(); + } + } +} + +function publishIconGeometries(taskItems) { + for (var i = 0; i < taskItems.length - 1; ++i) { + var task = taskItems[i]; + + if (task.isGroupParent) { + var taskIdList = backend.tasksModel.taskIdList(visualModel.modelIndex(task.itemIndex)); + + for (var j = 0; j < taskIdList.length; ++j) { + tasks.itemGeometryChanged(task, taskIdList[j].itemId); + } + } else if (!task.isLauncher) { + tasks.itemGeometryChanged(task, task.itemId); + } + } +} + +function taskPrefix(prefix) { + + var effectivePrefix; + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + effectivePrefix = "west-" + prefix; + break; + case PlasmaCore.Types.TopEdge: + effectivePrefix = "north-" + prefix; + break; + case PlasmaCore.Types.RightEdge: + effectivePrefix = "east-" + prefix; + break; + default: + effectivePrefix = "south-" + prefix; + } + if (!frame.hasElementPrefix(effectivePrefix)) { + return prefix; + } + return effectivePrefix; + +} diff --git a/applets/taskmanager/package/contents/config/config.qml b/applets/taskmanager/package/contents/config/config.qml new file mode 100644 index 0000000000..0776fda9e1 --- /dev/null +++ b/applets/taskmanager/package/contents/config/config.qml @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: "General" + icon: "preferences-system-windows" + source: "ConfigGeneral.qml" + } +} diff --git a/applets/taskmanager/package/contents/config/main.xml b/applets/taskmanager/package/contents/config/main.xml new file mode 100644 index 0000000000..1a8fe8a0a9 --- /dev/null +++ b/applets/taskmanager/package/contents/config/main.xml @@ -0,0 +1,48 @@ + + + + + + + true + + + false + + + true + + + false + + + 2 + + + true + + + 2 + + + 2 + + + false + + + true + + + false + + + + + + + + diff --git a/applets/taskmanager/package/contents/ui/ConfigGeneral.qml b/applets/taskmanager/package/contents/ui/ConfigGeneral.qml new file mode 100644 index 0000000000..c5ef053d83 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/ConfigGeneral.qml @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 as QtLayouts + +QtControls.GroupBox { + width: childrenRect.width + height: childrenRect.height + + flat: true + title: i18n("General") + + property alias cfg_forceStripes: forceStripes.checked + property alias cfg_showToolTips: showToolTips.checked + property alias cfg_highlightWindows: highlightWindows.checked + property alias cfg_maxStripes: maxStripes.value + property alias cfg_groupingStrategy: groupingStrategy.currentIndex + property alias cfg_onlyGroupWhenFull: onlyGroupWhenFull.checked + property alias cfg_sortingStrategy: sortingStrategy.currentIndex + property alias cfg_showOnlyCurrentScreen: showOnlyCurrentScreen.checked + property alias cfg_showOnlyCurrentDesktop: showOnlyCurrentDesktop.checked + property alias cfg_showOnlyCurrentActivity: showOnlyCurrentActivity.checked + property alias cfg_showOnlyMinimized: showOnlyMinimized.checked + + QtLayouts.ColumnLayout { + QtControls.GroupBox { + title: i18n("Appearance") + flat: true + + QtLayouts.ColumnLayout { + QtControls.CheckBox { + id: forceStripes + text: i18n("Force row settings") + } + + QtControls.CheckBox { + id: showToolTips + text: i18n("Show tooltips") + } + + QtControls.CheckBox { + id: highlightWindows + text: i18n("Highlight windows") + } + + QtLayouts.RowLayout { + QtControls.Label { + text: i18n("Maximum rows:") + } + + QtControls.SpinBox { + id: maxStripes + } + } + } + } + + QtControls.GroupBox { + title: i18n("Grouping and Sorting") + flat: true + + QtLayouts.ColumnLayout { + QtLayouts.RowLayout { + QtControls.Label { + text: i18n("Grouping:") + } + + QtControls.ComboBox { + id: groupingStrategy + model: [i18n("Do Not Group"), i18n("Manually"), i18n("By Program Name")] + } + } + + QtControls.CheckBox { + id: onlyGroupWhenFull + text: i18n("Only when the task manager is full") + } + + QtLayouts.RowLayout { + QtControls.Label { + text: i18n("Sorting:") + } + + QtControls.ComboBox { + id: sortingStrategy + model: [i18n("Do Not Sort"), i18n("Manually"), i18n("Alphabetically"), i18n("By Desktop"), i18n("By Activity")] + } + } + } + } + + QtControls.GroupBox { + title: i18n("Filters") + flat: true + + QtLayouts.ColumnLayout { + QtControls.CheckBox { + id: showOnlyCurrentScreen + text: i18n("Show only tasks from the current screen") + } + + QtControls.CheckBox { + id: showOnlyCurrentDesktop + text: i18n("Show only tasks from the current desktop") + } + + QtControls.CheckBox { + id: showOnlyCurrentActivity + text: i18n("Show only tasks from the current activity") + } + + QtControls.CheckBox { + id: showOnlyMinimized + text: i18n("Show only tasks that are minimized") + } + } + } + } +} diff --git a/applets/taskmanager/package/contents/ui/GroupDialog.qml b/applets/taskmanager/package/contents/ui/GroupDialog.qml new file mode 100644 index 0000000000..cf66b8c279 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/GroupDialog.qml @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.draganddrop 2.0 + +import "../code/layout.js" as LayoutManager + +PlasmaCore.Dialog { + visible: false + + type: PlasmaCore.Dialog.PopupMenu + flags: Qt.WindowStaysOnTopHint + hideOnWindowDeactivate: true + location: plasmoid.location + + mainItem: Item { + MouseHandler { + anchors.fill: parent + + id: mouseHandler + + target: taskList + } + + TaskList { + id: taskList + + anchors.fill: parent + + Repeater { + id: groupRepeater + + onCountChanged: updateSize() + } + } + } + + data: [ + VisualDataModel { + id: groupFilter + + delegate: Task { + visible: true + inPopup: true + } + } + ] + + onVisualParentChanged: { + if (!visualParent) { + visible = false; + } + } + + onVisibleChanged: { + if (visible && visualParent) { + groupFilter.model = backend.tasksModel; + groupFilter.rootIndex = groupFilter.modelIndex(visualParent.itemIndex); + groupRepeater.model = groupFilter; + } else { + visualParent = null; + groupRepeater.model = undefined; + groupFilter.model = undefined; + groupFilter.rootIndex = undefined; + } + } + + function updateSize() { + if (!visible || !visualParent) { + return; + } + + if (!groupRepeater.count) { + visible = false; + } else { + var task; + var maxWidth = 0; + + for (var i = 0; i < taskList.children.length - 1; ++i) { + task = taskList.children[i]; + + if (task.textWidth > maxWidth) { + maxWidth = task.textWidth; + } + + task.textWidthChanged.connect(updateSize); + } + + maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.small + 6; + + // TODO: Properly derive limits from work area size (screen size sans struts). + mainItem.width = Math.min(maxWidth, (tasks.vertical ? 640 - tasks.width : Math.max(tasks.width, 640)) - 20); + mainItem.height = groupRepeater.count * (LayoutManager.verticalMargins() + units.iconSizes.small); + } + } +} diff --git a/applets/taskmanager/package/contents/ui/GroupExpanderOverlay.qml b/applets/taskmanager/package/contents/ui/GroupExpanderOverlay.qml new file mode 100644 index 0000000000..0c23098344 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/GroupExpanderOverlay.qml @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.SvgItem { + id: arrow + anchors { + bottom: { + if (plasmoid.location != PlasmaCore.Types.TopEdge && plasmoid.location != PlasmaCore.Types.LeftEdge && plasmoid.location != PlasmaCore.Types.RightEdge) { + return parent.bottom; + } else { + return undefined; + } + } + horizontalCenter: { + if (plasmoid.location != PlasmaCore.Types.LeftEdge && plasmoid.location != PlasmaCore.Types.RightEdge) { + return iconBox.horizontalCenter; + } else { + return undefined; + } + } + verticalCenter: { + if (plasmoid.location == PlasmaCore.Types.LeftEdge || plasmoid.location == PlasmaCore.Types.RightEdge) { + return iconBox.verticalCenter; + } else { + return undefined; + } + } + top: { + if (plasmoid.location == PlasmaCore.Types.TopEdge) { + return parent.top; + } else { + return undefined; + } + } + left: { + if (plasmoid.location == PlasmaCore.Types.LeftEdge) { + return parent.left; + } else { + return undefined; + } + } + right: { + if (plasmoid.location == PlasmaCore.Types.RightEdge) { + return parent.right; + } else { + return undefined; + } + } + } + + implicitWidth: Math.min(units.iconSizes.small, iconBox.width) + implicitHeight: implicitWidth + + svg: arrows + elementId: elementForLocation() + + function elementForLocation() { + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + return "right-arrow"; + case PlasmaCore.Types.TopEdge: + return "down-arrow"; + case PlasmaCore.Types.RightEdge: + return "left-arrow"; + case PlasmaCore.Types.BottomEdge: + default: + return "up-arrow"; + } + } + + Connections { + target: plasmoid.configuration + onLocationChanged: { + arrow.width = arrow.implicitWidth + arrow.height = arrow.implicitHeight + } + } +} diff --git a/applets/taskmanager/package/contents/ui/MouseHandler.qml b/applets/taskmanager/package/contents/ui/MouseHandler.qml new file mode 100644 index 0000000000..124e4c9ba4 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/MouseHandler.qml @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +import org.kde.draganddrop 2.0 +import org.kde.kquickcontrolsaddons 2.0 + +import "../code/layout.js" as LayoutManager +import "../code/tools.js" as TaskTools + +Item { + property Item target + + DropArea { + id: dropHandler + + anchors.fill: parent + + property Item hoveredItem + + onDragMove: { + if (target.animating) { + return; + } + + var above = target.childAt(event.x, event.y); + + // HACK: plasmoid.configuration.sortingStrategy is an integer representation + // of the TaskManager::GroupManager::TaskSortingStrategy enum. + if (tasks.dragSource && plasmoid.configuration.sortingStrategy == 1) { + if (tasks.dragSource != above && !tasks.dragSource.isLauncher + && !(above && "isLauncher" in above && above.isLauncher)) { + itemMove(tasks.dragSource.itemId, + TaskTools.insertionIndexAt(tasks.dragSource.itemIndex, + event.x, event.y)); + } + } else if (!tasks.dragSource && above && hoveredItem != above) { + hoveredItem = above; + activationTimer.restart(); + } else if (!above) { + hoveredItem = null; + activationTimer.stop(); + } + } + + onDragLeave: { + hoveredItem = null; + activationTimer.stop(); + } + + Timer { + id: activationTimer + + interval: 250 + repeat: false + + onTriggered: { + if (parent.hoveredItem.isGroupParent) { + groupDialog.target = parent.hoveredItem; + groupDialog.visible = true; + } else if (!parent.hoveredItem.isLauncher) { + tasks.activateItem(parent.hoveredItem.itemId, false); + } + } + } + } + + MouseEventListener { + id: wheelHandler + + anchors.fill: parent + + onWheelMoved: TaskTools.activateNextPrevTask(false, wheel.delta < 0) + } +} diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml new file mode 100644 index 0000000000..56a1f05372 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/Task.qml @@ -0,0 +1,340 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.draganddrop 2.0 +import org.kde.kquickcontrolsaddons 2.0 + +import org.kde.plasma.private.taskmanager 0.1 as TaskManager + +import "../code/layout.js" as LayoutManager +import "../code/tools.js" as TaskTools + +MouseEventListener { + id: task + + width: groupDialog.mainItem.width + height: units.iconSizes.small + LayoutManager.verticalMargins() + + visible: false + + LayoutMirroring.enabled: (Qt.application.layoutDirection == Qt.RightToLeft) + LayoutMirroring.childrenInherit: (Qt.application.layoutDirection == Qt.RightToLeft) + + property int itemIndex: index + property int itemId: model.Id + property bool inPopup: false + property bool isGroupParent: model.hasModelChildren + property bool isLauncher: model.IsLauncher + property bool isStartup: model.IsStartup + property bool demandsAttention: model.DemandsAttention + property int textWidth: label.implicitWidth + property bool pressed: false + property int pressX: -1 + property int pressY: -1 + property Item busyIndicator + + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + + onItemIndexChanged: { + if (!inPopup && !tasks.vertical && LayoutManager.calculateStripes() > 1) { + var newWidth = LayoutManager.taskWidth(); + + if (index == backend.tasksModel.launcherCount) { + newWidth += LayoutManager.launcherLayoutWidthDiff(); + } + + width = newWidth; + } + } + + onIsStartupChanged: { + if (!isStartup) { + tasks.itemGeometryChanged(itemId, x, y, width, height); + } + } + + onDemandsAttentionChanged: { + tasks.updateStatus(demandsAttention); + } + + onContainsMouseChanged: { + if (!containsMouse) { + pressed = false; + } + + tasks.itemHovered(model.Id, containsMouse); + } + + onPressed: { + if (mouse.buttons & Qt.LeftButton) { + pressed = true; + pressX = mouse.x; + pressY = mouse.y; + } else if (mouse.buttons & Qt.RightButton) { + if (plasmoid.configuration.showToolTips) { + toolTip.hideToolTip(); + } + + tasks.itemContextMenu(task, plasmoid.action("configure")); + } + } + + onReleased: { + if (pressed) { + if (!mouse.button == Qt.LeftButton) { + return; + } + + if (isGroupParent) { + if (groupDialog.visible) { + groupDialog.visible = false; + } else { + groupDialog.visualParent = task; + groupDialog.visible = true; + } + } else { + tasks.activateItem(model.Id, true); + } + } + + pressed = false; + pressX = -1; + pressY = -1; + } + + onPositionChanged: { + if (pressX != -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { + tasks.dragSource = task; + dragHelper.startDrag(task, model.MimeType, model.MimeData, + model.LauncherUrl, model.DecorationRole); + pressX = -1; + pressY = -1; + + return; + } + } + + onWheelMoved: TaskTools.activateNextPrevTask(task, wheel.delta < 0) + + PlasmaCore.FrameSvgItem { + id: frame + + anchors.fill: parent + + imagePath: "widgets/tasks" + prefix: TaskTools.taskPrefix("normal") + + PlasmaCore.ToolTipArea { + id: toolTip + + anchors.fill: parent + + property variant windows: model.WindowList + + active: !inPopup && plasmoid.configuration.showToolTips + interactive: true + location: plasmoid.location + + mainItem: toolTipDelegate + + //FIXME TODO: highlightWindows: plasmoid.configuration.highlightWindows + onContainsMouseChanged: { + if (containsMouse) { + toolTip.windows = model.WindowList; + toolTip.mainText = model.DisplayRole; + toolTip.icon = model.DecorationRole; + toolTip.subText = model.IsLauncher ? model.GenericName + : toolTip.generateSubText(model); + } + } + + function generateSubText(task) { + var subTextEntries = new Array(); + + if (!plasmoid.configuration.showOnlyCurrentDesktop) { + subTextEntries.push(i18n("On %1", task.DesktopName)); + } + + if (task.OnAllActivities) { + subTextEntries.push(i18nc("Which virtual desktop a window is currently on", + "Available on all activities")); + } else if (plasmoid.configuration.showOnlyCurrentActivity) { + if (task.OtherActivityNames.length > 0) { + subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", + "Also available on %1", + task.OtherActivityNames.join(", "))); + } + } else if (task.ActivityNames.length > 0) { + subTextEntries.push(i18nc("Which activities a window is currently on", + "Available on %1", + task.ActivityNames.join(", "))); + } + + return subTextEntries.join("
    "); + } + } + } + + Item { + id: iconBox + + anchors { + left: parent.left + leftMargin: taskFrame.margins.left + verticalCenter: parent.verticalCenter + } + + width: inPopup ? units.iconSizes.small : Math.min(height, parent.width - LayoutManager.horizontalMargins()) + height: Math.min(units.iconSizes.huge, + parent.height - (parent.height - LayoutManager.verticalMargins() < units.iconSizes.small ? + Math.min(9, LayoutManager.verticalMargins()) : LayoutManager.verticalMargins())) + + PlasmaCore.IconItem { + id: icon + + anchors.fill: parent + + visible: !model.IsStartup + + active: task.containsMouse + enabled: !(model.Minimized && !plasmoid.configuration.showOnlyMinimized) + + source: model.DecorationRole + + onVisibleChanged: { + if (visible && busyIndicator) { + busyIndicator.destroy(); + } + } + } + + states: [ + // Using a state transition avoids a binding loop between label.visible and + // the text label margin, which derives from the icon width. + State { + name: "standalone" + when: !label.visible + + AnchorChanges { + target: iconBox + anchors.left: undefined + anchors.horizontalCenter: parent.horizontalCenter + } + + PropertyChanges { + target: iconBox + anchors.leftMargin: 0 + } + } + ] + } + + TaskManager.TextLabel { + id: label + + anchors { + left: parent.left + right: parent.right + leftMargin: taskFrame.margins.left + icon.width + 4 + rightMargin: taskFrame.margins.right + verticalCenter: parent.verticalCenter + } + + height: Math.max(theme.mSize(theme.defaultFont).height, parent.height - taskFrame.margins.top - taskFrame.margins.bottom) + + visible: !model.IsLauncher && (parent.width - anchors.leftMargin - anchors.rightMargin) >= (theme.mSize(theme.defaultFont).width * 3) + + enabled: !model.Minimized + + text: model.DisplayRole + color: theme.textColor + elide: !inPopup + } + + states: [ + State { + name: "launcher" + when: model.IsLauncher + + PropertyChanges { + target: frame + prefix: "" + } + }, + State { + name: "hovered" + when: containsMouse + + PropertyChanges { + target: frame + prefix: TaskTools.taskPrefix("hover"); + } + }, + State { + name: "attention" + when: model.DemandsAttention + + PropertyChanges { + target: frame + prefix: TaskTools.taskPrefix("attention"); + } + }, + State { + name: "minimized" + when: model.Minimized && !(groupDialog.visible && groupDialog.target == task) + + PropertyChanges { + target: frame + prefix: TaskTools.taskPrefix("minimized"); + } + }, + State { + name: "active" + when: model.Active || groupDialog.visible && groupDialog.target == task + + PropertyChanges { + target: frame + prefix: TaskTools.taskPrefix("focus"); + } + } + ] + + Component.onCompleted: { + if (model.IsStartup) { + busyIndicator = Qt.createQmlObject('import org.kde.plasma.components 2.0 as PlasmaComponents; ' + + 'PlasmaComponents.BusyIndicator { anchors.fill: parent; running: true }', iconBox); + } + + if (model.hasModelChildren) { + var component = Qt.createComponent("GroupExpanderOverlay.qml"); + component.createObject(task); + } + } +/* + Component.onDestruction: { + if (groupDialog.visible && groupDialog.groupItemId == model.Id) { + groupDialog.visible = false; + } + } +*/ +} diff --git a/applets/taskmanager/package/contents/ui/TaskList.qml b/applets/taskmanager/package/contents/ui/TaskList.qml new file mode 100644 index 0000000000..5572966709 --- /dev/null +++ b/applets/taskmanager/package/contents/ui/TaskList.qml @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 + +Flow { + property bool animating: false + + layoutDirection: Qt.application.layoutDirection + + property int rows: Math.floor(height / children[0].height) + property int columns: Math.floor(width / children[0].width) + + move: Transition { + SequentialAnimation { + PropertyAction { target: taskList; property: "animating"; value: true } + + NumberAnimation { + properties: "x, y" + easing.type: Easing.OutQuad + } + + PropertyAction { target: taskList; property: "animating"; value: false } + } + } +} diff --git a/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml b/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml new file mode 100644 index 0000000000..1f5cf34acd --- /dev/null +++ b/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml @@ -0,0 +1,111 @@ +/* +* Copyright 2013 by Sebastian Kügler +* Copyright 2014 by Martin Gräßlin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, 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 Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. +*/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +Column { + id: tooltipContentItem + + property Item toolTip + + property int preferredTextWidth: theme.mSize(theme.defaultFont).width * 40 + property int _s: units.largeSpacing / 2 + + Layout.minimumWidth: childrenRect.width + _s + Layout.minimumHeight: childrenRect.height + units.largeSpacing + Layout.maximumWidth: childrenRect.width + _s + Layout.maximumHeight: childrenRect.height + units.largeSpacing + width: childrenRect.width + _s + height: childrenRect.height + units.largeSpacing + spacing: units.largeSpacing + + Row { + anchors.horizontalCenter: parent.horizontalCenter + width: childrenRect.width + height: childrenRect.height + spacing: units.largeSpacing + Repeater { + model: toolTip.windows + PlasmaCore.WindowThumbnail { + // hardcoded values from old implementation + width: 200 + height: 150 + winId: modelData + } + } + } + + Row { + width: childrenRect.width + _s + height: childrenRect.height + units.largeSpacing + spacing: units.largeSpacing + + Item { + id: imageContainer + visible: toolTip != null && toolTip.icon != null + width: tooltipIcon.width + height: tooltipIcon.height + x: _s + y: _s + + PlasmaCore.IconItem { + id: tooltipIcon + x: _s + width: toolTip != undefined && toolTip.icon != null ? units.iconSizes.desktop : 0 + height: width + source: toolTip != undefined && toolTip.icon != null ? toolTip.icon : "" + } + } + + Column { + id: mainColumn + x: _s + y: _s + + //This instance is purely for metrics + PlasmaExtras.Heading { + id: tooltipMaintextPlaceholder + visible: false + level: 3 + text: toolTip ? toolTip.mainText : "" + } + PlasmaExtras.Heading { + id: tooltipMaintext + level: 3 + width: Math.min(tooltipMaintextPlaceholder.width, preferredTextWidth) + //width: 400 + elide: Text.ElideRight + text: toolTip ? toolTip.mainText : "" + } + PlasmaComponents.Label { + id: tooltipSubtext + width: tooltipContentItem.preferredTextWidth + wrapMode: Text.WordWrap + text: toolTip ? toolTip.subText : "" + opacity: 0.5 + } + } + } +} diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml new file mode 100644 index 0000000000..229c3dfc2b --- /dev/null +++ b/applets/taskmanager/package/contents/ui/main.qml @@ -0,0 +1,236 @@ +/*************************************************************************** + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.plasmoid 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +import org.kde.plasma.private.taskmanager 0.1 as TaskManager + +import "../code/layout.js" as LayoutManager +import "../code/tools.js" as TaskTools + +Item { + id: tasks + + anchors.fill: parent + + property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) + + Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation + + Layout.fillWidth: true + Layout.fillHeight:true + Layout.minimumWidth: tasks.vertical ? 0 : LayoutManager.preferredMinWidth() + Layout.minimumHeight: !tasks.vertical ? 0 : LayoutManager.preferredMinHeight() + +//BEGIN TODO: this is not precise enough: launchers are smaller than full tasks + Layout.preferredWidth: tasks.vertical ? units.gridUnit * 10 : Math.min(taskList.columns, taskList.children.length) * units.gridUnit * 10; + Layout.preferredHeight: tasks.vertical ? Math.min(taskList.rows, taskList.children.length) * units.gridUnit * 2 : units.gridUnit * 2; +//END TODO + + property Item dragSource: null + + signal activateItem(int id, bool toggle) + signal itemContextMenu(Item item, QtObject configAction) + signal itemHovered(int id, bool hovered) + signal itemMove(int id, int newIndex) + signal itemGeometryChanged(Item item, int id) + + onWidthChanged: { + taskList.width = LayoutManager.layoutWidth(); + + if (plasmoid.configuration.forceStripes) { + taskList.height = LayoutManager.layoutHeight(); + } + } + + onHeightChanged: { + if (plasmoid.configuration.forceStripes) { + taskList.width = LayoutManager.layoutWidth(); + } + + taskList.height = LayoutManager.layoutHeight(); + } + + TaskManager.Backend { + id: backend + + taskManagerItem: tasks + highlightWindows: plasmoid.configuration.highlightWindows + + groupingStrategy: plasmoid.configuration.groupingStrategy + sortingStrategy: plasmoid.configuration.sortingStrategy + + onLaunchersChanged: plasmoid.configuration.launchers = launchers + + Component.onCompleted: { + launchers = plasmoid.configuration.launchers; + } + } + + Connections { + target: plasmoid.configuration + onLaunchersChanged: backend.launchers = plasmoid.configuration.launchers + } + + Binding { + target: backend.groupManager + property: "screen" + value: plasmoid.screen + } + + Binding { + target: backend.groupManager + property: "onlyGroupWhenFull" + value: plasmoid.configuration.onlyGroupWhenFull + } + + Binding { + target: backend.groupManager + property: "fullLimit" + value: LayoutManager.optimumCapacity(width, height) + 1 + } + + Binding { + target: backend.groupManager + property: "showOnlyCurrentScreen" + value: plasmoid.configuration.showOnlyCurrentScreen + } + + Binding { + target: backend.groupManager + property: "showOnlyCurrentDesktop" + value: plasmoid.configuration.showOnlyCurrentDesktop + } + + Binding { + target: backend.groupManager + property: "showOnlyCurrentActivity" + value: plasmoid.configuration.showOnlyCurrentActivity + } + + Binding { + target: backend.groupManager + property: "showOnlyMinimized" + value: plasmoid.configuration.showOnlyMinimized + } + + TaskManager.DragHelper { id: dragHelper } + + PlasmaCore.FrameSvgItem { + id: taskFrame + + visible: false; + + imagePath: "widgets/tasks"; + prefix: "normal" + } + + PlasmaCore.Svg { + id: arrows + + imagePath: "widgets/arrows" + size: "16x16" + } + + MouseHandler { + id: mouseHandler + + anchors.fill: parent + + target: taskList + } + + VisualDataModel { + id: visualModel + + model: backend.tasksModel + delegate: Task {} + } + + ToolTipDelegate { + id: toolTipDelegate + } + + TaskList { + id: taskList + + anchors { + left: parent.left + top: parent.top + } + + onWidthChanged: LayoutManager.layout(taskRepeater) + onHeightChanged: LayoutManager.layout(taskRepeater) + + flow: tasks.vertical ? Flow.TopToBottom : Flow.LeftToRight + + onAnimatingChanged: { + if (!animating) { + TaskTools.publishIconGeometries(children); + } + } + + Repeater { + id: taskRepeater + + model: visualModel + + onCountChanged: { + taskList.width = LayoutManager.layoutWidth(); + taskList.height = LayoutManager.layoutHeight(); + LayoutManager.layout(taskRepeater); + } + + function modelWasReset() { + LayoutManager.layout(taskRepeater); + } + + Component.onCompleted: { + backend.tasksModel.modelReset.connect(modelWasReset); + } + } + } + + GroupDialog { id: groupDialog } + + function updateStatus(demandsAttention) { + if (demandsAttention) { + plasmoid.status = PlasmaCore.Types.NeedsAttentionStatus; + } else if (!backend.anyTaskNeedsAttention) { + plasmoid.status = PlasmaCore.Types.PassiveStatus; + } + } + + function resetDragSource() { + dragSource = null; + } + + Component.onCompleted: { + tasks.activateItem.connect(backend.activateItem); + tasks.itemContextMenu.connect(backend.itemContextMenu); + tasks.itemHovered.connect(backend.itemHovered); + tasks.itemMove.connect(backend.itemMove); + tasks.itemGeometryChanged.connect(backend.itemGeometryChanged); + dragHelper.dropped.connect(resetDragSource); + } +} diff --git a/applets/taskmanager/package/metadata.desktop b/applets/taskmanager/package/metadata.desktop new file mode 100644 index 0000000000..35fe84ab89 --- /dev/null +++ b/applets/taskmanager/package/metadata.desktop @@ -0,0 +1,172 @@ +[Desktop Entry] +Name=Task Manager +Name[af]=Taakbestuurder +Name[ar]=مدير المهام +Name[ast]=Xestor de xeres +Name[be]=Кіраванне заданнямі +Name[be@latin]=Kiraŭnik zadańniaŭ +Name[bg]=Управление на задачи +Name[bn]=টাস্ক ম্যানেজার +Name[bs]=menadžer zadataka +Name[ca]=Gestor de tasques +Name[ca@valencia]=Gestor de tasques +Name[cs]=Správce úloh +Name[csb]=Menedżer dzejaniów +Name[da]=Opgavelinje +Name[de]=Fensterleiste +Name[el]=Διαχειριστής εργασιών +Name[en_GB]=Task Manager +Name[eo]=Taskadministrilo +Name[es]=Gestor de tareas +Name[et]=Tegumihaldur +Name[eu]=Ataza-kudeatzailea +Name[fi]=Tehtävienhallinta +Name[fr]=Gestionnaire de tâches +Name[fy]=Taakbehearder +Name[ga]=Bainisteoir na dTascanna +Name[gl]=Xestor de tarefas +Name[gu]=ટાસ્ક વ્યવસ્થાપક +Name[he]=מנהל משימות +Name[hi]=कार्य प्रबंधक +Name[hne]=काम प्रबंधक +Name[hr]=Upravljanje zadacima +Name[hsb]=Rjadowar nadawkow +Name[hu]=Feladatkezelő +Name[ia]=Gerente de carga +Name[id]=Manajer Tugas +Name[is]=Verkefnastjóri +Name[it]=Gestore dei processi +Name[ja]=タスクマネージャ +Name[kk]=Тапсырмалар менеджері +Name[km]=កម្មវិធី​គ្រប់គ្រង​ភារកិច្ច +Name[kn]=ಕಾರ್ಯ ವ್ಯವಸ್ಥಾಪಕ (ಟಾಸ್ಕ್ ಮ್ಯಾನೇಜರ್) +Name[ko]=작업 관리자 +Name[lt]=Užduočių tvarkyklė +Name[lv]=Uzdevumu pārvaldnieks +Name[mk]=Менаџер на апликации +Name[ml]=ടാസ്ക് മാനേജര്‍ +Name[mr]=कार्य व्यवस्थापक +Name[nb]=Oppgavebehandler +Name[nds]=Opgavenpleger +Name[ne]=कार्य प्रबन्धक +Name[nl]=Takenbeheer +Name[nn]=Oppgåvehandsamar +Name[or]=କାର୍ଯ୍ୟ ପରିଚାଳକ +Name[pa]=ਟਾਸਕ ਮੈਨੇਜਰ +Name[pl]=Zarządzanie zadaniami +Name[pt]=Gestor de Tarefas +Name[pt_BR]=Gerenciador de tarefas +Name[ro]=Gestionar de sarcini +Name[ru]=Панель задач +Name[se]=Bargogieđahalli +Name[si]=කාර්‍යය කළමණාකරු +Name[sk]=Správca úloh +Name[sl]=Upravljalnik opravil +Name[sr]=менаџер задатака +Name[sr@ijekavian]=менаџер задатака +Name[sr@ijekavianlatin]=menadžer zadataka +Name[sr@latin]=menadžer zadataka +Name[sv]=Aktivitetshanterare +Name[ta]=பணி மேலாளர் +Name[te]=కర్తవ్య నిర్వాహకి +Name[tg]=Мудири вазифаҳо +Name[th]=จัดการงาน +Name[tr]=Görev Yöneticisi +Name[ug]=ۋەزىپە باشقۇرغۇچ +Name[uk]=Менеджер задач +Name[uz]=Vazifa boshqaruvchisi +Name[uz@cyrillic]=Вазифа бошқарувчиси +Name[wa]=Manaedjeu des bouyes +Name[x-test]=xxTask Managerxx +Name[zh_CN]=任务管理器 +Name[zh_TW]=工作管理員 +Comment=Switch between running applications +Comment[ar]=بدّل بين التطبيقات التي تعمل +Comment[ast]=Camudar ente aplicaciones n'execución +Comment[be@latin]=Pieraklučeńnie dziejnych aplikacyjaŭ +Comment[bg]=Превключване между стартирани програми +Comment[bs]=Prebacujte između programa u radu +Comment[ca]=Commuta entre aplicacions en execució +Comment[ca@valencia]=Commuta entre aplicacions en execució +Comment[cs]=Přepínač mezi běžícími aplikacemi +Comment[da]=Skift mellem kørende programmer +Comment[de]=Ermöglicht den wechselnden Zugriff auf laufende Programme. +Comment[el]=Εναλλαγή μεταξύ εκτελούμενων εφαρμογών +Comment[en_GB]=Switch between running applications +Comment[eo]=Komuti inter funkciantaj aplikaĵoj +Comment[es]=Cambiar entre aplicaciones en ejecución +Comment[et]=Lülitumine töötavate rakenduste vahel +Comment[eu]=Aldatu abian dauden aplikazio batetik bestera +Comment[fi]=Vaihda avoinna olevien ohjelmien välillä +Comment[fr]=Passe d'une application à l'autre +Comment[fy]=Wikselje tusken rinnende programma's +Comment[ga]=Athraigh idir feidhmchláir atá ag rith +Comment[gl]=Troca entre programas en execución +Comment[gu]=ચાલતાં કાર્યક્રમો વચ્ચે ફેરબદલી કરો +Comment[he]=מעבר בין יישומים פעילים +Comment[hi]=चल रहे अनुप्रयोगों के बीच स्विच करें +Comment[hne]=चलत अनुपरयोग मं स्विच करव +Comment[hr]=Promjena među pokrenutim aplikacijama +Comment[hsb]=Šaltuje mjez běžacymi aplikacijemi +Comment[hu]=A futó alkalmazások között lehet vele váltani +Comment[ia]=Commuta inter applicationes que on exeque +Comment[id]=Berpindah di antara aplikasi yang berjalan +Comment[is]=Skipta á milli forrita sem eru í gangi +Comment[it]=Passa ad altre applicazioni in esecuzione +Comment[ja]=実行中のアプリケーションを切り替えます +Comment[kk]=Жегілген қолданбаларды ақтару +Comment[km]=ប្ដូរ​រវាង​កម្មវិធី​ដែល​កំពុង​រត់ +Comment[kn]=ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಅನ್ವಯಗಳ ನಡುವೆ ಅಂತರಿಸು +Comment[ko]=실행 중인 프로그램을 전환합니다 +Comment[lt]=Perjungimas tarp veikiančių programų +Comment[lv]=Pārslēdzas starp palaistajām programmām +Comment[mk]=Движете се низ активните апликации +Comment[ml]=പ്രവര്‍ത്തിക്കുന്ന പ്രവര്‍ത്തങ്ങള്‍ തമ്മില്‍ മാറുക +Comment[mr]=कार्यरत अनुप्रयोग अंतर्गत बदल करा +Comment[nb]=Bytt mellom kjørende programmer +Comment[nds]=Twischen lopen Programmen wesseln +Comment[nl]=Schakel tussen draaiende programma's +Comment[nn]=Byt mellom program som køyrer +Comment[or]=ଚାଲୁଥିବା ପ୍ରୟୋଗଗୁଡ଼ିକ ମଧ୍ଯରେ ଅଦଳ ବଦଳ କରନ୍ତୁ +Comment[pa]=ਚੱਲਦੀਆਂ ਐਪਲੀਕੇਸ਼ਨਾਂ ਬਦਲੋ +Comment[pl]=Przełączanie między działającymi programami +Comment[pt]=Mudar de aplicações em execução +Comment[pt_BR]=Alterna entre os aplicativos em execução +Comment[ro]=Comută printre aplicațiile ce rulează +Comment[ru]=Переключение между запущенными приложениями +Comment[si]=ක්‍රියාත්මක වන යෙදුම් අතරේ මාරුවන්න +Comment[sk]=Prepínanie medzi bežiacimi aplikáciami +Comment[sl]=Preklapljajte med zagnanimi programi +Comment[sr]=Пребацујте између програма у раду +Comment[sr@ijekavian]=Пребацујте између програма у раду +Comment[sr@ijekavianlatin]=Prebacujte između programa u radu +Comment[sr@latin]=Prebacujte između programa u radu +Comment[sv]=Byt mellan program som kör +Comment[ta]=Switch between running applications +Comment[te]=నడుస్తున్న అనువర్తనముల మద్యన మారుము +Comment[tg]=Переключение между рабочими столами +Comment[th]=สลับการทำงานระหว่างโปรแกรมต่าง ๆ +Comment[tr]=Çalışan uygulamalar arasında gezin +Comment[ug]=ئىجرا بولۇۋاتقان پروگراممىلارنى ئالماشتۇر +Comment[uk]=Перемкніть запущені програми +Comment[wa]=Passer d' on programe ovrant a èn ôte +Comment[x-test]=xxSwitch between running applicationsxx +Comment[zh_CN]=在已运行的程序间切换 +Comment[zh_TW]=在執行中的應用程式間切換 +Type=Service +Icon=preferences-system-windows +X-KDE-ServiceTypes=Plasma/Applet + +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml + + +X-KDE-PluginInfo-Author=Eike Hein +X-KDE-PluginInfo-Email=hein@kde.org +X-KDE-PluginInfo-Name=org.kde.plasma.taskmanager +X-KDE-PluginInfo-Version=3.0 +X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/Tasks +X-KDE-PluginInfo-Category=Windows and Tasks +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL v2+ +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/applets/taskmanager/plugin/backend.cpp b/applets/taskmanager/plugin/backend.cpp new file mode 100644 index 0000000000..7d8d47c6c4 --- /dev/null +++ b/applets/taskmanager/plugin/backend.cpp @@ -0,0 +1,272 @@ +/*************************************************************************** + * Copyright (C) 2007 by Robert Knight * + * Copyright (C) 2008 by Alexis Ménard * + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "backend.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +//FIXME TODO HACK See comment in Backend::activateItem(). +#include +#include + +#include +#include + +Backend::Backend(QObject* parent) : QObject(parent), + m_groupManager(new TaskManager::GroupManager(this)), + m_tasksModel(new TaskManager::TasksModel(m_groupManager, this)), + m_lastWindowId(0), + m_highlightWindows(false) +{ + connect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); +} + +Backend::~Backend() +{ +} + +TaskManager::GroupManager* Backend::groupManager() const +{ + return m_groupManager; +} + +TaskManager::TasksModel* Backend::tasksModel() const +{ + return m_tasksModel; +} + +QQuickItem* Backend::taskManagerItem() const +{ + return m_taskManagerItem; +} + +void Backend::setTaskManagerItem(QQuickItem* item) +{ + if (item != m_taskManagerItem) { + m_taskManagerItem = item; + emit taskManagerItemChanged(item); + } +} + +bool Backend::anyTaskNeedsAttention() const +{ + return groupManager()->rootGroup()->demandsAttention(); +} + +bool Backend::highlightWindows() const +{ + return m_highlightWindows; +} + +void Backend::setHighlightWindows(bool highlight) +{ + if (highlight != m_highlightWindows) { + m_highlightWindows = highlight; + emit highlightWindowsChanged(highlight); + } +} + +int Backend::groupingStrategy() const +{ + return m_groupManager->groupingStrategy(); +} + +void Backend::setGroupingStrategy(int groupingStrategy) +{ + // FIXME TODO: This is fucking terrible. + m_groupManager->setGroupingStrategy(static_cast(groupingStrategy)); +} + +int Backend::sortingStrategy() const +{ + return m_groupManager->sortingStrategy(); +} + +void Backend::setSortingStrategy(int sortingStrategy) +{ + // FIXME TODO: This is fucking terrible. + m_groupManager->setSortingStrategy(static_cast(sortingStrategy)); +} + +void Backend::updateLaunchersCache() +{ + const QList launcherList = m_groupManager->launcherList(); + + QStringList launchers; + + foreach(const QUrl& launcher, launcherList) { + launchers.append(launcher.toString()); + } + + QString joined(launchers.join(',')); + + if (joined != m_launchers) { + m_launchers = joined; + emit launchersChanged(); + } +} + +QString Backend::launchers() const +{ + return m_launchers; +} + +void Backend::setLaunchers(const QString& launchers) +{ + if (launchers == m_launchers) { + return; + } + + m_launchers = launchers; + + QList launcherList; + + foreach(const QString& launcher, launchers.split(',')) { + launcherList.append(launcher); + } + + disconnect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); + m_groupManager->setLauncherList(launcherList); + connect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); + + emit launchersChanged(); +} + +void Backend::activateItem(int id, bool toggle) +{ + TaskManager::AbstractGroupableItem* item = m_groupManager->rootGroup()->getMemberById(id); + + if (!item) { + return; + } + + if (item->itemType() == TaskManager::TaskItemType && !item->isStartupItem()) { + TaskManager::TaskItem* taskItem = static_cast(item); + + if (toggle) { + taskItem->task()->activateRaiseOrIconify(); + } else { + taskItem->task()->activate(); + } + + //FIXME TODO HACK This papers over XSendEvent() calls lacking an explicit flush in + //KWindowSystem; not addressed there due to pending xcb port. + XFlush(QX11Info::display()); + } else if (item->itemType() == TaskManager::LauncherItemType) { + static_cast(item)->launch(); + } +} + + +void Backend::itemContextMenu(QQuickItem *item, QObject *configAction) +{ + TaskManager::AbstractGroupableItem* agItem = m_groupManager->rootGroup()->getMemberById(item->property("itemId").toInt()); + + if (!KAuthorized::authorizeKAction("kwin_rmb") || !item || !item->window() || !agItem) { + return; + } + + QList actionList; + + QAction *action = static_cast(configAction); + + if (action && action->isEnabled()) { + actionList << action; + } + + TaskManager::BasicMenu* menu = 0; + + if (agItem->itemType() == TaskManager::TaskItemType && !agItem->isStartupItem()) { + TaskManager::TaskItem* taskItem = static_cast(agItem); + menu = new TaskManager::BasicMenu(0, taskItem, m_groupManager, actionList); + } else if (agItem->itemType() == TaskManager::GroupItemType) { + TaskManager::TaskGroup* taskGroup = static_cast(agItem); + const int maxWidth = 0.8 * item->window()->screen()->size().width(); + menu = new TaskManager::BasicMenu(0, taskGroup, m_groupManager, actionList, QList (), maxWidth); + } else if (agItem->itemType() == TaskManager::LauncherItemType) { + menu = new TaskManager::BasicMenu(0, static_cast(agItem), + m_groupManager, actionList); + } + + if (!menu) { + return; + } + + menu->adjustSize(); + + if (!taskManagerItem()->property("vertical").toBool()) { + menu->setMinimumWidth(item->width()); + } + + menu->exec(QCursor::pos()); + menu->deleteLater(); +} + +void Backend::itemHovered(int id, bool hovered) +{ + TaskManager::AbstractGroupableItem* item = m_groupManager->rootGroup()->getMemberById(id); + + if (!item) { + return; + } + + if (hovered && m_highlightWindows && m_taskManagerItem->window()) { + m_lastWindowId = m_taskManagerItem->window()->winId(); + KWindowEffects::highlightWindows(m_lastWindowId, QList::fromSet(item->winIds())); + } else if (m_highlightWindows && m_lastWindowId) { + KWindowEffects::highlightWindows(m_lastWindowId, QList()); + } +} + +void Backend::itemMove(int id, int newIndex) +{ + m_groupManager->manualSortingRequest(m_groupManager->rootGroup()->getMemberById(id), newIndex); +} + +void Backend::itemGeometryChanged(QQuickItem *item, int id) +{ + TaskManager:: AbstractGroupableItem *agItem = m_groupManager->rootGroup()->getMemberById(id); + + if (!item || !item->window() || !agItem || agItem->itemType() != TaskManager::TaskItemType) + { + return; + } + + TaskManager::TaskItem *taskItem = static_cast(agItem); + + if (!taskItem->task()) { + return; + } + + QRect iconRect(item->x(), item->y(), item->width(), item->height()); + iconRect.moveTopLeft(item->parentItem()->mapToScene(iconRect.topLeft()).toPoint()); + iconRect.moveTopLeft(item->window()->mapToGlobal(iconRect.topLeft())); + + taskItem->task()->publishIconGeometry(iconRect); +} diff --git a/applets/taskmanager/plugin/backend.h b/applets/taskmanager/plugin/backend.h new file mode 100644 index 0000000000..b1a4f5f812 --- /dev/null +++ b/applets/taskmanager/plugin/backend.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef BACKEND_H +#define BACKEND_H + +#include + +#include +#include + +class QQuickItem; + +class Backend : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QObject* groupManager READ groupManager CONSTANT) + Q_PROPERTY(QObject* tasksModel READ tasksModel CONSTANT) + Q_PROPERTY(QQuickItem* taskManagerItem READ taskManagerItem WRITE setTaskManagerItem NOTIFY taskManagerItemChanged) + Q_PROPERTY(bool anyTaskNeedsAttention READ anyTaskNeedsAttention) + Q_PROPERTY(bool highlightWindows READ highlightWindows WRITE setHighlightWindows NOTIFY highlightWindowsChanged) + Q_PROPERTY(int groupingStrategy READ groupingStrategy WRITE setGroupingStrategy) + Q_PROPERTY(int sortingStrategy READ sortingStrategy WRITE setSortingStrategy) + Q_PROPERTY(QString launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) + + public: + Backend(QObject *parent = 0); + ~Backend(); + + TaskManager::GroupManager *groupManager() const; + TaskManager::TasksModel *tasksModel() const; + + QQuickItem* taskManagerItem() const; + void setTaskManagerItem(QQuickItem *item); + + bool anyTaskNeedsAttention() const; + + bool highlightWindows() const; + void setHighlightWindows(bool highlight); + + int groupingStrategy() const; + void setGroupingStrategy(int groupingStrategy); + + int sortingStrategy() const; + void setSortingStrategy(int sortingStrategy); + + QString launchers() const; + void setLaunchers(const QString& launchers); + + public Q_SLOTS: + void activateItem(int id, bool toggle); + void itemContextMenu(QQuickItem *item, QObject *configAction); + void itemHovered(int id, bool hovered); + void itemMove(int id, int newIndex); + void itemGeometryChanged(QQuickItem *item, int id); + + private Q_SLOTS: + void updateLaunchersCache(); + + Q_SIGNALS: + void taskManagerItemChanged(QQuickItem*); + void highlightWindowsChanged(bool); + void launchersChanged(); + + private: + TaskManager::GroupManager *m_groupManager; + TaskManager::TasksModel *m_tasksModel; + QQuickItem* m_taskManagerItem; + WId m_lastWindowId; + bool m_highlightWindows; + QString m_launchers; +}; + +#endif diff --git a/applets/taskmanager/plugin/blur.cpp b/applets/taskmanager/plugin/blur.cpp new file mode 100644 index 0000000000..7135e9cacb --- /dev/null +++ b/applets/taskmanager/plugin/blur.cpp @@ -0,0 +1,154 @@ +#ifndef BLUR_CPP +#define BLUR_CPP + +/* + * Copyright 2007 Jani Huhtanen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +// Exponential blur, Jani Huhtanen, 2006 +// +template +static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha); + +template +static inline void blurrow(QImage &im, int line, int alpha); + +template +static inline void blurcol(QImage &im, int col, int alpha); + +/* +* expblur(QImage &img, int radius) +* +* In-place blur of image 'img' with kernel +* of approximate radius 'radius'. +* +* Blurs with two sided exponential impulse +* response. +* +* aprec = precision of alpha parameter +* in fixed-point format 0.aprec +* +* zprec = precision of state parameters +* zR,zG,zB and zA in fp format 8.zprec +*/ +template +void expblur(QImage &img, int radius) +{ + if (radius < 1) { + return; + } + + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + /* Calculate the alpha such that 90% of + the kernel is within the radius. + (Kernel extends to infinity) + */ + int alpha = (int)((1 << aprec) * (1.0f - std::exp(-2.3f / (radius + 1.f)))); + + int height = img.height(); + int width = img.width(); + for (int row=0; row(img, row, alpha); + } + + for (int col=0; col(img, col, alpha); + } + return; +} + +template +static inline void blurinner(unsigned char *bptr, int &zR, int &zG, int &zB, int &zA, int alpha) +{ + int R, G, B, A; + R = *bptr; + G = *(bptr + 1); + B = *(bptr + 2); + A = *(bptr + 3); + + zR += (alpha * ((R << zprec) - zR)) >> aprec; + zG += (alpha * ((G << zprec) - zG)) >> aprec; + zB += (alpha * ((B << zprec) - zB)) >> aprec; + zA += (alpha * ((A << zprec) - zA)) >> aprec; + + *bptr = zR >> zprec; + *(bptr+1) = zG >> zprec; + *(bptr+2) = zB >> zprec; + *(bptr+3) = zA >> zprec; +} + +template +static inline void blurrow(QImage &im, int line, int alpha) +{ + int zR, zG, zB, zA; + + QRgb *ptr = (QRgb *)im.scanLine(line); + int width = im.width(); + + zR = *((unsigned char *)ptr ) << zprec; + zG = *((unsigned char *)ptr + 1) << zprec; + zB = *((unsigned char *)ptr + 2) << zprec; + zA = *((unsigned char *)ptr + 3) << zprec; + + for (int index=1; index((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); + } + for (int index=width-2; index>=0; index--) { + blurinner((unsigned char *)&ptr[index],zR,zG,zB,zA,alpha); + } +} + +template +static inline void blurcol(QImage &im, int col, int alpha) +{ + int zR, zG, zB, zA; + + QRgb *ptr = (QRgb *)im.bits(); + ptr += col; + int height = im.height(); + int width = im.width(); + + zR = *((unsigned char *)ptr ) << zprec; + zG = *((unsigned char *)ptr + 1) << zprec; + zB = *((unsigned char *)ptr + 2) << zprec; + zA = *((unsigned char *)ptr + 3) << zprec; + + for (int index=width; index<(height-1)*width; index+=width) { + blurinner((unsigned char *)&ptr[index], zR, zG, zB, zA, alpha); + } + + for (int index=(height-2)*width; index>=0; index-=width) { + blurinner((unsigned char *)&ptr[index], zR, zG, zB, zA, alpha); + } +} + +template +inline const T &qClamp(const T &x, const T &low, const T &high) +{ + if (x < low) { + return low; + } else if (x > high) { + return high; + } else { + return x; + } +} + +#endif diff --git a/applets/taskmanager/plugin/draghelper.cpp b/applets/taskmanager/plugin/draghelper.cpp new file mode 100644 index 0000000000..64f8ca237c --- /dev/null +++ b/applets/taskmanager/plugin/draghelper.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "draghelper.h" + +#include +#include +#include +#include +#include +#include + +DragHelper::DragHelper(QObject* parent) : QObject(parent) +{ +} + +DragHelper::~DragHelper() +{ +} + +bool DragHelper::isDrag(int oldX, int oldY, int newX, int newY) const +{ + return ((QPoint(oldX, oldY) - QPoint(newX, newY)).manhattanLength() >= QApplication::startDragDistance()); +} + +void DragHelper::startDrag(QQuickItem *item, const QString &mimeType, + const QVariant &mimeData, const QUrl &url, const QIcon &icon) const +{ + QList urlList; + urlList.append(url); + + QMimeData *dragData = new QMimeData(); + dragData->setData(mimeType, mimeData.toByteArray()); + dragData->setUrls(urlList); + + QDrag *drag = new QDrag(item); + drag->setMimeData(dragData); + drag->setPixmap(icon.pixmap(QSize(48, 48))); + drag->setHotSpot(QPoint(drag->pixmap().width() / 2, drag->pixmap().height() / 2)); + + drag->exec(); + + emit dropped(); +} + diff --git a/applets/taskmanager/plugin/draghelper.h b/applets/taskmanager/plugin/draghelper.h new file mode 100644 index 0000000000..48e291afd2 --- /dev/null +++ b/applets/taskmanager/plugin/draghelper.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef DRAGHELPER_H +#define DRAGHELPER_H + +#include + +class QIcon; +class QQuickItem; +class QUrl; + +class DragHelper : public QObject +{ + Q_OBJECT + + public: + DragHelper(QObject *parent = 0); + ~DragHelper(); + + Q_INVOKABLE bool isDrag(int oldX, int oldY, int newX, int newY) const; + Q_INVOKABLE void startDrag(QQuickItem* item, const QString &mimeType, + const QVariant &mimeData, const QUrl &url, const QIcon &icon) const; + + Q_SIGNALS: + void dropped() const; +}; + +#endif diff --git a/applets/taskmanager/plugin/qmldir b/applets/taskmanager/plugin/qmldir new file mode 100644 index 0000000000..6bc8b190d9 --- /dev/null +++ b/applets/taskmanager/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.plasma.private.taskmanager +plugin taskmanagerplugin diff --git a/applets/taskmanager/plugin/taskmanagerplugin.cpp b/applets/taskmanager/plugin/taskmanagerplugin.cpp new file mode 100644 index 0000000000..8921f83a0b --- /dev/null +++ b/applets/taskmanager/plugin/taskmanagerplugin.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +/* + Copyright (C) 2011 Martin Gräßlin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "taskmanagerplugin.h" +#include "backend.h" +#include "draghelper.h" +#include "textlabel.h" + +#include + +void TaskManagerPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.taskmanager")); + qmlRegisterType(uri, 0, 1, "Backend"); + qmlRegisterType(uri, 0, 1, "DragHelper"); + qmlRegisterType(uri, 0, 1, "TextLabel"); +} + +#include "taskmanagerplugin.moc" diff --git a/applets/taskmanager/plugin/taskmanagerplugin.h b/applets/taskmanager/plugin/taskmanagerplugin.h new file mode 100644 index 0000000000..7b7fb0926d --- /dev/null +++ b/applets/taskmanager/plugin/taskmanagerplugin.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef TASKMANAGERPLUGIN_H +#define TASKMANAGERPLUGIN_H + +#include +#include + +class TaskManagerPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + + public: + virtual void registerTypes(const char *uri); +}; + +#endif // TASKMANAGERPLUGIN_H diff --git a/applets/taskmanager/plugin/textlabel.cpp b/applets/taskmanager/plugin/textlabel.cpp new file mode 100644 index 0000000000..f916384425 --- /dev/null +++ b/applets/taskmanager/plugin/textlabel.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (C) 2007 Jani Huhtanen * + * Copyright (C) 2007 by Robert Knight * + * Copyright (C) 2008 by Alexis Ménard * + * Copyright (C) 2008 by Marco Martin * + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "textlabel.h" +#include "blur.cpp" + +#include +#include +#include + +TextLabel::TextLabel(QQuickItem* parent) : QQuickPaintedItem(parent), + m_enabled(true), + m_elide(false) +{ +} + +TextLabel::~TextLabel() +{ +} + +bool TextLabel::enabled() const +{ + return m_enabled; +} + +void TextLabel::setEnabled(bool enabled) +{ + if (enabled != m_enabled) { + m_enabled = enabled; + update(); + } +} + +QString TextLabel::text() const +{ + return m_text; +} + +void TextLabel::setText(const QString& text) +{ + if (text != m_text) + { + m_text = text; + m_cachedShadow = QPixmap(); + updateImplicitSize(); + update(); + emit textChanged(text); + } +} + +QColor TextLabel::color() const +{ + return m_color; +} + +void TextLabel::setColor(const QColor& color) +{ + if (color != m_color) + { + m_color = color; + m_cachedShadow = QPixmap(); + update(); + } +} + + +bool TextLabel::elide() const +{ + return m_elide; +} + +void TextLabel::setElide(bool elide) +{ + m_elide = elide; + + updateImplicitSize(); +} + +void TextLabel::updateImplicitSize() +{ + if (m_elide) { + setImplicitWidth(0); + setImplicitHeight(0); + } else { + QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); + + setImplicitWidth(fm.width(m_text)); + setImplicitHeight(fm.height()); + } +} + +void TextLabel::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) +{ + if (newGeometry.size() != oldGeometry.size()) { + m_cachedShadow = QPixmap(); + } +} + +QColor TextLabel::textColor() const +{ + QColor color(m_color); + + if (!m_enabled) { + color.setAlphaF(0.5); + } + + return color; +} + +QTextOption TextLabel::textOption() const +{ + Qt::LayoutDirection direction = QApplication::layoutDirection(); + Qt::Alignment alignment = QStyle::visualAlignment(direction, Qt::AlignLeft | Qt::AlignVCenter); + + QTextOption option; + option.setTextDirection(direction); + option.setAlignment(alignment); + + return option; +} + +void TextLabel::layoutText(QTextLayout &layout, const QString &text, const QSize &constraints) +{ + QFontMetrics metrics(layout.font()); + int leading = metrics.leading(); + int height = 0; + int maxWidth = constraints.width(); + int widthUsed = 0; + int lineSpacing = metrics.lineSpacing(); + QTextLine line; + + layout.setText(text); + + layout.beginLayout(); + while ((line = layout.createLine()).isValid()) { + height += leading; + + // Make the last line that will fit infinitely long. + // drawTextLayout() will handle this by fading the line out + // if it won't fit in the constraints. + if (height + 2 * lineSpacing > constraints.height()) { + line.setPosition(QPoint(0, height)); + break; + } + + line.setLineWidth(maxWidth); + line.setPosition(QPoint(0, height)); + + height += int(line.height()); + widthUsed = int(qMax(qreal(widthUsed), line.naturalTextWidth())); + } + layout.endLayout(); +} + +void TextLabel::drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect) +{ + if (rect.width() < 1 || rect.height() < 1) { + return; + } + + QPixmap pixmap(rect.size()); + pixmap.fill(Qt::transparent); + + QPainter p(&pixmap); + p.setPen(painter->pen()); + + // Create the alpha gradient for the fade out effect + QLinearGradient alphaGradient(0, 0, 1, 0); + alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode); + if (layout.textOption().textDirection() == Qt::LeftToRight) { + alphaGradient.setColorAt(0, QColor(0, 0, 0, 255)); + alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); + } else { + alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); + alphaGradient.setColorAt(1, QColor(0, 0, 0, 255)); + } + + QFontMetrics fm(layout.font()); + int textHeight = layout.lineCount() * fm.lineSpacing(); + + QPointF position(0, (rect.height() - textHeight) / 2); + QList fadeRects; + int fadeWidth = 30; + + // Draw each line in the layout + for (int i = 0; i < layout.lineCount(); i++) { + QTextLine line = layout.lineAt(i); + line.draw(&p, position); + + // Add a fade out rect to the list if the line is too long + if (line.naturalTextWidth() > rect.width()) + { + int x = int(qMin(line.naturalTextWidth(), (qreal)pixmap.width())) - fadeWidth; + int y = int(line.position().y() + position.y()); + QRect r = QStyle::visualRect(layout.textOption().textDirection(), pixmap.rect(), + QRect(x, y, fadeWidth, int(line.height()))); + fadeRects.append(r); + } + } + + // Reduce the alpha in each fade out rect using the alpha gradient + if (!fadeRects.isEmpty()) { + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + foreach (const QRect &rect, fadeRects) { + p.fillRect(rect, alphaGradient); + } + } + + p.end(); + + + QColor shadowColor; + if (qGray(textColor().rgb()) > 192) { + shadowColor = Qt::black; + } else { + shadowColor = Qt::white; + } + + if (m_cachedShadow.isNull()) { + QImage shadow = pixmap.toImage(); + expblur<16, 7>(shadow, 1); + QPainter p(&shadow); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(shadow.rect(), shadowColor); + p.end(); + m_cachedShadow = QPixmap(shadow.size()); + m_cachedShadow.fill(Qt::transparent); + QPainter buffPainter(&m_cachedShadow); + buffPainter.drawImage(QPoint(0,0), shadow); + } + + if (shadowColor == Qt::white) { + painter->drawPixmap(rect.topLeft(), m_cachedShadow); + } else { + painter->drawPixmap(rect.topLeft() + QPoint(1, 2), m_cachedShadow); + } + painter->drawPixmap(rect.topLeft(), pixmap); +} + +void TextLabel::paint(QPainter* painter) +{ + painter->setPen(QPen(textColor(), 1.0)); + + if (QFontDatabase::systemFont(QFontDatabase::GeneralFont) != m_layout.font()) { + m_cachedShadow = QPixmap(); + } + + m_layout.setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); + m_layout.setTextOption(textOption()); + + layoutText(m_layout, text(), boundingRect().toRect().size()); + drawTextLayout(painter, m_layout, boundingRect().toRect()); +} diff --git a/applets/taskmanager/plugin/textlabel.h b/applets/taskmanager/plugin/textlabel.h new file mode 100644 index 0000000000..f5f6de2b51 --- /dev/null +++ b/applets/taskmanager/plugin/textlabel.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2007 by Robert Knight * + * Copyright (C) 2008 by Alexis Ménard * + * Copyright (C) 2008 by Marco Martin * + * Copyright (C) 2012-2013 by Eike Hein * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef TEXTLABEL_H +#define TEXTLABEL_H + +#include +#include +#include +#include +#include + +class TextLabel : public QQuickPaintedItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(bool elide READ elide WRITE setElide) + + public: + TextLabel(QQuickItem *parent = 0); + ~TextLabel(); + + bool enabled() const; + void setEnabled(bool enabled); + + QString text() const; + void setText(const QString& text); + + QColor color() const; + void setColor(const QColor& color); + + bool elide() const; + void setElide(bool elide); + + void paint(QPainter *painter); + + Q_SIGNALS: + void textChanged(const QString& text); + + protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + + private: + void updateImplicitSize(); + QColor textColor() const; + QTextOption textOption() const;; + void layoutText(QTextLayout &layout, const QString &text, const QSize &constraints); + void drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect); + + bool m_enabled; + QString m_text; + QColor m_color; + bool m_elide; + QTextLayout m_layout; + QPixmap m_cachedShadow; +}; + +#endif diff --git a/applets/trash/Messages.sh b/applets/trash/Messages.sh new file mode 100644 index 0000000000..b1303ad54a --- /dev/null +++ b/applets/trash/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_trash.pot +rm -f rc.cpp \ No newline at end of file diff --git a/applets/trash/contents/ui/main.qml b/applets/trash/contents/ui/main.qml new file mode 100644 index 0000000000..d6cb9bcd90 --- /dev/null +++ b/applets/trash/contents/ui/main.qml @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Heena Mahour + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as Components +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.dirmodel 2.0 + +MouseArea { + id:root + + Layout.minimumWidth: formFactor == PlasmaCore.Types.Horizontal ? height: 1 + Layout.minimumHeight: formFactor == PlasmaCore.Types.Vertical ? width : 1 + property int formFactor: plasmoid.formFactor + property bool constrained: formFactor==PlasmaCore.Types.Vertical||formFactor==PlasmaCore.Types.Horizontal + hoverEnabled: true + onClicked: Qt.openUrlExternally("trash:/"); + + DirModel { + id: dirModel + url: "trash:/" + } + + function action_open() { + Qt.openUrlExternally("trash:/"); + } + + function action_empty() { + queryDialog.open(); + } + + Component.onCompleted: { + plasmoid.backgroundHints = 0; + plasmoid.setAction("open", "Open","document-open"); + plasmoid.setAction("empty","Empty","trash-empty"); + plasmoid.popupIcon = "user-trash"; + } + + PlasmaCore.IconItem { + id:icon + source: (dirModel.count > 0) ? "user-trash-full": "user-trash" + anchors{ + left: parent.left + right: parent.right + top: parent.top + bottom: constrained ? parent.bottom: text.top + } + active: root.containsMouse + } + Components.Label { + id: text + text: (dirModel.count==0) ? i18n("Trash\nEmpty"): i18np("Trash\nOne item", "Trash\n %1 items", dirModel.count); + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + } + horizontalAlignment: Text.AlignHCenter + opacity: constrained ? 0: 1 + } + PlasmaCore.ToolTipArea { + anchors.fill: parent + mainText: i18n("Trash") + subText: (dirModel.count==0) ? i18n("Trash \n Empty"): i18np("Trash\nOne item", "Trash\n %1 items", dirModel.count ); + image: (dirModel.count > 0) ? "user-trash-full": "user-trash" + } + + + Components.QueryDialog { + id: queryDialog + titleIcon: "user-trash" + titleText: i18n("Empty Trash") + message: i18n("Do you really want to empty the trash ? All the items will be deleted.") + acceptButtonText: i18n("Empty Trash") + rejectButtonText: i18n("Cancel") + onAccepted: plasmoid.runCommand("ktrash", ["--empty"]); + onRejected: queryDialog.close(); + visualParent: root + } +} diff --git a/applets/trash/metadata.desktop b/applets/trash/metadata.desktop new file mode 100644 index 0000000000..ef366301e5 --- /dev/null +++ b/applets/trash/metadata.desktop @@ -0,0 +1,90 @@ +[Desktop Entry] +Name=Trashcan +Name[af]=Gemors +Name[ar]=سلة المهملات +Name[ast]=Papelera +Name[be@latin]=Śmietnica +Name[bg]=Кошче +Name[bn]=আবর্জনা +Name[bs]=korpa za smeće +Name[ca]=Paperera +Name[ca@valencia]=Paperera +Name[csb]=Kòsz +Name[da]=Affald +Name[de]=Papierkorb +Name[el]=Κάδος απορριμμάτων +Name[en_GB]=Wastebin +Name[eo]=Rubujo +Name[es]=Papelera +Name[et]=Prügikast +Name[eu]=Zakarontzia +Name[fi]=Roskakori +Name[fr]=Corbeille +Name[fy]=Jiskefet +Name[ga]=Bruscar +Name[gl]=Lixo +Name[gu]=કચરાપેટી +Name[he]=פח אשפה +Name[hi]=रद्दी की टोकरी +Name[hne]=घुरुवा +Name[hr]=Otpad +Name[hsb]=Papjernik +Name[hu]=Kuka +Name[ia]=Corbe +Name[id]=Tempat Sampah +Name[is]=Ruslafata +Name[it]=Cestino +Name[ja]=ごみ箱 +Name[kk]=Өшірілгендер +Name[km]=ធុង​សំរាម +Name[kn]=ಕಸಬುಟ್ಟಿ +Name[ko]=휴지통 +Name[ku]=Qutiya Sergo +Name[lt]=Šiukšliadėžė +Name[lv]=Miskaste +Name[mk]=Корпа +Name[ml]=ചവറ്റുകുട്ട +Name[mr]=कचरापेटी +Name[nds]=Affalltünn +Name[nl]=Prullenbak +Name[or]=Trashcan +Name[pa]=ਰੱਦੀ-ਟੋਕਰੀ +Name[pl]=Kosz +Name[ro]=Coș de gunoi +Name[ru]=Корзина +Name[si]=ඉවතලන බඳුන +Name[sv]=Papperskorg +Name[ta]=அகற்றிடம் +Name[te]=చెత్తబుట్ట +Name[tg]=Ахлотдон +Name[th]=ถังขยะ +Name[tr]=Çöp Kutusu +Name[ug]=ئەخلەتخانا +Name[uk]=Смітник +Name[uz]=Chiqindilar qutisi +Name[uz@cyrillic]=Чиқиндилар қутиси +Name[wa]=Batch ås mannestés +Name[zh_CN]=回收站 +Name[zh_TW]=資源回收筒 +Type=Service +Icon=user-trash +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml + +X-Plasma-RequiredExtensions=LaunchApp,localio +X-Plasma-OptionalExtensions=LaunchApp +X-Plasma-ConfigPlugins=kcmtrash +X-KDE-Library=plasma_applet_trash +X-KDE-PluginInfo-Author=Heena +X-KDE-PluginInfo-Email=heena393@gmail.com +X-KDE-PluginInfo-Name=org.kde.plasma.trash +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=http://plasma.kde.org/ +X-KDE-PluginInfo-Category=File System +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=Gpl +X-KDE-PluginInfo-EnabledByDefault=true +X-Plasma-Requires-FileDialog=Unused +X-Plasma-Requires-LaunchApp=Required +X-Plasma-Requires-KRun=true diff --git a/applets/window-list/Messages.sh b/applets/window-list/Messages.sh new file mode 100644 index 0000000000..ef7d038057 --- /dev/null +++ b/applets/window-list/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_windowlist.pot +rm -f rc.cpp \ No newline at end of file diff --git a/applets/window-list/contents/ui/Menu.qml b/applets/window-list/contents/ui/Menu.qml new file mode 100644 index 0000000000..acbc3d0402 --- /dev/null +++ b/applets/window-list/contents/ui/Menu.qml @@ -0,0 +1,79 @@ +/* + * Copyright 2013 Heena Mahour + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +PlasmaExtras.ScrollArea { + id: menu + + property alias model: menuListView.model + property alias section: menuListView.section + property int iconSize: units.iconSizes.small + property bool showDesktop: true + signal itemSelected(int id) + signal executeJob(string jobName, int id) + signal setOnDesktop(int id, int desktop) + + ListView { + id: menuListView + anchors.top: menu.top + anchors.left: menu.left + anchors.bottom: menu.bottom + focus: true + section.property: desktop + section.criteria: ViewSection.FullString + spacing:0 + + section.delegate: PlasmaComponents.Highlight { + hover: menu.focus + width: menu.width + height: sectionDelegateLabel.height + marginHints.top + marginHints.bottom + + PlasmaComponents.Label { + id: sectionDelegateLabel + text: section > 0 ? i18n("Desktop") + section : i18n("On all desktops") + anchors.centerIn: parent + } + } + + highlight: PlasmaComponents.Highlight { + hover: false + width: menuListView.width + } + + delegate: TaskDelegate { + id: menuItemDelegate + width: menuListView.width + nameLabel: DisplayRole + desktop: Desktop + icon: DecorationRole + active: Active + iconSize: menu.iconSize + showDesktop: menu.showDesktop + onClicked: menu.itemSelected(Id); + onExecuteJob: menu.executeJob(jobName, Id); + onSetOnDesktop: menu.setOnDesktop(Id, desktop); + onActiveChanged: { + if (active) { + menuListView.currentIndex = index + } + } + } + } +} diff --git a/applets/window-list/contents/ui/TaskDelegate.qml b/applets/window-list/contents/ui/TaskDelegate.qml new file mode 100644 index 0000000000..986433e023 --- /dev/null +++ b/applets/window-list/contents/ui/TaskDelegate.qml @@ -0,0 +1,91 @@ +/* + * Copyright 2013 Heena Mahour + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore + +Item { + id: menuItem + width: parent.width + height: Math.max(iconItem.height, label.height) + highlightItem.marginHints.top + highlightItem.marginHints.bottom + + signal clicked() + signal entered() + signal executeJob(string jobName) + signal setOnDesktop(int desktop) + property alias nameLabel: label.text + property int desktop + property alias icon: iconItem.source + property alias label: label.text + property bool active: false + property int iconSize: units.iconSizes.small + property bool showDesktop: true + property variant desktopItems: [] + + PlasmaCore.IconItem { + id: iconItem + + width: menuItem.iconSize + height: width + + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: highlightItem.marginHints.left + } + } + + PlasmaComponents.Label { + id: label + + height: implicitHeight + anchors { + left: iconItem.right + leftMargin: highlightItem.marginHints.left + right: parent.right + verticalCenter: parent.verticalCenter + } + + font.italic: (minimized == true) ? true : false + + elide: Text.ElideRight + } + + MouseArea { + id:root_item + anchors.fill: parent + hoverEnabled: true + + onClicked: { + menuItem.clicked(); + } + + onEntered:{ + highlightItem.trackingItem = menuItem + highlightItem.width = menuItem.width + highlightItem.height = menuItem.height + menuItem.entered(); + highlight = true + } + + onExited: { + highlight = false + } + } +} + diff --git a/applets/window-list/contents/ui/main.qml b/applets/window-list/contents/ui/main.qml new file mode 100644 index 0000000000..d2278cfea5 --- /dev/null +++ b/applets/window-list/contents/ui/main.qml @@ -0,0 +1,176 @@ +/* + * Copyright 2013 Heena Mahour + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +PlasmaExtras.ConditionalLoader { + Layout.minimumWidth: Math.max(200, item ? item.implicitWidth : 0) + Layout.minimumHeight: Math.max(400, item ? item.implicitHeight : 0) + when: plasmoid.expanded + + source: Component { + MouseArea { + id: main + anchors.fill: parent + implicitWidth: windowListMenu.implicitWidth + implicitHeight: windowListMenu.implicitHeight + property alias data: tasksSource.data; + property variant desktopList: [] + property int iconSize: units.iconSizes.smallMedium + property bool showDesktop: true + property bool highlight: false + + PlasmaComponents.Highlight { + id: highlightItem + + property Item trackingItem + onTrackingItemChanged: { + if (trackingItem) { + y = trackingItem.mapToItem(main, 0, 0).y + } + } + + hover: true + width: windowListMenu.width + x: 0 + y: 0 + height: trackingItem.height + visible: true + opacity: highlight ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing: Easing.InOutQuad + } + } + Behavior on y { + NumberAnimation { + duration: units.longDuration + easing: Easing.InOutQuad + } + } + } + Connections { + target: windowListMenu.flickableItem + onContentYChanged: { + highlightItem.trackingItem = null + } + } + + + function performOperation(op) { + var service =tasksSource.serviceForSource(""); + var operation = service.operationDescription(op); + service.startOperationCall(operation); + } + + function executeJob(operationName, id) { + var service = tasksSource.serviceForSource("tasks"); + var operation = service.operationDescription(operationName); + operation.Id = id; + service.startOperationCall(operation); + } + + function setOnDesktop(id, desktop) { + var service = tasksSource.serviceForSource("tasks"); + var operation = service.operationDescription("toDesktop"); + operation.Id = id; + operation.desktop = desktop; + service.startOperationCall(operation); + } + + Component.onCompleted: { + var toolTipData = new Object; + toolTipData["image"] = "preferences-system-window"; + toolTipData["mainText"] = i18n("Window List"); + toolTipData["subText"] = i18n("Show list of opened windows"); + plasmoid.popupIconToolTip = toolTipData; + plasmoid.popupIcon = QIcon("preferences-system-windows"); + } + + PlasmaCore.DataSource { + id: tasksSource + engine: "tasks" + onSourceAdded: { + connectSource(source); + } + connectedSources: "tasks" + Component.onCompleted: { + print("connected"); + connectSource("virtualDesktops"); + main.desktopList = tasksSource.data["virtualDesktops"]["names"]; + print(models.tasks) + } + } + + PlasmaCore.SortFilterModel { + id: tasksModelSortedByDesktop + sortRole: "desktop" + sourceModel: tasksSource.models.tasks + } + + Column { + id: col + + PlasmaComponents.Highlight { + hover: menu.focus + width: windowListMenu.width + height: actions.height + marginHints.top + marginHints.bottom + PlasmaComponents.Label { + id: actions + text: i18n("Actions") + anchors.centerIn: parent + } + } + + TaskDelegate { + nameLabel: i18n("Unclutter Windows") + onClicked: performOperation("unclutter") + } + + TaskDelegate { + nameLabel : i18n("Cascade Windows") + onClicked: performOperation("cascade") + } + } + + Menu { + id: windowListMenu + anchors { + top: col.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + model: tasksModelSortedByDesktop + section.property: "desktop" + section.criteria: ViewSection.FullString + iconSize: main.iconSize + showDesktop: main.showDesktop + onItemSelected: main.executeJob("activate", id); + onExecuteJob: main.executeJob(jobName, id); + onSetOnDesktop: main.setOnDesktop(id, desktop); + } + } + } +} diff --git a/applets/window-list/metadata.desktop b/applets/window-list/metadata.desktop new file mode 100644 index 0000000000..9400e8a646 --- /dev/null +++ b/applets/window-list/metadata.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Window list +Name[bs]=Lista prozora +Name[ca]=Llista de finestres +Name[ca@valencia]=Llista de finestres +Name[cs]=Seznam oken +Name[da]=Vinduesliste +Name[de]=Fensterliste +Name[el]=Λίστα παραθύρων +Name[es]=Lista de ventanas +Name[eu]=Leiho-zerrenda +Name[fi]=Ikkunaluettelo +Name[fr]=Liste des fenêtres +Name[gl]=Lista de xanelas +Name[hu]=Ablaklista +Name[ia]=Lista de fenestra +Name[kk]=Терезе тізімі +Name[ko]=창 목록 +Name[nb]=Vindusliste +Name[nds]=Finsterlist +Name[nl]=Vensterlijst +Name[pa]=ਵਿੰਡੋ ਲਿਸਟ +Name[pl]=Lista okien +Name[pt]=Lista de janelas +Name[pt_BR]=Lista de janelas +Name[ro]=Lista ferestrelor +Name[sk]=Zoznam okien +Name[sl]=Seznam oken +Name[sr]=списак прозора +Name[sr@ijekavian]=списак прозора +Name[sr@ijekavianlatin]=spisak prozora +Name[sr@latin]=spisak prozora +Name[sv]=Fönsterlista +Name[tr]=Pencere listesi +Name[uk]=Список вікон +Name[x-test]=xxWindow listxx +Name[zh_CN]=窗口列表 +Name[zh_TW]=視窗列表 +Comment=Plasmoid to show list of opened windows. +Comment[ar]=بلازمويد لعرض لائحة بالنوافذ المفتوحة +Comment[ast]=Plasmoide qu'amuesa una llista de les ventanes abiertes. +Comment[bg]=Джаджа, показваща списък с отворените прозорци +Comment[bs]=Plazmoid za prikaz spiska otvorenih prozora. +Comment[ca]=Plasmoide que mostra una llista de les finestres obertes. +Comment[ca@valencia]=Plasmoide que mostra una llista de les finestres obertes. +Comment[cs]=Zobrazit seznam otevřených oken. +Comment[csb]=Plazmòid wëskrzënianiô lëstë òtemkłich òknów +Comment[da]=Plasmoid til at vise en liste over åbne vinduer. +Comment[de]=Miniprogramm zur Auflistung geöffneter Fenster. +Comment[el]=Πλασμοειδές για εμφάνιση λίστας των ανοιχτών παραθύρων. +Comment[en_GB]=Plasmoid to show list of opened windows. +Comment[es]=Plasmoide que muestra una lista de las ventanas abiertas. +Comment[et]=Avatud akende nimekirja näitav plasmoid. +Comment[eu]=Irekitako leihoen zerrenda erakusteko plasmoidea. +Comment[fi]=Plasmoidi avoimien ikkunoiden luettelon näyttämiseen. +Comment[fr]=Composant graphique affichant la liste des fenêtres ouvertes. +Comment[fy]=PLasmoid om in opsomming fan iepen finsters sjen te litten. +Comment[ga]=Plasmoid a thaispeánann liosta d'fhuinneoga oscailte. +Comment[gl]=Un plasmoide que mostra unha lista coas xanelas abertas. +Comment[he]=‏Plasmoid להצגת רשימת חלונות פתוחים. +Comment[hr]=Plasmoid koji prikazuje listu otvorenih prozora +Comment[hu]=Plazmoid a megnyitott ablakok listájának megjelenítésére. +Comment[ia]=Plasmoid per monstrar lista de fenestras aperite. +Comment[id]=Plasmoid untuk menampilkan senarai jendela terbuka. +Comment[is]=Plasmíð sem birtir lista yfir alla opna glugga +Comment[it]=Plasmoide per mostrare un elenco di finestre aperte. +Comment[ja]=開いているウィンドウの一覧を表示するプラズモイド +Comment[kk]=Ашылған терезелер тізімін көрсететін плазмоид. +Comment[km]=Plasmoid ត្រូវ​បង្ហាញ​បញ្ជី​បង្អួច​​ដែល​បា​ន​បើក ។ +Comment[ko]=열린 창 목록을 보여주는 Plasmoid. +Comment[lt]=Atvertų langų sąrašą rodantis plazmoidas. +Comment[lv]=Plazmoīds, kas rāda atvērto logu sarakstu. +Comment[mk]=Плазмоид за прикажување листа од отворените прозорци +Comment[ml]=തുറന്ന ജാലകങ്ങളുടെ പട്ടിക കാണിയ്ക്കാനുള്ള പ്ലാസ്മോയിഡ് +Comment[mr]=उघडलेल्या चौकटींची यादी दर्शविणारे प्लाज्मोइड +Comment[nb]=Plasmoide som viser en liste over åpne vinduer. +Comment[nds]=Plasma-Lüttprogramm, dat de List mit opmaakt Finstern wiest +Comment[nl]=Plasmoid voor het tonen van een lijst van geopende vensters. +Comment[nn]=Plasmoide for å visa oversikt over opne vindauge. +Comment[pa]=ਖੁੱਲ੍ਹੀਆਂ ਵਿੰਡੋਜ਼ ਦੀ ਲਿਸਟ ਵੇਖਾਉਣ ਲਈ ਪਲਾਜ਼ਾਮੋਡ। +Comment[pl]=Plazmoid pokazujący listę otwartych okien. +Comment[pt]=Um plasmóide para ver a lista de janelas abertas. +Comment[pt_BR]=Plasmoide para exibir a lista de janelas abertas. +Comment[ro]=Plasmoid ce arată lista ferestrelor deschise. +Comment[ru]=Виджет, показывающий список окон +Comment[si]=විවෘත කල කවුළු ලැයිස්තුවක් පෙන්වීම සඳහා ප්ලැස්මොයිඩය. +Comment[sk]=Zobrazenie zoznamu otvorených okien. +Comment[sl]=Gradnik, ki prikazuje seznam odprtih oken. +Comment[sr]=Плазмоид за приказ списка отворених прозора. +Comment[sr@ijekavian]=Плазмоид за приказ списка отворених прозора. +Comment[sr@ijekavianlatin]=Plazmoid za prikaz spiska otvorenih prozora. +Comment[sr@latin]=Plazmoid za prikaz spiska otvorenih prozora. +Comment[sv]=Plasmoid för att visa lista med öppna fönster. +Comment[tg]=Барномаи Plasmoid барои намоиши рӯйхат бо тирезаҳои кушодашуда. +Comment[th]=พลาสมอยด์สำหรับแสดงรายการของหน้าต่างต่าง ๆ ที่เปิดอยู่ +Comment[tr]=Açık pencerelerin listesini gösteren plasmoid. +Comment[ug]=ئېچىلغان كۆزنەكلەرنىڭ Plasmoid تىزىمىنى كۆرسىتىدۇ. +Comment[uk]=Плазмоїд для показу списку відкритих вікон. +Comment[wa]=Plasmowide po mostrer l' djivêye des fniesses di drovowes. +Comment[x-test]=xxPlasmoid to show list of opened windows.xx +Comment[zh_CN]=显示已打开窗口的 Plasmoid 部件。 +Comment[zh_TW]=顯示已開啟視窗清單的元件 +Icon=preferences-system-windows +Type=Service +X-KDE-ParentApp= + +X-KDE-PluginInfo-Author=Heena +X-KDE-PluginInfo-Email=heena393@gmail.com +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.plasma.windowlist +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=plasma.kde.org +X-KDE-ServiceTypes=Plasma/Applet,Plasma/PopupApplet +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= +X-KDE-PluginInfo-Category=Windows and Tasks diff --git a/config-X11.h.cmake b/config-X11.h.cmake new file mode 100644 index 0000000000..ee5183a632 --- /dev/null +++ b/config-X11.h.cmake @@ -0,0 +1,44 @@ +/* Define if you have the XRandR extension */ +#cmakedefine HAVE_XRANDR 1 + +/* Define if you have the XDamage extension */ +#cmakedefine HAVE_XDAMAGE 1 + +/* Define if you have the XKB extension */ +#cmakedefine HAVE_XKB 1 + +/* Define if you have the Xinerama extension */ +#cmakedefine HAVE_XINERAMA 1 + +/* Define if you have the XSHM (MIT SHM) extension */ +#cmakedefine HAVE_XSHM 1 + +/* Define if you have the XComposite extension */ +#cmakedefine HAVE_XCOMPOSITE 1 + +/* Define to 1 if you have Xcursor */ +#cmakedefine HAVE_XCURSOR 1 + +/* Define if you have the xf86misc extension */ +#cmakedefine HAVE_XF86MISC 1 + +/* Define if you have the XFixes extension */ +#cmakedefine HAVE_XFIXES 1 + +/* Define if you have the XTest extension */ +#cmakedefine HAVE_XTEST 1 + +/* Define if your system has XRender support */ +#cmakedefine HAVE_XRENDER 1 + +/* Define if you have OpenGL */ +#cmakedefine HAVE_OPENGL 1 + +/* Define if you have the XSync extension */ +#cmakedefine HAVE_XSYNC 1 + +/* Define if you have XRandR 1.3 */ +#cmakedefine HAS_RANDR_1_3 1 + +/* Define if you have X11 at all */ +#define HAVE_X11 ${X11_FOUND} \ No newline at end of file diff --git a/config-unix.h.cmake b/config-unix.h.cmake new file mode 100644 index 0000000000..ddb49b680d --- /dev/null +++ b/config-unix.h.cmake @@ -0,0 +1,35 @@ +/* Defines if you have PAM (Pluggable Authentication Modules) */ +#cmakedefine HAVE_PAM 1 + +/* Define if your PAM headers are in pam/ instead of security/ */ +#cmakedefine HAVE_PAM_PAM_APPL_H 1 + +/* Define if your PAM expects a conversation function with const pam_message (Solaris) */ +#cmakedefine PAM_MESSAGE_CONST 1 + +/* The PAM service to be used by kdm */ +#cmakedefine KDM_PAM_SERVICE ${KDM_PAM_SERVICE} + +/* The PAM service to be used by kscreensaver */ +#cmakedefine KSCREENSAVER_PAM_SERVICE ${KSCREENSAVER_PAM_SERVICE} + +/* Defines if your system has the getspnam function */ +#cmakedefine HAVE_GETSPNAM 1 + +/* Defines if your system has the crypt function */ +#cmakedefine HAVE_CRYPT 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CRYPT_H 1 + +/* Define to 1 if you have the `pw_encrypt' function. */ +#cmakedefine HAVE_PW_ENCRYPT 1 + +/* Define to 1 if you have the `getpassphrase' function. */ +#cmakedefine HAVE_GETPASSPHRASE 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#cmakedefine HAVE_VSYSLOG 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIMITS_H 1 diff --git a/config-workspace.h.cmake b/config-workspace.h.cmake new file mode 100644 index 0000000000..66c1d6359a --- /dev/null +++ b/config-workspace.h.cmake @@ -0,0 +1,159 @@ +/* config-workspace.h. Generated by cmake from config-workspace.h.cmake */ + +#cmakedefine HAVE_QIMAGEBLITZ + +/* Define if you have DPMS support */ +#cmakedefine HAVE_DPMS 1 + +/* Define if you have the DPMSCapable prototype in */ +#cmakedefine HAVE_DPMSCAPABLE_PROTO 1 + +/* Define if you have the DPMSInfo prototype in */ +#cmakedefine HAVE_DPMSINFO_PROTO 1 + +/* Defines if your system has the libfontconfig library */ +#cmakedefine HAVE_FONTCONFIG 1 + +/* Defines if your system has the freetype library */ +#cmakedefine HAVE_FREETYPE 1 + +/* Define if you have gethostname */ +#cmakedefine HAVE_GETHOSTNAME 1 + +/* Define if you have the gethostname prototype */ +#cmakedefine HAVE_GETHOSTNAME_PROTO 1 + +/* Define to 1 if you have the `getpeereid' function. */ +#cmakedefine HAVE_GETPEEREID 1 + +/* Defines if you have Solaris' libkstat */ +/* #undef HAVE_KSTAT */ + +/* Define if you have long long as datatype */ +#cmakedefine HAVE_LONG_LONG 1 + +/* Define to 1 if you have the `nice' function. */ +#cmakedefine HAVE_NICE 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SASL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SASL_SASL_H 1 + +/* Define to 1 if you have the `setpriority' function. */ +#cmakedefine HAVE_SETPRIORITY 1 + +/* Define to 1 if you have the `sigaction' function. */ +#cmakedefine HAVE_SIGACTION 1 + +/* Define to 1 if you have the `sigset' function. */ +#cmakedefine HAVE_SIGSET 1 + +/* Define to 1 if you have statvfs */ +#cmakedefine HAVE_STATVFS 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define if you have the struct ucred */ +#cmakedefine HAVE_STRUCT_UCRED 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_LOADAVG_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_MOUNT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STATFS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have statfs(). */ +#cmakedefine HAVE_STATFS 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_VFS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MALLOC_H 1 + +/* Define if you have unsetenv */ +#cmakedefine HAVE_UNSETENV 1 + +/* Define if you have the unsetenv prototype */ +#cmakedefine HAVE_UNSETENV_PROTO 1 + +/* Define if you have usleep */ +#cmakedefine HAVE_USLEEP 1 + +/* Define if you have the usleep prototype */ +#cmakedefine HAVE_USLEEP_PROTO 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#cmakedefine HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the Wayland libraries. */ +#cmakedefine WAYLAND_FOUND 1 + +/* KDE's default home directory */ +#cmakedefine KDE_DEFAULT_HOME "${KDE_DEFAULT_HOME}" + +/* KDE's binaries directory */ +#define KDE_BINDIR "${BIN_INSTALL_DIR}" + +/* KDE's configuration directory */ +#define KDE_CONFDIR "${CONFIG_INSTALL_DIR}" + +/* KDE's static data directory */ +#define KDE_DATADIR "${DATA_INSTALL_DIR}" + +/* Define where your java executable is */ +#undef PATH_JAVA + +/* Define to 1 if you can safely include both and . */ +#cmakedefine TIME_WITH_SYS_TIME 1 + +/* X binaries directory */ +#cmakedefine XBINDIR "${XBINDIR}" + +/* X libraries directory */ +#cmakedefine XLIBDIR "${XLIBDIR}" + +/* Number of bits in a file offset, on hosts where this is settable. */ +#define _FILE_OFFSET_BITS 64 + +/* + * On HP-UX, the declaration of vsnprintf() is needed every time ! + */ + +/* type to use in place of socklen_t if not defined */ +#define kde_socklen_t socklen_t + +#define WORKSPACE_VERSION_STRING "${KDE4WORKSPACE_VERSION}" \ No newline at end of file diff --git a/containments/CMakeLists.txt b/containments/CMakeLists.txt new file mode 100644 index 0000000000..3d364eef09 --- /dev/null +++ b/containments/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(desktop) +plasma_install_package(panel org.kde.panel plasma/plasmoids containment) diff --git a/containments/desktop/CMakeLists.txt b/containments/desktop/CMakeLists.txt new file mode 100644 index 0000000000..d66cd41460 --- /dev/null +++ b/containments/desktop/CMakeLists.txt @@ -0,0 +1,2 @@ +plasma_install_package(package org.kde.desktopcontainment plasma/plasmoids containment) +add_subdirectory(tests) diff --git a/containments/desktop/Messages.sh b/containments/desktop/Messages.sh new file mode 100644 index 0000000000..c8a9b6091b --- /dev/null +++ b/containments/desktop/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.qml` -L Java -o $podir/plasma_containment_org.kde.plasma.desktopcontainment.pot +rm -f rc.cpp diff --git a/containments/desktop/TODO b/containments/desktop/TODO new file mode 100644 index 0000000000..23fb4a670d --- /dev/null +++ b/containments/desktop/TODO @@ -0,0 +1,73 @@ + + == Declarative Desktop for Plasma == + + +TODO: +* hide handle after short timeout +* fix user interaction with rotation +* + + +BUGS: +* drag input is blocked for folderview, debug, applet is dragged instead of rubberband or items +* folderview doesn't have "run associated application" action +* blackboard doesn't have "run associated application" action + +DONE: +o ToolBox QML files move into their own package +o ToolBox is put on top of the scene in DeclarativeAppletScript::qmlCreationFinished() +o rename ToolBox.qml to ToolBoxItem.qml +o ToolBox-visible name to the runtime is ToolBox +o mouse drag expands over handle + o move mouseventlistener into subitem to be able to manage its margins / position + o everything else under the mouse *has to be* parented to this one, still +o minimum size for applethandle +o switch handle appearance when minimumSize is reached during resize +o implement rotation + o rotate item + o save rotation per item + o restore rotation per item +o rename main to root +o rename itemgroup to AppletItem +o meaningful name for plasmoidgroup or merge into AppletItem? +o open associated app + +OTHER: + +/** Action Mapping for ToolBox + +list-add Add Panel +list-add Add Widgets +preferences-activities Activities Activities +configure-shortcuts Shortcut Settings Shortcut Settings +configure $containment_name Settings $containment_name Settings +object-locked Lock Widgets +object-unlocked Unlock Widgets +system-lock-screen Lock Screen Lock Screen +system-shutdown Leave Leave + +**/ + +[17:04:19] sebas: i think should be in DeclarativeAppletScript::qmlCreationFinished, same thing as now checks if is a popupapplet and does compactrepresentation +[17:04:58] notmart: hah, I've that file open as well +[17:05:05] in init() +[17:05:50] So I'll split out the toolbox into its own QML Package +[17:06:38] but I'll still have to expose what is now InternalToolBox to the Containment +[17:06:52] right now, to have something that works quickly, you may just load a single file +[17:07:07] --> Savago (~adenilson@200.160.100.16) has joined #plasma +[17:07:21] it needs two elements anyway, so I suppose a package is fine +[17:07:28] (button to open it, the toolbox itself) +[17:07:41] aseigo: what do you think about that? ^^ (how to do toolbox) +[17:07:45] I've basically the QML code for those already written +[17:07:54] that's how i would do it, don't know if i'm overengineering +[17:08:05] <-> krake_ is now known as krake +[17:08:28] I don't really see a problem with putting the toolbox directly into the containment, but I might be missing some plugin architecture behind it +[17:08:42] and it should be possible to share the toolbox between containments +[17:08:49] terietor: m_namedFiles gets all the files with key names in the model... +[17:09:26] notmart: looks reasonable i think +[17:09:46] sebas: it gets used by several different containments +[17:09:50] folderview and desktop, e.g. +[17:10:00] notmart, aseigo: is the InternalToolBox api-wise OK with you to expose to both containment and ToolBox? +[17:10:46] terietor: and then in PackageModel::data it assumes that the # of files listed == the # of keys (m_namedFiles) +[17:10:57] sebas: looks sensible + diff --git a/containments/desktop/package/contents/code/LayoutManager.js b/containments/desktop/package/contents/code/LayoutManager.js new file mode 100644 index 0000000000..108987ffe9 --- /dev/null +++ b/containments/desktop/package/contents/code/LayoutManager.js @@ -0,0 +1,294 @@ +/* + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +.pragma library + +var positions = new Array(); + +var cellSize = new Object(); +cellSize.width = 24; +cellSize.height = 24; + +var defaultAppletSize = new Object(); +defaultAppletSize.width = cellSize.width * 6; +defaultAppletSize.height = cellSize.height * 6; + + +var resultsFlow +var plasmoid; +var itemsConfig + +//bookkeeping for the item groups +var itemGroups = new Object() + +function restore() +{ + var appletsMap = new Object(); + var applet; + for (var i = 0; i < plasmoid.applets.length; ++i) { + applet = plasmoid.applets[i]; + appletsMap["Applet-"+applet.id] = applet; + } + + itemsConfig = new Object(); + var configString = String(plasmoid.configuration.ItemsGeometries) + //print("Config read from configfile: " + configString); + //array, a cell for encoded item geometry + var itemsStrings = configString.split(";") + for (var i=0; i= Math.max(item.minimumWidth, cellSize.width) && + forwardAvail.height >= Math.max(item.minimumHeight, cellSize.height)) { + x = forwardX + y = forwardY + avail = forwardAvail + break + } + forwardX += cellSize.width + //new line + if (forwardX+item.width > resultsFlow.width) { + forwardX = 0 + forwardY += cellSize.height + //forward positions exausted + //scrolling now + /*if (forwardY > resultsFlow.height) { + break; + }*/ + } + + //backwards positions exausted + if (backY < 0) { + continue + } + + //look backwards + var backAvail = availableSpace(backX, backY, + Math.max(item.minimumWidth, item.width), + Math.max(item.minimumHeight, item.height)) + //print("checking backwards "+backX/cellSize.width+" "+backY/cellSize.height+" "+backAvail.width/cellSize.width+" "+backAvail.height/cellSize.height) + + if (backAvail.width >= Math.max(item.minimumWidth, cellSize.width) && + backAvail.height >= Math.max(item.minimumHeight, cellSize.height)) { + x = backX + y = backY + avail = backAvail + break + } + backX -= cellSize.width + if (backX < 0) { + backX = resultsFlow.width - item.width + backY -= cellSize.height + } + } + + var width = Math.max(cellSize.width*Math.max(1, Math.ceil(item.minimumWidth/cellSize.width)), + Math.round(Math.min(avail.width, item.width)/cellSize.width)*cellSize.width) + var height = Math.max(cellSize.height*Math.max(1, Math.ceil(item.minimumHeight/cellSize.height)), + Math.round(Math.min(avail.height, item.height)/cellSize.height)*cellSize.height) + + setSpaceAvailable(x, y, width, height, false) + //resultsFlow.height = Math.max(resultsFlow.height, y+cellSize.height) + + item.x = x + item.y = y + item.width = width + item.height = height + + saveItem(item); +} + +function removeApplet(applet) { + // Keeps config in sync, deletion of items in the scene + // are handled in AppletAppearance and main.qml + delete itemsConfig["Applet-"+applet.id]; + delete itemGroups["Applet-"+applet.id]; +} + +function saveItem(item) { + var rect = new Object() + rect.x = item.x + rect.y = item.y + rect.width = item.width + rect.height = item.height + rect.rotation = item.rotation + //print("-- Saving rotation : " + rect.rotation); + //save only things that actually have a category (exclude the placeholder) + if (item.category) { + //print("-- Saving rotation : " + rect.rotation); + itemsConfig[item.category] = rect + } + +} diff --git a/containments/desktop/package/contents/config/main.xml b/containments/desktop/package/contents/config/main.xml new file mode 100644 index 0000000000..d889999299 --- /dev/null +++ b/containments/desktop/package/contents/config/main.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + topleft + + + + + + + + + + + + + true + + + + diff --git a/containments/desktop/package/contents/images/resize-handle.svgz b/containments/desktop/package/contents/images/resize-handle.svgz new file mode 100755 index 0000000000..888b14e169 Binary files /dev/null and b/containments/desktop/package/contents/images/resize-handle.svgz differ diff --git a/containments/desktop/package/contents/ui/ActionButton.qml b/containments/desktop/package/contents/ui/ActionButton.qml new file mode 100644 index 0000000000..ff0322fa31 --- /dev/null +++ b/containments/desktop/package/contents/ui/ActionButton.qml @@ -0,0 +1,144 @@ +/* + * Copyright 2011 Marco Martin + * Copyright 2013 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: button + + //API + property QtObject svg + property alias elementId: icon.elementId + property QtObject action + property bool backgroundVisible: false + property int iconSize: 32 + property int pressedOffset: 1 + property bool checked: false + property bool toggle: false + property string text + signal clicked + + width: buttonRow.width + height: buttonRow.height + + opacity: action==undefined||action.enabled?1:0.6 + + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + + onCheckedChanged: { + if (checked) { + buttonItem.elementId = "pressed" + shadowItem.opacity = 0 + } else { + buttonItem.elementId = "normal" + shadowItem.opacity = 1 + } + } + + PlasmaCore.Svg { + id: buttonSvg + imagePath: "widgets/actionbutton" + } + + PlasmaCore.SvgItem { + id: shadowItem + svg: buttonSvg + elementId: "shadow" + width: iconSize+13//button.backgroundVisible?iconSize+8:iconSize + height: width + visible: button.backgroundVisible + } + + Row { + id: buttonRow + + Item { + width: buttonItem.visible?buttonItem.width:iconSize + height: buttonItem.visible?buttonItem.height:iconSize + + PlasmaCore.SvgItem { + id: buttonItem + svg: buttonSvg + elementId: "normal" + width: shadowItem.width + height: shadowItem.height + visible: backgroundVisible + } + + PlasmaCore.SvgItem { + id: icon + width: iconSize + height: iconSize + svg: button.svg + anchors.centerIn: parent + } + } + + Text { + id: actionText + text: button.text + style: Text.Outline + color: theme.textColor + styleColor: Qt.rgba(1,1,1,0.4) + anchors.verticalCenter: parent.verticalCenter + } + } + + MouseArea { + anchors.fill: parent + anchors.leftMargin: -10 + anchors.topMargin: -10 + anchors.rightMargin: -10 + anchors.bottomMargin: -10 + preventStealing: true + onPressed: { + buttonItem.elementId = "pressed" + shadowItem.opacity = 0; + button.x = button.x + button.pressedOffset; + button.y = button.y + button.pressedOffset; + } + onReleased: { + if (button.checked || !button.toggle) { + buttonItem.elementId = "normal" + shadowItem.opacity = 1 + button.checked = false + } else { + button.checked = true + } + button.x = button.x - button.pressedOffset; + button.y = button.y - button.pressedOffset; + } + onClicked: { + if (action) { + action.trigger() + } else { + button.clicked() + } + } + } + Rectangle { color: "white"; opacity: 0.4; visible: debug; anchors.fill: parent; } +} diff --git a/containments/desktop/package/contents/ui/AppletAppearance.qml b/containments/desktop/package/contents/ui/AppletAppearance.qml new file mode 100644 index 0000000000..3fd88b376b --- /dev/null +++ b/containments/desktop/package/contents/ui/AppletAppearance.qml @@ -0,0 +1,376 @@ +/* + * Copyright 2011-2013 Sebastian Kügler + * Copyright 2011 Marco Martin + * Copyright 2014 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +Item { + id: appletItem + + anchors.rightMargin: -handleWidth*controlsOpacity + + property int handleWidth: iconSize + 8 // 4 pixels margins inside handle + property int minimumHandleHeight: 6 * (root.iconSize + 6) + margins.top + margins.bottom + property int handleHeight: (height < minimumHandleHeight) ? minimumHandleHeight : height + property string category + + property bool showAppletHandle: false + property real controlsOpacity: (plasmoid.immutable || !showAppletHandle) ? 0 : 1 + property bool handleShown: true + property string backgroundHints: "NoBackground" + property bool hasBackground: false + property bool handleMerged: (height > minimumHandleHeight) + property bool animationsEnabled: false + + property int minimumWidth: Math.max(LayoutManager.cellSize.width, + appletContainer.minimumWidth + + appletItem.contents.anchors.leftMargin + + appletItem.contents.anchors.rightMargin) + + property int minimumHeight: Math.max(LayoutManager.cellSize.height, + appletContainer.minimumHeight + + appletItem.contents.anchors.topMargin + + appletItem.contents.anchors.bottomMargin) + + property alias applet: appletContainer.applet + + property Item contents: appletContainer + property alias margins: plasmoidBackground.margins + property alias imagePath: plasmoidBackground.imagePath + + visible: false + + //FIXME: this delay is because backgroundHints gets updated only after a while in qml applets + Timer { + id: appletTimer + interval: 50 + repeat: false + running: false + onTriggered: { + updateBackgroundHints(); + applet.parent = appletContainer; + applet.anchors.fill = appletContainer; + } + } + + function updateBackgroundHints() { + hasBackground = (applet.backgroundHints != "NoBackground"); + if (applet.backgroundHints == 1) { + appletItem.imagePath = "widgets/background"; + backgroundHints = "StandardBackground"; + } else if (applet.backgroundHints == 2) { + appletItem.imagePath = "widgets/translucentbackground" + backgroundHints = "TranslucentBackground"; + } else if (applet.backgroundHints == 0) { + appletItem.imagePath = "" + backgroundHints = "NoBackground"; + } else { + backgroundHints = "DefaultBackground"; + appletItem.imagePath = "widgets/background"; + } + //print("Backgroundhints changed: " + appletItem.imagePath); + } + + Rectangle { color: Qt.rgba(0,0,0,0); border.width: 3; border.color: "white"; opacity: 0.5; visible: debug; anchors.fill: parent; } + + KQuickControlsAddons.MouseEventListener { + id: mouseListener + + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + height: handleHeight + width: parent.width+handleWidth; + z: 10 + + hoverEnabled: true + + onPressAndHold: { + if (root.pressAndHoldHandle && !plasmoid.immutable) { + //hoverTracker.interval = 400; + hoverTracker.restart(); + } + } + + onContainsMouseChanged: { + + animationsEnabled = true; + //print("Mouse is " + containsMouse); + if (!plasmoid.immutable && containsMouse) { + if (!root.pressAndHoldHandle) { + hoverTracker.interval = root.handleDelay; + hoverTracker.restart(); + } + } else { + hoverTracker.stop(); + showAppletHandle = false; + } + } + + Timer { + id: hoverTracker + repeat: false + interval: handleDelay + onTriggered: showAppletHandle = true; + } + Rectangle { color: Qt.rgba(0,0,0,0); border.width: 3; border.color: "red"; opacity: 0.5; visible: debug; anchors.fill: parent; } + + + + Item { + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } + width: parent.width+handleWidth; + + z: mouseListener.z + 4 + + PlasmaCore.FrameSvgItem { + id: plasmoidBackground + visible: backgroundHints != "NoBackground" + imagePath: "widgets/background" + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } + width: (showAppletHandle && handleMerged) ? parent.width : parent.width-handleWidth; + smooth: true + + Behavior on width { + enabled: animationsEnabled + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + + Connections { + target: plasmoid + onImmutableChanged: { + print(" TB dragMouseArea.visible: " + plasmoid.immutable) + dragMouseArea.visible = !plasmoid.immutable; + showAppletHandle = false; + } + onAppletRemoved: { + print("Applet removed Applet-" + applet.id) + if (applet.id == appletItem.applet.id) { + print("Destroying Applet-" + applet.id) + LayoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) + //applet.action("remove").trigger(); + //appletItem.destroy() + appletItem.destroy(); + } + } + } + Connections { + target: applet + onBusyChanged: { + if (applet.busy) { + busyLoader.source = "BusyOverlay.qml" + } else if (busyLoader.item && typeof(busyLoader.item) != "undefined") { + busyLoader.item.disappear(); + } + } + onBackgroundHintsChanged: { + print("plasmoid.backgroundHintsChanged"); + updateBackgroundHints(); + } + } + + MouseArea { + id: dragMouseArea + + anchors.fill: parent + z: appletContainer.z - 2 + + property int lastX + property int lastY + property int zoffset: 1000 + + onPressed: { + appletItem.z = appletItem.z + zoffset; + animationsEnabled = false + mouse.accepted = true + var x = Math.round(appletItem.x/LayoutManager.cellSize.width)*LayoutManager.cellSize.width + var y = Math.round(appletItem.y/LayoutManager.cellSize.height)*LayoutManager.cellSize.height + LayoutManager.setSpaceAvailable(x, y, appletItem.width, appletItem.height, true) + + var globalMousePos = mapToItem(root, mouse.x, mouse.y) + lastX = globalMousePos.x + lastY = globalMousePos.y + + + placeHolder.syncWithItem(appletItem) + placeHolderPaint.opacity = root.haloOpacity; + } + onPositionChanged: { + placeHolder.syncWithItem(appletItem) + + var globalPos = mapToItem(root, x, y) + + var globalMousePos = mapToItem(root, mouse.x, mouse.y) + appletItem.x += (globalMousePos.x - lastX) + appletItem.y += (globalMousePos.y - lastY) + + lastX = globalMousePos.x + lastY = globalMousePos.y + } + onReleased: { + appletItem.z = appletItem.z - zoffset; + repositionTimer.running = false + placeHolderPaint.opacity = 0 + animationsEnabled = true + LayoutManager.positionItem(appletItem) + LayoutManager.save() + } + } + + Item { + id: appletContainer + anchors { + fill: parent + leftMargin: plasmoidBackground.margins.left + rightMargin: plasmoidBackground.margins.right + handleWidth + topMargin: plasmoidBackground.margins.top + bottomMargin: plasmoidBackground.margins.bottom + } + z: mouseListener.z+1 + + property QtObject applet + + property int minimumWidth: applet && applet.Layout ? applet.Layout.minimumWidth : 0 + property int minimumHeight: applet && applet.Layout ? applet.Layout.minimumHeight : 0 + + function appletDestroyed() { + print("Applet DESTROYED."); + LayoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) + applet.action("remove").trigger(); + appletItem.destroy() + } + + onAppletChanged: { + if (applet) { + appletTimer.running = true; + } + } + Connections { + target: appletHandle.item + onRemoveApplet: { + killAnim.running = true; + } + } + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + SequentialAnimation { + id: killAnim + PlasmaExtras.DisappearAnimation { + targetItem: appletItem + } + ScriptAction { + script: appletContainer.appletDestroyed() + } + } + Loader { + id: busyLoader + anchors.centerIn: parent + z: appletContainer.z + 1 + } + Rectangle { color: "green"; opacity: 0.3; visible: debug; anchors.fill: parent; } + Component.onCompleted: PlasmaExtras.AppearAnimation { + targetItem: appletItem + } + } + + Loader { + id: appletHandle + z: appletContainer.z + 1 + anchors { + top: parent.top + bottom: parent.bottom + right: plasmoidBackground.right + rightMargin: appletItem.margins.right + } + Connections { + target: appletItem + onShowAppletHandleChanged: { + if (appletItem.showAppletHandle && appletHandle.source == "") { + //print("Loading applethandle "); + appletHandle.source = "AppletHandle.qml"; + } + } + } + + } + + Rectangle { color: "orange"; opacity: 0.1; visible: debug; anchors.fill: parent; } + } + } + + Behavior on controlsOpacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on x { + enabled: animationsEnabled + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on y { + enabled: animationsEnabled + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on width { + enabled: animationsEnabled + NumberAnimation { + id: widthAnimation + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on height { + enabled: animationsEnabled + NumberAnimation { + id: heightAnimation + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + + Component.onCompleted: { + layoutTimer.running = true + layoutTimer.restart() + visible = false + // restore rotation + } +} diff --git a/containments/desktop/package/contents/ui/AppletHandle.qml b/containments/desktop/package/contents/ui/AppletHandle.qml new file mode 100644 index 0000000000..eabe15ea41 --- /dev/null +++ b/containments/desktop/package/contents/ui/AppletHandle.qml @@ -0,0 +1,260 @@ +/* + * Copyright 2011-2013 Sebastian Kügler + * Copyright 2011 Marco Martin + * Copyright 2014 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +Item { + id: appletHandle + + opacity: appletItem.controlsOpacity + visible: opacity > 0 + width: appletItem.handleWidth + //z: dragMouseArea.z + 1 + + property int buttonMargin: 6 + property int minimumHeight: 6 * (root.iconSize + buttonMargin) + property Item buttonsParent: appletItem.handleMerged ? appletHandle : noBackgroundHandle + + signal removeApplet + + transform: Translate { + x: handleMerged ? 0 : controlsOpacity * appletHandle.width + } + + PlasmaCore.FrameSvgItem { + id: noBackgroundHandle + visible: controlsOpacity > 0 + z: plasmoidBackground.z - 10 + + height: (backgroundHints != "NoBackground") ? appletItem.handleHeight : appletItem.handleHeight + noBackgroundHandle.margins.top + noBackgroundHandle.margins.bottom + + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + + leftMargin: -margins.left + topMargin: -margins.top + rightMargin: -margins.right + bottomMargin: -margins.bottom + } + + smooth: true + imagePath: (backgroundHints == "NoBackground" || !handleMerged) ? "widgets/background" : "" + } + + ColumnLayout { + id: buttonColumn + width: handleWidth + parent: buttonsParent + anchors { + top: parent.top + topMargin: (!appletItem.hasBackground || !handleMerged) ? noBackgroundHandle.margins.top : appletItem.margins.top + bottom: parent.bottom + bottomMargin: (!appletItem.hasBackground && !handleMerged) ? noBackgroundHandle.margins.bottom : appletItem.margins.bottom + right: parent.right + rightMargin: appletItem.handleMerged ? -buttonMargin : noBackgroundHandle.margins.right - buttonMargin + } + spacing: buttonMargin*2 + ActionButton { + svg: configIconsSvg + elementId: "size-diagonal-tr2bl" + iconSize: root.iconSize + visible: (action && typeof(action) != "undefined") ? action.enabled : false + action: (applet) ? applet.action("configure") : null + Component.onCompleted: { + if (action && typeof(action) != "undefined") { + action.enabled = true + } + } + + MouseArea { + id: resizeHandle + anchors { + fill: parent + margins: -buttonMargin + } + + property int startX + property int startY + + onPressed: { + mouse.accepted = true + animationsEnabled = false; + startX = mouse.x; + startY = mouse.y; + LayoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) + } + onPositionChanged: { + appletItem.width = Math.max(appletItem.minimumWidth, appletItem.width + mouse.x-startX) + appletItem.y = appletItem.y + (mouse.y-startY); + appletItem.height = Math.max(appletItem.minimumHeight, appletItem.height + startY-mouse.y) + } + onReleased: { + animationsEnabled = true + LayoutManager.positionItem(appletItem) + LayoutManager.save() + LayoutManager.setSpaceAvailable(appletItem.x, appletItem.y, widthAnimation.to, heightAnimation.to, false) + } + Rectangle { color: "blue"; opacity: 0.4; visible: debug; anchors.fill: parent; } + } + } + ActionButton { + id: rotateButton + svg: configIconsSvg + elementId: "rotate" + iconSize: root.iconSize + action: (applet) ? applet.action("rotate") : null + Component.onCompleted: { + if (action && typeof(action) != "undefined") { + action.enabled = true + } + } + MouseArea { + + anchors { + fill: parent + margins: -buttonMargin + } + + property int startRotation + property real startCenterRelativeAngle; + + function pointAngle(pos) { + var r = Math.sqrt(pos.x * pos.x + pos.y * pos.y); + var cosine = pos.x / r; + + if (pos.y >= 0) { + return Math.acos(cosine) * (180/Math.PI); + } else { + return -Math.acos(cosine) * (180/Math.PI); + } + } + + function centerRelativePos(x, y) { + var mousePos = appletItem.parent.mapFromItem(rotateButton, x, y); + var centerPos = appletItem.parent.mapFromItem(appletItem, appletItem.width/2, appletItem.height/2); + + mousePos.x -= centerPos.x; + mousePos.y -= centerPos.y; + return mousePos; + } + + onPressed: { + mouse.accepted = true + animationsEnabled = false; + startRotation = appletItem.rotation; + LayoutManager.setSpaceAvailable(appletItem.x, appletItem.y, appletItem.width, appletItem.height, true) + + startCenterRelativeAngle = pointAngle(centerRelativePos(mouse.x, mouse.y)); + } + onPositionChanged: { + + var rot = startRotation%360; + var snap = 4; + var newRotation = pointAngle(centerRelativePos(mouse.x, mouse.y)) - startCenterRelativeAngle + startRotation; + + if (newRotation < 0) { + newRotation = newRotation + 360; + } + + snapIt(0); + snapIt(90); + snapIt(180); + snapIt(270); + + function snapIt(snapTo) { + if (newRotation > (snapTo - snap) && newRotation < (snapTo + snap)) { + newRotation = snapTo; + } + } + print("Start: " + startRotation + " new: " + newRotation); + appletItem.rotation = newRotation; + } + onReleased: { + // save rotation +// print("saving..."); + LayoutManager.saveItem(appletItem); + } + Rectangle { color: "red"; opacity: 0.6; visible: debug; anchors.fill: parent; } + } + } + ActionButton { + svg: configIconsSvg + elementId: "configure" + iconSize: root.iconSize + visible: (action && typeof(action) != "undefined") ? action.enabled : false + action: (applet) ? applet.action("configure") : null + Component.onCompleted: { + if (action && typeof(action) != "undefined") { + action.enabled = true + } + } + } + ActionButton { + svg: configIconsSvg + elementId: "size-diagonal-tr2bl" // FIXME should be maximize + //elementId: "maximize" + iconSize: root.iconSize + visible: (action && typeof(action) != "undefined") ? action.enabled : false + action: (applet) ? applet.action("run associated application") : null + Component.onCompleted: { + if (action && typeof(action) != "undefined") { + action.enabled = true + } + } + } + + //spacer + Item { + Layout.fillHeight: true + height: 0 + } + + ActionButton { + svg: configIconsSvg + elementId: "close" + iconSize: root.iconSize + visible: { + var a = applet.action("remove"); + return (a && typeof(a) != "undefined") ? a.enabled : false; + } + // we don't set action, since we want to catch the button click, + // animate, and then trigger the "remove" action + // Triggering the action is handled in the appletItem, we just + // emit a signal here to avoid the applet-gets-removed-before-we- + // can-animate it race condition. + onClicked: { + appletHandle.removeApplet(); + } + Component.onCompleted: { + var a = applet.action("remove"); + if (a && typeof(a) != "undefined") { + a.enabled = true + } + } + } + } +} diff --git a/containments/desktop/package/contents/ui/BusyOverlay.qml b/containments/desktop/package/contents/ui/BusyOverlay.qml new file mode 100644 index 0000000000..33d72c0f98 --- /dev/null +++ b/containments/desktop/package/contents/ui/BusyOverlay.qml @@ -0,0 +1,64 @@ +/* + * Copyright 2011-2013 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +PlasmaComponents.BusyIndicator { + id: busyIndicator + + anchors.centerIn: parent + z: appletContainer.z + 1 + visible: applet.busy + + running: visible + + function disappear() { + appearAnim.running = false; + disappearAnim.running = true; + } + + onRunningChanged: { + if (running) { + disappearAnim.running = false; + appearAnim.running = true; + } + } + + PlasmaExtras.AppearAnimation { + id: appearAnim + targetItem: busyIndicator + running: false + } + + SequentialAnimation { + id: disappearAnim + running: false + PlasmaExtras.DisappearAnimation { + targetItem: busyIndicator + } + ScriptAction { + script: { + // unload ourselves + busyLoader.source = ""; + } + } + } +} diff --git a/containments/desktop/package/contents/ui/ContainmentConfig.qml b/containments/desktop/package/contents/ui/ContainmentConfig.qml new file mode 100644 index 0000000000..ea6c7bd504 --- /dev/null +++ b/containments/desktop/package/contents/ui/ContainmentConfig.qml @@ -0,0 +1,127 @@ +/* + * Copyright 2011-2013 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +Item { + id: containmentConfig + + width: 400 + height: 320 + + property bool open: false + + PlasmaComponents.ToolButton { + id: cfgbutton + + width: root.iconSize + height: width + + iconSource: "configure" + onClicked: open = !open; + } + + PlasmaCore.FrameSvgItem { + id: configFrame + anchors.fill: parent + imagePath: "widgets/background" + opacity: open ? 1 : 0 + Behavior on opacity { PropertyAnimation {} } + + Row { + anchors.fill: parent + anchors.margins: parent.margins.top + + spacing: configFrame.margins.top/2 + + Column { + width: parent.width / 2 - (configFrame.margins.top) + spacing: configFrame.margins.top/2 + PlasmaComponents.Label { + width: parent.width + horizontalAlignment: Text.AlignRight + text: "Visual debugging:" + } + PlasmaComponents.Label { + width: parent.width + horizontalAlignment: Text.AlignRight + text: "Icon size:" + } + PlasmaComponents.Label { + width: parent.width + horizontalAlignment: Text.AlignRight + text: "Applethandle delay:" + } + PlasmaComponents.Label { + width: parent.width + horizontalAlignment: Text.AlignRight + text: "Halo move duration:" + } + PlasmaComponents.Label { + width: parent.width + horizontalAlignment: Text.AlignRight + text: "Halo opacity:" + } + } + + Column { + spacing: configFrame.margins.top/2 + PlasmaComponents.CheckBox { + checked: root.debug + onCheckedChanged: { + print("Debug is now " + checked); + root.debug = checked; + } + } + PlasmaComponents.TextField { + text: root.iconSize + onTextChanged: { + print("Set icon size to " + text); + root.iconSize = text; + } + } + PlasmaComponents.TextField { + text: root.handleDelay + onTextChanged: { + print("Set handle delay to " + text); + root.handleDelay = text; + } + } + PlasmaComponents.TextField { + text: placeHolderPaint.moveDuration + onTextChanged: { + print("Set move duration to " + text); + placeHolderPaint.moveDuration = text; + } + } + PlasmaComponents.TextField { + text: root.haloOpacity + onTextChanged: { + print("Set halo opacity to " + text); + root.haloOpacity = text; + } + } + } + } + } +} diff --git a/containments/desktop/package/contents/ui/main.qml b/containments/desktop/package/contents/ui/main.qml new file mode 100644 index 0000000000..9b72842028 --- /dev/null +++ b/containments/desktop/package/contents/ui/main.qml @@ -0,0 +1,315 @@ +// -*- coding: iso-8859-1 -*- +/* + * Copyright 2011-2013 Sebastian Kügler + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.draganddrop 2.0 as DragDrop + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +DragDrop.DropArea { + id: root + + property Item toolBox + + property bool debug: false + property int handleDelay: 800 + property real haloOpacity: 0.5 + property bool pressAndHoldHandle: false + + property int iconSize: 16 + property int iconWidth: iconSize + property int iconHeight: iconWidth + + onIconHeightChanged: updateGridSize() + + function updateGridSize() + { + print("Updategridsize"); + LayoutManager.cellSize.width = root.iconWidth + toolBoxSvg.elementSize("left").width + toolBoxSvg.elementSize("right").width + LayoutManager.cellSize.height = root.iconHeight + toolBoxSvg.elementSize("top").height + toolBoxSvg.elementSize("bottom").height; + LayoutManager.defaultAppletSize.width = LayoutManager.cellSize.width * 6; + LayoutManager.defaultAppletSize.height = LayoutManager.cellSize.height * 6; + layoutTimer.restart() + } + + function addApplet(applet, x, y) { + var component = Qt.createComponent("AppletAppearance.qml"); + var e = component.errorString(); + if (e != "") { + print("Error loading AppletAppearance.qml: " + component.errorString()); + } + + var container = component.createObject(resultsFlow) + + applet.parent = container + applet.visible = true; + + container.category = "Applet-"+applet.id; + var config = LayoutManager.itemsConfig[container.category]; + + //Not a valid size? reset + if (config === undefined || config.width === undefined || config.height === undefined || + config.width <= 0 || config.height <=0) { + container.width = LayoutManager.defaultAppletSize.width; + container.height = LayoutManager.defaultAppletSize.height; + } else { + container.width = config.width; + container.height = config.height; + } + container.applet = applet; + //coordinated passed by param? + if ( x >= 0 && y >= 0) { + container.x = x; + container.y = y; + //coordinates stored? + } else if (config !== undefined && config.x !== undefined && config.y !== undefined && + config.x >= 0 && config.y >= 0) { + container.x = config.x; + container.y = config.y; + } + + //rotation sotired and significative? + if (config !== undefined && config.rotation !== undefined && + (config.rotation > 5 || config.rotation < -5)) { + container.rotation = config.rotation; + } + + LayoutManager.itemGroups[container.category] = container; + print("Applet " + container.category + applet.title + " added at" + container.x + " " + container.y); + + if (container.x >= 0 && container.y >= 0) { + LayoutManager.positionItem(container); + } + } + + onDragEnter: { + placeHolder.width = LayoutManager.defaultAppletSize.width; + placeHolder.height = LayoutManager.defaultAppletSize.height; + placeHolder.x = event.x - placeHolder.width / 2; + placeHolder.y = event.y - placeHolder.width / 2; + LayoutManager.positionItem(placeHolder); + LayoutManager.setSpaceAvailable(placeHolder.x, placeHolder.y, placeHolder.width, placeHolder.height, true); + placeHolderPaint.opacity = root.haloOpacity; + } + + onDragMove: { + placeHolder.width = LayoutManager.defaultAppletSize.width; + placeHolder.height = LayoutManager.defaultAppletSize.height; + placeHolder.x = event.x - placeHolder.width / 2; + placeHolder.y = event.y - placeHolder.width / 2; + LayoutManager.positionItem(placeHolder); + LayoutManager.setSpaceAvailable(placeHolder.x, placeHolder.y, placeHolder.width, placeHolder.height, true); + placeHolderPaint.opacity = root.haloOpacity; + } + + onDragLeave: { + placeHolderPaint.opacity = 0; + } + + onDrop: { + placeHolderPaint.opacity = 0; + plasmoid.processMimeData(event.mimeData, event.x - placeHolder.width / 2, event.y - placeHolder.height / 2); + } + + + Connections { + target: plasmoid + + onAppletAdded: { + addApplet(applet, x, y); + //clean any eventual invalid chunks in the config + LayoutManager.save(); + } + + onAppletRemoved: { + //clean any eventual invalid chunks in the config + print("Applet REMOVED!!!!!!!!!"); + LayoutManager.removeApplet(applet); + LayoutManager.save(); + } + } + + PlasmaCore.Svg { + id: toolBoxSvg + imagePath: "widgets/toolbox" + property int rightBorder: elementSize("right").width + property int topBorder: elementSize("top").height + property int bottomBorder: elementSize("bottom").height + property int leftBorder: elementSize("left").width + } + PlasmaCore.Svg { + id: configIconsSvg + imagePath: "widgets/configuration-icons" + } + + Item { + id: resultsFlow + anchors.fill: parent + + anchors { + top: parent.top + topMargin: 5 + horizontalCenter: parent.horizontalCenter + } + + //This is just for event compression when a lot of boxes are created one after the other + Timer { + id: layoutTimer + repeat: false + running: false + interval: 100 + onTriggered: { + LayoutManager.resetPositions() + for (var i=0; i 0 + NumberAnimation { + duration: placeHolderPaint.moveDuration + easing.type: Easing.InOutQuad + } + } + Behavior on y { + enabled: placeHolderPaint.opacity > 0 + NumberAnimation { + duration: placeHolderPaint.moveDuration + easing.type: Easing.InOutQuad + } + } + Behavior on width { + enabled: placeHolderPaint.opacity > 0 + NumberAnimation { + duration: placeHolderPaint.moveDuration + easing.type: Easing.InOutQuad + } + } + } + } + + Component.onCompleted: { + placeHolderPaint.opacity = 0; + placeHolderPaint.visible = true; + print("Containment completed."); + LayoutManager.resultsFlow = resultsFlow + LayoutManager.plasmoid = plasmoid + updateGridSize() + //plasmoid.containmentType = "DesktopContainment" + + LayoutManager.restore() + + for (var i = 0; i < plasmoid.applets.length; ++i) { + var applet = plasmoid.applets[i] + addApplet(applet, -1, -1); + } + //clean any eventual invalid chunks in the config + LayoutManager.save(); + } +} diff --git a/containments/desktop/package/metadata.desktop b/containments/desktop/package/metadata.desktop new file mode 100644 index 0000000000..2ab7289592 --- /dev/null +++ b/containments/desktop/package/metadata.desktop @@ -0,0 +1,152 @@ +[Desktop Entry] +Name=Desktop +Name[af]=Werkskerm +Name[ar]=سطح المكتب +Name[ast]=Escritoriu +Name[be]=Працоўны стол +Name[be@latin]=Rabočy stoł +Name[bg]=Работен плот +Name[bn]=ডেস্কটপ +Name[bn_IN]=ডেস্কটপ +Name[br]=Gorretaol +Name[ca]=Escriptori +Name[ca@valencia]=Escriptori +Name[cs]=Pracovní plocha +Name[csb]=Pùlt +Name[cy]=Penbwrdd +Name[da]=Skrivebord +Name[de]=Arbeitsfläche +Name[el]=Επιφάνεια εργασίας +Name[en_GB]=Desktop +Name[eo]=Labortablo +Name[es]=Escritorio +Name[et]=Töölaud +Name[eu]=Mahaigaina +Name[fa]=رومیزی +Name[fi]=Työpöytä +Name[fr]=Bureau +Name[fy]=Buroblêd +Name[ga]=Deasc +Name[gl]=Escritorio +Name[gu]=ડેસ્કટોપ +Name[he]=שולחן עבודה +Name[hi]=डेस्कटॉप +Name[hne]=डेस्कटाप +Name[hsb]=Dźěłowy powjerch +Name[hu]=Munkaasztal +Name[ia]=Scriptorio +Name[id]=Desktop +Name[it]=Desktop +Name[ka]=სამუშაო დაფა +Name[kk]=Жұмыс үстелі +Name[kn]=ಗಣಕತೆರೆ +Name[ko]=데스크톱 +Name[ku]=Sermasê +Name[lt]=Darbastalis +Name[mai]=डेस्कटाप +Name[mk]=Работна површина +Name[ml]=പണിയിടം +Name[mr]=डेस्कटॉप +Name[ms]=Ruang Kerja +Name[nb]=Skrivebord +Name[nds]=Schriefdisch +Name[ne]=डेस्कटप +Name[nl]=Bureaublad +Name[nn]=Skrivebord +Name[oc]=Burèu +Name[or]=ଡ଼େସ୍କଟପ +Name[pa]=ਡੈਸਕਟਾਪ +Name[pl]=Pulpit +Name[pt]=Ambiente de Trabalho +Name[pt_BR]=Área de trabalho +Name[ro]=Birou +Name[se]=Čállinbeavdi +Name[si]=වැඩතලය +Name[sk]=Plocha +Name[sl]=Namizje +Name[sr]=Површ +Name[sr@ijekavian]=Површ +Name[sr@ijekavianlatin]=Površ +Name[sr@latin]=Površ +Name[sv]=Skrivbord +Name[ta]=பணிமேசை +Name[te]=రంగస్ఠలం +Name[tg]=Мизи корӣ +Name[th]=พื้นที่ทำงาน +Name[tr]=Masaüstü +Name[uk]=Стільниця +Name[uz]=Ish stoli +Name[uz@cyrillic]=Иш столи +Name[vi]=Màn hình nền +Name[wa]=Sicribanne +Name[xh]=Desktop +Name[x-test]=xxDesktopxx +Name[zh_CN]=桌面 +Name[zh_TW]=桌面 +Comment=A clean and simple layout +Comment[bs]=Čist i jednostavan Izgled +Comment[ca]=Una disposició neta i senzilla +Comment[ca@valencia]=Una disposició neta i senzilla +Comment[cs]=Čisté a jednoduché rozvržení +Comment[da]=Et rent og simpelt layout +Comment[de]=Ein klares und einfaches Layout +Comment[el]=Μια σαφής και απλή διάταξη +Comment[es]=Un esquema sencillo y claro +Comment[et]=Puhas ja lihtne paigutus +Comment[eu]=Diseinu garbi eta xumea +Comment[fi]=Selkeä ja yksinkertainen asettelu +Comment[fr]=Une disposition simple et épurée +Comment[gl]=Unha disposición sinxela e limpa +Comment[hu]=Egy tiszta és egyszerű elrendezés +Comment[ia]=Un disposition simple e munde +Comment[it]=Una disposizione semplice e pulita +Comment[kk]=Анық және қарапайым қалыпы +Comment[km]=ប្លង់​សាមញ្ញ ហើយ​​ស្អាត +Comment[ko]=깨끗하고 정돈된 레이아웃 +Comment[lt]=Švarus ir paprastas Išdėstymas +Comment[mr]=स्वच्छ व साधी रचना +Comment[nb]=En ren og enkel utforming +Comment[nds]=En schier un eenfach Utsehn +Comment[nl]=Een schone en simpele opmaak +Comment[pa]=ਸਾਫ਼ ਅਤੇ ਸਧਾਰਨ ਲੇਆਉਟ +Comment[pl]=Schludny i prosty układ +Comment[pt]=Uma disposição limpa e simples +Comment[pt_BR]=Um leiaute limpo e simples +Comment[ro]=Aranjament curat și simplu +Comment[ru]=Ясный и простой шаблон +Comment[sk]=Čisté a jednoduché rozloženie +Comment[sl]=Čist in preprost razpored +Comment[sr]=Чист и једноставан распоред +Comment[sr@ijekavian]=Чист и једноставан распоред +Comment[sr@ijekavianlatin]=Čist i jednostavan raspored +Comment[sr@latin]=Čist i jednostavan raspored +Comment[sv]=En ren och enkel layout +Comment[tr]=Temiz ve basit bir yerleşim +Comment[uk]=Просте компонування +Comment[x-test]=xxA clean and simple layoutxx +Comment[zh_CN]=简洁的布局 +Comment[zh_TW]=乾淨、簡單的佈局 +Encoding=UTF-8 + +Type=Service +Icon=user-desktop +X-KDE-ParentApp= +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment + +X-KDE-PluginInfo-Author=Sebastian Kügler +X-KDE-PluginInfo-Email=sebas@kde.org +X-KDE-PluginInfo-Name=org.kde.desktopcontainment +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=http://plasma.kde.org/ +X-KDE-PluginInfo-Category=Containments +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-PluginInfo-Version=0.1 + +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= +X-Plasma-RequiredExtensions=LaunchApp,http,localio,networkio +X-Plasma-ContainmentCategories=desktop diff --git a/containments/desktop/tests/CMakeLists.txt b/containments/desktop/tests/CMakeLists.txt new file mode 100644 index 0000000000..a5eb468d11 --- /dev/null +++ b/containments/desktop/tests/CMakeLists.txt @@ -0,0 +1,6 @@ + + +# Background test plasmoid +install(DIRECTORY org.kde.tests.background/ DESTINATION ${DATA_INSTALL_DIR}/plasma/plasmoids/org.kde.tests.background) +install(FILES org.kde.tests.background/metadata.desktop DESTINATION ${SERVICES_INSTALL_DIR} RENAME plasma-applet-org.kde.tests.background.desktop) + diff --git a/containments/desktop/tests/framehandle-resizing-draft.qml b/containments/desktop/tests/framehandle-resizing-draft.qml new file mode 100644 index 0000000000..c521503c5f --- /dev/null +++ b/containments/desktop/tests/framehandle-resizing-draft.qml @@ -0,0 +1,100 @@ +/* + * Copyright 2013 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 1.1 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.kquickcontrolsaddons 0.1 as KQuickControlsAddons + +Rectangle { + + property bool showAppletHandle: mouseListener.containsMouse + + width: 600 + height: 600 + color: "black" + anchors.fill: parent + + property int w: 30 + + Rectangle { + id: applet + color: "blue" + x: 100 + y: 60 + width: 400 + height: 400 + + KQuickControlsAddons.MouseEventListener { + /* The MouseEventListener + * + * this is the parent of the applet and handle, + * it's wider than it's alloted position, however, + * and unclipped. It carries the whole area that + * is hoverable. + */ + id: mouseListener + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } + width: parent.width+w; + hoverEnabled: true + + onContainsMouseChanged: print("Mouse is " + containsMouse); + + PlasmaCore.FrameSvgItem { + id: backgroundFrame + /* The applet+handle frame + * + * This is the growing frame containing the applet + * and handle. + * + */ + imagePath: "widgets/background" + z: parent.z+1 + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; } + width: showAppletHandle ? parent.width: parent.width-w; + Behavior on width { PropertyAnimation {}} + + // DragArea goes here + + // Handle itself goes here + } + Rectangle { + /* The Applet + * + * Sits on top of the frame, but doesn't resize with it, + * only with the whole allotted area. + * + * As it's a child of the MouseEventListener, the applet + * still receives all input. + */ + id: yellow + color: "green" + z: parent.z+1 + x: 0 + backgroundFrame.margins.left + y: 0 + backgroundFrame.margins.top + width: applet.width - (backgroundFrame.margins.left + backgroundFrame.margins.right) + height: applet.width - (backgroundFrame.margins.top + backgroundFrame.margins.bottom) + MouseArea { + anchors.fill: parent + onClicked: print("Applet clicked."); + } + } + } + } + onShowAppletHandleChanged: print("Active? " + showAppletHandle); +} diff --git a/containments/desktop/tests/org.kde.tests.background/contents/code/testbackground.qml b/containments/desktop/tests/org.kde.tests.background/contents/code/testbackground.qml new file mode 100644 index 0000000000..e4813d5636 --- /dev/null +++ b/containments/desktop/tests/org.kde.tests.background/contents/code/testbackground.qml @@ -0,0 +1,141 @@ +/* + * Copyright 2013 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 1.1 +import org.kde.plasma.components 0.1 as PlasmaComponents +import org.kde.plasma.extras 0.1 as PlasmaExtras +import org.kde.plasma.core 0.1 as PlasmaCore + +Item { + id: root + width: 200 + height: 300 + clip: true + + Rectangle { + color: "white" + opacity: 0.0 + anchors.fill: parent + } + PlasmaExtras.Title { + id: title + anchors { top: parent.top; left: parent.left; right: parent.right; } + text: i18n("Test Applet") + } + PlasmaExtras.Paragraph { + id: para + anchors { top: title.bottom; left: parent.left; right: parent.right; } + text: { + var o = "x: " + root.x; + o += "
    y: " + root.y; + o += "
    w: " + root.width; + o += "
    h: " + root.height; + //o += "

    s: " + plasmoid.availableScreenRect(screen[0]); + return o; + } + } + + PlasmaComponents.ButtonColumn { + visible: root.width + root.height > 500 + anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter; } + spacing: 12 + //anchors.fill: parent + PlasmaComponents.Button { + text: hint + iconSource: "view-refresh" + property string hint: "NoBackground" + checkable: true + checked: plasmoid.backgroundHints == hint + onClicked: saveHints(hint); + } + PlasmaComponents.Button { + text: hint + iconSource: "view-refresh" + property string hint: "TranslucentBackground" + checkable: true + checked: plasmoid.backgroundHints == hint + onClicked: saveHints(hint); + } + PlasmaComponents.Button { + text: hint + iconSource: "view-refresh" + property string hint: "NormalBackground" + checkable: true + checked: plasmoid.backgroundHints == hint + onClicked: saveHints(hint); + } + PlasmaComponents.Button { + text: hint + iconSource: "view-refresh" + property string hint: "DefaultBackground" + checkable: true + checked: plasmoid.backgroundHints == hint + onClicked: saveHints(hint); + } + } + + function saveHints(hint) { + print("writing to config: " + hint); + plasmoid.writeConfig("bgHints", hint); + } + + function configChanged() + { +// //serviceUrl = plasmoid.readConfig("serviceUrl"); +// var u = plasmoid.readConfig("userName"); +// var s = plasmoid.readConfig("serviceUrl"); +// print(" @@@@@@@@@@@@@@@@@@ configChanged()" + u + " s: " + s); +// +// if (u != "") { +// userName = u; +// } +// if (s != "") { +// serviceUrl = s; +// imageSourc + var bgh = "DefaultBackground"; + var _bgh = plasmoid.readConfig("bgHints"); + print("-- " + _bgh); + if (_bgh != "") { + bgh = _bgh; + } else { + //bgh = ; + print("Default"); + } +// if (typeof(bgh) == "undefined" || bgh == undefined || bgh == "") { +// } + print(" ----------? Read Background Hints from Config: " + bgh + " " + (typeof(_bgh))); + + //plasmoid.backgroundHints = "DefaultBackground"; + plasmoid.backgroundHints = "NormalBackground"; + } + + Timer { + repeat: false + //running: true + interval: 100 + //onTriggered: configChanged() + } + + Component.onCompleted: { + //plasmoid.backgroundHints = "NoBackground" + //plasmoid.addEventListener('ConfigChanged', configChanged); + //plasmoid.configurationRequired = true + configChanged() + } +} \ No newline at end of file diff --git a/containments/desktop/tests/org.kde.tests.background/contents/plasmatepreviewerlog.txt b/containments/desktop/tests/org.kde.tests.background/contents/plasmatepreviewerlog.txt new file mode 100644 index 0000000000..fd868ad6fe --- /dev/null +++ b/containments/desktop/tests/org.kde.tests.background/contents/plasmatepreviewerlog.txt @@ -0,0 +1,498 @@ +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00166 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00178 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00194 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c001ac +Warning: QWidget::setLayout: Attempting to set QLayout "" on Publisher "", when the QLayout already has a parent +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c001c3 +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c001ce +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c001e4 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c001f5 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00266 +Warning: QFont::setPixelSize: Pixel size <= 0 (0) +Warning: static bool QDeclarativeMetaType::isModule(const QByteArray&, int, int) Qt 4.7 import detected; please note that Qt 4.7 is directly reusable as QtQuick 1.x with no code changes. Continuing, but startup time will be slower. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0028e +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0029b +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c002a6 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0030c +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00328 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00317 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00333 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0034f +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0033e +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0035a +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Debug: Enchant dict for "en_US" 0x2a7dfb0 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c00365 +Debug: Enchant dict for "en_US" 0x3568e30 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5c0037c +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00175 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/.kde4/share/apps/plasmate/testbackground/contents/code/testbackground.qml:52: ReferenceError: Can't find variable: screen +Warning: file:///home/sebas/.kde4/share/apps/plasmate/testbackground/contents/code/testbackground.qml:52: ReferenceError: Can't find variable: screen +Warning: file:///home/sebas/.kde4/share/apps/plasmate/testbackground/contents/code/testbackground.qml:52: ReferenceError: Can't find variable: screen +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QWidget::setLayout: Attempting to set QLayout "" on Publisher "", when the QLayout already has a parent +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001af +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001ba +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001dc +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001e7 +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001f2 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e001fd +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00208 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00213 +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e0021e +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00229 +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00234 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e0023f +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e0024a +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00255 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e00260 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: QPainter::begin: Paint device returned engine == 0, type: 2 +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Warning: file:///home/sebas/kdesvn/install/lib/kde4/imports/org/kde/plasma/components/Button.qml:194:5: QML Row: Possible anchor loop detected on fill. +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" +Warning: X Error: BadWindow (invalid Window parameter) 3 + Major opcode: 20 (X_GetProperty) + Resource id: 0x5e0026b +Debug: tempPackagePath "/home/sebas/.kde4/share/apps/plasmate/testbackground.plasmoid" +Debug: m_projectPath "/home/sebas/.kde4/share/apps/plasmate/testbackground/" diff --git a/containments/desktop/tests/org.kde.tests.background/metadata.desktop b/containments/desktop/tests/org.kde.tests.background/metadata.desktop new file mode 100644 index 0000000000..fb58dc7f00 --- /dev/null +++ b/containments/desktop/tests/org.kde.tests.background/metadata.desktop @@ -0,0 +1,87 @@ +[Desktop Entry] +Comment=Testing background settings in Plasma Quick Containments +Comment[bs]=Testiranje pozadinskih postavki u Plasma Quick Containmentsu +Comment[ca]=Prova de la configuració del fons dels «Plasma Quick Containments» +Comment[ca@valencia]=Prova de la configuració del fons dels «Plasma Quick Containments» +Comment[da]=Test af baggrundsindstillinger i Plasma Quick-beholdere +Comment[de]=Test von Hintergrundeinstellungen in „Plasma Quick Containments“ +Comment[el]=Έλεγχος των ρυθμίσεων φόντου στους υποδοχείς του Plasma Quick +Comment[es]=Prueba de las preferencias del fondo de los contenedores de Plasma Quick +Comment[eu]=Atzeko planoko ezarpenen proba Plasma Quick edukiontzietan +Comment[fi]=Plasma Quick -sovelmasäiliöiden tausta-asetusten testi +Comment[fr]=Test des paramètres d'arrière plan dans un conteneur rapide Plasma +Comment[gl]=Probas da configuración do fondo nos Plasma Quick Containments +Comment[hu]=Háttérbeállítás tesztelése a Plasma gyors elhatárolásban +Comment[ia]=Essayante preferentias de fundo in Plasma Quick Containments +Comment[kk]=Plasma шұғыл контейнерде аясының параметрлерін тексеру +Comment[ko]=Plasma Quick 컨테이너의 배경 설정 시험 +Comment[nb]=Tester bakgrunnsinnstillinger i Plasma Quick Containments +Comment[nds]=Achtergrund-Instellen binnen gau Plasma-Gelaatsen utproberen +Comment[nl]=Achtergrondinstellingen testen in Plasma Quick Containments +Comment[pl]=Testowanie ustawień tła w Plasma Quick Containments +Comment[pt]=Configuração de testes do fundo dos contentores do Plasma Quick +Comment[pt_BR]=Configuração de testes do plano de fundo dos contêineres do Plasma Quick +Comment[sk]=Testujem nastavenia pozadia v Plasma Quick Containments +Comment[sl]=Preizkušanje nastavitev ozadij v vsebnikih Plasma Quick +Comment[sr]=Испробавање поставки позадине у плазма‑квик садржаоцима +Comment[sr@ijekavian]=Испробавање поставки позадине у плазма‑квик садржаоцима +Comment[sr@ijekavianlatin]=Isprobavanje postavki pozadine u Plasma Quick sadržaocima +Comment[sr@latin]=Isprobavanje postavki pozadine u Plasma Quick sadržaocima +Comment[sv]=Prova bakgrundsinställningar i Plasma Quick-omgivningar +Comment[tr]=Plasma Hızlı Barındırmasında arkaplan ayarlarını sınama +Comment[uk]=Перевірка параметрів тла у контейнерах мобільної Плазми +Comment[x-test]=xxTesting background settings in Plasma Quick Containmentsxx +Comment[zh_CN]=在 Plasma Quick 容器中测试背景设置 +Comment[zh_TW]=在 Plasma Quick Containments 中測試背景設定 +Encoding=UTF-8 +Icon=text-frame-link +Keywords= +Name=Background Test +Name[bs]=Pozadinski test +Name[ca]=Prova del fons +Name[ca@valencia]=Prova del fons +Name[da]=Baggrundstest +Name[de]=Hintergrundtest +Name[el]=Έλεγχος φόντου +Name[es]=Prueba del fondo +Name[eu]=Atzeko planoko proba +Name[fi]=Taustatesti +Name[fr]=Test de l'arrière plan +Name[gl]=Proba do fondo +Name[hu]=Háttér teszt +Name[ia]=Essayo de fundo +Name[kk]=Аясын тексеру +Name[ko]=배경 시험 +Name[nb]=Bakgrunnstest +Name[nds]=Achtergrund-Test +Name[nl]=Achtergrondtest +Name[pa]=ਬੈਕਗਰਾਊਂਡ ਟੈਸਟ +Name[pl]=Test tła +Name[pt]=Teste de Fundo +Name[pt_BR]=Teste do plano de fundo +Name[sk]=Test pozadia +Name[sl]=Preizkus ozadja +Name[sr]=Проба позадине +Name[sr@ijekavian]=Проба позадине +Name[sr@ijekavianlatin]=Proba pozadine +Name[sr@latin]=Proba pozadine +Name[sv]=Bakgrundsprov +Name[tr]=Arkaplan Sınaması +Name[uk]=Перевірка тла +Name[x-test]=xxBackground Testxx +Name[zh_CN]=背景测试 +Name[zh_TW]=背景測試 +Type=Service +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Sebastian Kügler +X-KDE-PluginInfo-Category=Utilities +X-KDE-PluginInfo-Email=sebas@kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.tests.background +X-KDE-PluginInfo-Version=1 +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=code/testbackground.qml +X-Plasma-RemoteLocation= diff --git a/containments/panel/Messages.sh b/containments/panel/Messages.sh new file mode 100644 index 0000000000..b1326bce1a --- /dev/null +++ b/containments/panel/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_panel.pot +rm -f rc.cpp diff --git a/containments/panel/contents/code/LayoutManager.js b/containments/panel/contents/code/LayoutManager.js new file mode 100644 index 0000000000..914ac265c0 --- /dev/null +++ b/containments/panel/contents/code/LayoutManager.js @@ -0,0 +1,158 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +.pragma library + + +var layout; +var root; +var plasmoid; + + +function restore() { + var configString = String(plasmoid.configuration.AppletOrder) + + //array, a cell for encoded item order + var itemsArray = configString.split(";"); + + //map applet id->order in panel + var idsOrder = new Object(); + //map order in panel -> applet pointer + var appletsOrder = new Object(); + + for (var i = 0; i < itemsArray.length; i++) { + //property name: applet id + //property value: order + idsOrder[itemsArray[i]] = i; + } + + for (var i = 0; i < plasmoid.applets.length; ++i) { + if (idsOrder[plasmoid.applets[i].id] !== undefined) { + appletsOrder[idsOrder[plasmoid.applets[i].id]] = plasmoid.applets[i]; + //ones that weren't saved in AppletOrder go to the end + } else { + appletsOrder["unordered"+i] = plasmoid.applets[i]; + } + } + + //finally, restore the applets in the correct order + for (var i in appletsOrder) { + root.addApplet(appletsOrder[i], -1, -1) + } + //rewrite, so if in the orders there were now invalid ids or if some were missing creates a correct list instead + save(); +} + +function save() { + var ids = new Array(); + for (var i = 0; i < layout.children.length; ++i) { + var child = layout.children[i]; + + if (child.applet) { + ids.push(child.applet.id); + } + } + plasmoid.configuration.AppletOrder = ids.join(';'); +} + +function insertBefore(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + removed.push(child); + child.parent = root; + + if (child === item1) { + break; + } + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +function insertAfter(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + if (child === item1) { + break; + } + + removed.push(child); + child.parent = root; + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +function insertAtIndex(item, position) { + var removedItems = new Array(); + + for (var i = position; i < layout.children.length; ++i) { + var child = layout.children[position]; + child.parent = root; + removedItems.push(child); + } + + item.parent = layout; + for (var i in removedItems) { + removedItems[i].parent = layout; + } +} + +function insertAtCoordinates(item, x, y) { + var child = layout.childAt(x, y); + + if (!child || child === item) { + return -1; + } + item.parent = root; + + //PlasmaCore.Types.Vertical = 3 + if ((plasmoid.formFactor === 3 && y < child.y + child.height/2) || + (plasmoid.formFactor !== 3 && x < child.x + child.width/2)) { + return insertBefore(child, item); + } else { + return insertAfter(child, item); + } +} diff --git a/containments/panel/contents/config/main.xml b/containments/panel/contents/config/main.xml new file mode 100644 index 0000000000..61a525b9b8 --- /dev/null +++ b/containments/panel/contents/config/main.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/containments/panel/contents/ui/ConfigOverlay.qml b/containments/panel/contents/ui/ConfigOverlay.qml new file mode 100644 index 0000000000..d2aa42b8bb --- /dev/null +++ b/containments/panel/contents/ui/ConfigOverlay.qml @@ -0,0 +1,292 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +MouseArea { + id: configurationArea + z: 1000 + anchors { + fill: parent + rightMargin: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? toolBox.width : 0 + bottomMargin: (plasmoid.formFactor === PlasmaCore.Types.Vertical) ? toolBox.height : 0 + } + hoverEnabled: true + + property bool isResizingLeft: false + property bool isResizingRight: false + property Item currentApplet + + property int lastX + property int lastY + + onPositionChanged: { + if (currentApplet && currentApplet.applet && + currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + if ((mouse.y - handle.y) < units.largeSpacing || + (mouse.y - handle.y) > (handle.height - units.largeSpacing)) { + configurationArea.cursorShape = Qt.SizeVerCursor; + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + } + } else { + if ((mouse.x - handle.x) < units.largeSpacing || + (mouse.x - handle.x) > (handle.width - units.largeSpacing)) { + configurationArea.cursorShape = Qt.SizeHorCursor; + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + } + } + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + } + + if (pressed) { + if (currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + + if (isResizingLeft) { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + handle.y += (mouse.y - lastY); + handle.height = currentApplet.height + (currentApplet.y - handle.y); + } else { + handle.x += (mouse.x - lastX); + handle.width = currentApplet.width + (currentApplet.x - handle.x); + } + + lastX = mouse.x; + lastY = mouse.y; + return; + + } else if (isResizingRight) { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + handle.height = mouse.y - handle.y; + } else { + handle.width = mouse.x - handle.x; + } + + lastX = mouse.x; + lastY = mouse.y; + return; + } + } + + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + currentApplet.y += (mouse.y - lastY); + handle.y = currentApplet.y; + } else { + currentApplet.x += (mouse.x - lastX); + handle.x = currentApplet.x; + } + + lastX = mouse.x; + lastY = mouse.y; + + var item = currentLayout.childAt(mouse.x, mouse.y); + + if (item && item !== placeHolder) { + placeHolder.width = item.width; + placeHolder.height = item.height; + placeHolder.parent = configurationArea; + var posInItem = mapToItem(item, mouse.x, mouse.y); + + if ((plasmoid.formFactor === PlasmaCore.Types.Vertical && posInItem.y < item.height/2) || + (plasmoid.formFactor !== PlasmaCore.Types.Vertical && posInItem.x < item.width/2)) { + LayoutManager.insertBefore(item, placeHolder); + } else { + LayoutManager.insertAfter(item, placeHolder); + } + } + + } else { + var item = currentLayout.childAt(mouse.x, mouse.y); + if (root.dragOverlay && item && item !== lastSpacer) { + root.dragOverlay.currentApplet = item; + } else { + root.dragOverlay.currentApplet = null; + } + } + } + onCurrentAppletChanged: { + if (!root.dragOverlay.currentApplet) { + return; + } + + handle.x = currentApplet.x; + handle.y = currentApplet.y; + handle.width = currentApplet.width; + handle.height = currentApplet.height; + } + onPressed: { + if (!root.dragOverlay.currentApplet) { + return; + } + + if (currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + if ((mouse.y - handle.y) < units.largeSpacing) { + configurationArea.isResizingLeft = true; + configurationArea.isResizingRight = false; + } else if ((mouse.y - handle.y) > (handle.height - units.largeSpacing)) { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = true; + } else { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + } + + } else { + if ((mouse.x - handle.x) < units.largeSpacing) { + configurationArea.isResizingLeft = true; + configurationArea.isResizingRight = false; + } else if ((mouse.x - handle.x) > (handle.width - units.largeSpacing)) { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = true; + } else { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + } + } + } + + lastX = mouse.x; + lastY = mouse.y; + placeHolder.width = currentApplet.width; + placeHolder.height = currentApplet.height; + LayoutManager.insertBefore(currentApplet, placeHolder); + currentApplet.parent = root; + currentApplet.z = 900; + } + onReleased: { + if (!root.dragOverlay.currentApplet) { + return; + } + + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + currentApplet.applet.configuration.length = handle.height; + } else { + currentApplet.applet.configuration.length = handle.width; + } + + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + + LayoutManager.insertBefore(placeHolder, currentApplet); + placeHolder.parent = configurationArea; + currentApplet.z = 1; + + handle.x = currentApplet.x; + handle.y = currentApplet.y; + handle.width = currentApplet.width; + handle.height = currentApplet.height; + LayoutManager.save(); + } + Item { + id: placeHolder + visible: configurationArea.containsMouse + Layout.fillWidth: currentApplet ? currentApplet.Layout.fillWidth : false + Layout.fillHeight: currentApplet ? currentApplet.Layout.fillHeight : false + } + + Connections { + target: currentApplet + onXChanged: handle.x = currentApplet.x + onYChanged: handle.y = currentApplet.y + onWidthChanged: handle.width = currentApplet.width + onHeightChanged: handle.height = currentApplet.height + } + + Rectangle { + id: handle + visible: configurationArea.containsMouse + color: theme.backgroundColor + radius: 3 + opacity: currentApplet ? 0.5 : 0 + PlasmaCore.IconItem { + source: "transform-move" + width: Math.min(parent.width, parent.height) + height: width + anchors.centerIn: parent + } + Rectangle { + anchors { + left: parent.left + top: parent.top + bottom: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? parent.bottom : undefined + right: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? undefined : parent.right + } + visible: currentApplet.applet.pluginName == "org.kde.plasma.panelspacer" + width: units.largeSpacing + height: units.largeSpacing + color: theme.textColor + } + Rectangle { + anchors { + right: parent.right + top: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? parent.top : undefined + bottom: parent.bottom + left: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? undefined : parent.left + } + visible: currentApplet.applet.pluginName == "org.kde.plasma.panelspacer" + width: units.largeSpacing + height: units.largeSpacing + color: theme.textColor + } + Behavior on x { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on y { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on width { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on height { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } +} diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml new file mode 100644 index 0000000000..3dd531fe5b --- /dev/null +++ b/containments/panel/contents/ui/main.qml @@ -0,0 +1,328 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import org.kde.plasma.plasmoid 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.draganddrop 2.0 as DragDrop + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +DragDrop.DropArea { + id: root + width: 640 + height: 48 + +//BEGIN properties + Layout.minimumWidth: currentLayout.Layout.minimumWidth + Layout.maximumWidth: currentLayout.Layout.maximumWidth + Layout.preferredWidth: currentLayout.Layout.preferredWidth + + Layout.minimumHeight: currentLayout.Layout.minimumHeight + Layout.maximumHeight: currentLayout.Layout.maximumHeight + Layout.preferredHeight: currentLayout.Layout.preferredHeight + + property Item toolBox + + property Item dragOverlay + + property bool isHorizontal: plasmoid.formFactor != PlasmaCore.Types.Vertical +//END properties + +//BEGIN functions +function addApplet(applet, x, y) { + var container = appletContainerComponent.createObject(root) + print("Applet added in test panel: " + applet + applet.title + " at: " + x + ", " + y); + + applet.parent = container; + container.applet = applet; + applet.anchors.fill = container; + applet.visible = true; + container.visible = true; + + //is there a DND placeholder? replace it! + if (dndSpacer.parent === currentLayout) { + LayoutManager.insertBefore(dndSpacer, container); + dndSpacer.parent = root; + return; + + //if the provided position is valid, try it + } else if (x >= 0 && y >= 0) { + var index = LayoutManager.insertAtCoordinates(container, x, y); + print("Applet " + applet.id + " " + applet.title + " was added in position " + index) + + //give up: enqueue the applet after all the others + } else { + //if lastspacer is here, enqueue after it + if (lastSpacer.parent === currentLayout) { + LayoutManager.insertBefore(lastSpacer, container); + //else put it in last position + } else { + container.parent = currentLayout; + } + } + + if (applet.Layout.fillWidth) { + lastSpacer.parent = root; + } +} + + +function checkLastSpacer() { + lastSpacer.parent = root + + var expands = false; + + if (isHorizontal) { + for (var container in currentLayout.children) { + var item = currentLayout.children[container]; + if (item.Layout && item.Layout.fillWidth) { + expands = true; + } + } + } else { + for (var container in currentLayout.children) { + var item = currentLayout.children[container]; + if (item.Layout && item.Layout.fillHeight) { + expands = true; + } + } + } + if (!expands) { + lastSpacer.parent = currentLayout + } + +} + +//END functions + +//BEGIN connections + Component.onCompleted: { + currentLayout.isLayoutHorizontal = isHorizontal + LayoutManager.plasmoid = plasmoid; + LayoutManager.root = root; + LayoutManager.layout = currentLayout; + LayoutManager.restore(); + containmentSizeSyncTimer.restart(); + } + + onDragEnter: { + LayoutManager.insertAtCoordinates(dndSpacer, event.x, event.y) + } + + onDragMove: { + LayoutManager.insertAtCoordinates(dndSpacer, event.x, event.y) + } + + onDragLeave: { + dndSpacer.parent = root; + } + + onDrop: { + plasmoid.processMimeData(event.mimeData, event.x, event.y); + } + + + Containment.onAppletAdded: { + addApplet(applet, x, y); + LayoutManager.save(); + } + + Containment.onAppletRemoved: { + var flexibleFound = false; + for (var i = 0; i < currentLayout.children.length; ++i) { + if (currentLayout.children[i].applet.Layout.fillWidth) { + flexibleFound = true; + break + } + } + if (!flexibleFound) { + lastSpacer.parent = currentLayout; + } + + LayoutManager.save(); + } + + Plasmoid.onUserConfiguringChanged: { + if (plasmoid.immutable) { + if (dragOverlay) { + dragOverlay.destroy(); + } + return; + } + + if (plasmoid.userConfiguring) { + if (!dragOverlay) { + var component = Qt.createComponent("ConfigOverlay.qml"); + dragOverlay = component.createObject(root); + component.destroy(); + } else { + dragOverlay.visible = true; + } + } else { + dragOverlay.visible = false; + dragOverlay.destroy(); + } + } + + Plasmoid.onFormFactorChanged: containmentSizeSyncTimer.restart(); + Plasmoid.onImmutableChanged: containmentSizeSyncTimer.restart(); + onToolBoxChanged: containmentSizeSyncTimer.restart(); +//END connections + +//BEGIN components + Component { + id: appletContainerComponent + Item { + id: container + visible: false + + Layout.fillWidth: applet && applet.Layout.fillWidth + Layout.onFillWidthChanged: { + if (plasmoid.formFactor != PlasmaCore.Types.Vertical) { + checkLastSpacer(); + } + } + Layout.fillHeight: applet && applet.Layout.fillHeight + Layout.onFillHeightChanged: { + if (plasmoid.formFactor == PlasmaCore.Types.Vertical) { + checkLastSpacer(); + } + } + + Layout.minimumWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.minimumWidth > 0 ? applet.Layout.minimumWidth : root.height) : root.width) + Layout.minimumHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.minimumHeight > 0 ? applet.Layout.minimumHeight : root.width) : root.height) + + Layout.preferredWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.preferredWidth > 0 ? applet.Layout.preferredWidth : root.height) : root.width) + Layout.preferredHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.preferredHeight > 0 ? applet.Layout.preferredHeight : root.width) : root.height) + + Layout.maximumWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.maximumWidth > 0 ? applet.Layout.maximumWidth : (Layout.fillWidth ? root.width : root.height)) : root.height) + Layout.maximumHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.maximumHeight > 0 ? applet.Layout.maximumHeight : (Layout.fillHeight ? root.height : root.width)) : root.width) + + property int oldX: x + property int oldY: y + + property Item applet + onAppletChanged: { + if (!applet) { + destroy(); + } + } + + PlasmaComponents.BusyIndicator { + z: 1000 + visible: applet && applet.busy + running: visible + anchors.centerIn: parent + } + onXChanged: { + translation.x = oldX - x + translation.y = oldY - y + translAnim.running = true + oldX = x + oldY = y + } + onYChanged: { + translation.x = oldX - x + translation.y = oldY - y + translAnim.running = true + oldX = x + oldY = y + } + transform: Translate { + id: translation + } + NumberAnimation { + id: translAnim + duration: units.longDuration + easing.type: Easing.InOutQuad + target: translation + properties: "x,y" + to: 0 + } + } + } +//END components + +//BEGIN UI elements + Item { + id: lastSpacer + parent: currentLayout + + Layout.fillWidth: true + Layout.fillHeight: true + } + + Item { + id: dndSpacer + width: (plasmoid.formFactor == PlasmaCore.Types.Vertical) ? currentLayout.width : theme.mSize(theme.defaultFont).width * 10 + height: (plasmoid.formFactor == PlasmaCore.Types.Vertical) ? theme.mSize(theme.defaultFont).width * 10 : currentLayout.height + } + + GridLayout { + id: currentLayout + property bool isLayoutHorizontal + + Layout.preferredWidth: { + var width = 0; + for (var i = 0; i < currentLayout.children.length; ++i) { + if (currentLayout.children[i].Layout) { + width += Math.max(currentLayout.children[i].Layout.minimumWidth, currentLayout.children[i].Layout.preferredWidth); + } + } + return width; + } + Layout.preferredHeight: { + var height = 0; + for (var i = 0; i < currentLayout.children.length; ++i) { + if (currentLayout.children[i].Layout) { + height += Math.max(currentLayout.children[i].Layout.minimumHeight, currentLayout.children[i].Layout.preferredHeight); + } + } + return height; + } + rows: 1 + columns: 1 + //when horizontal layout top-to-bottom, this way it will obey our limit of one row and actually lay out left to right + flow: isHorizontal ? GridLayout.TopToBottom : GridLayout.LeftToRight + } + + onWidthChanged: { + containmentSizeSyncTimer.restart() + } + onHeightChanged: { + containmentSizeSyncTimer.restart() + } + + Timer { + id: containmentSizeSyncTimer + interval: 150 + onTriggered: { + currentLayout.x = 0 + currentLayout.y = 0 + currentLayout.width = root.width - (isHorizontal && toolBox && toolBox.visible ? toolBox.width : 0) + currentLayout.height = root.height - (!isHorizontal && toolBox && toolBox.visible ? toolBox.height : 0) + currentLayout.isLayoutHorizontal = isHorizontal + } + } +//END UI elements +} diff --git a/containments/panel/metadata.desktop b/containments/panel/metadata.desktop new file mode 100644 index 0000000000..bb81dceea5 --- /dev/null +++ b/containments/panel/metadata.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Encoding=UTF-8 +Keywords= +Name=Panel +Name[be]=Панель +Name[bn]=প্যানেল +Name[br]=Panell +Name[bs]=Panel +Name[ca]=Plafó +Name[ca@valencia]=Plafó +Name[cs]=Panel +Name[cy]=Panel +Name[da]=Panel +Name[de]=Kontrollleiste +Name[el]=Πίνακας +Name[en_GB]=Panel +Name[es]=Panel +Name[eu]=Panela +Name[fa]=تابلو +Name[fi]=Paneeli +Name[fr]=Tableau de bord +Name[ga]=Painéal +Name[gl]=Panel +Name[hu]=Panel +Name[ia]=Pannello +Name[ka]=პანელი +Name[kk]=Панель +Name[kn]=ಪುಟೀಪು (ಪಾನಲ್) +Name[ko]=패널 +Name[nb]=Panel +Name[nds]=Paneel +Name[nl]=Paneel +Name[pa]=ਪੈਨਲ +Name[pl]=Panel +Name[pt]=Painel +Name[pt_BR]=Painel +Name[ro]=Panou +Name[sk]=Panel +Name[sl]=Pult +Name[sr]=Панел +Name[sr@ijekavian]=Панел +Name[sr@ijekavianlatin]=Panel +Name[sr@latin]=Panel +Name[sv]=Panel +Name[tr]=Panel +Name[uk]=Панель +Name[uz]=Panel +Name[uz@cyrillic]=Панел +Name[vi]=Bảng điều khiển +Name[xh]=Iqela lenjongo ethile +Name[x-test]=xxPanelxx +Name[zh_CN]=面板 +Name[zh_TW]=面板 +Type=Service + +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment +X-Plasma-API=declarativeappletscript +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-PluginInfo-Name=org.kde.panel +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-Plasma-MainScript=ui/main.qml +X-Plasma-ContainmentType=Panel diff --git a/containments/panel/plasma-containment-panel.desktop b/containments/panel/plasma-containment-panel.desktop new file mode 100644 index 0000000000..766d6be6e7 --- /dev/null +++ b/containments/panel/plasma-containment-panel.desktop @@ -0,0 +1,113 @@ +[Desktop Entry] +Name=Empty Panel +Name[bs]=prazan panel +Name[ca]=Plafó buit +Name[ca@valencia]=Plafó buit +Name[cs]=Prázdný panel +Name[da]=Tomt panel +Name[de]=Leere Kontrollleiste +Name[el]=Κενός πίνακας +Name[en_GB]=Empty Panel +Name[es]=Panel vacío +Name[eu]=Panel hutsa +Name[fi]=Tyhjä paneeli +Name[fr]=Tableau de bord vide +Name[ga]=Painéal Folamh +Name[gl]=Panel baleiro +Name[hr]=Prazan panel +Name[hu]=Üres panel +Name[ia]=Pannello vacue +Name[kn]=ಖಾಲಿ ಪುಟೀಪು (ಪ್ಯಾನಲ್) +Name[ko]=빈 패널 +Name[nb]=Tomt panel +Name[nds]=Leddig Paneel +Name[nl]=Leeg paneel +Name[pa]=ਖਾਲੀ ਪੈਨਲ +Name[pl]=Pusty panel +Name[pt]=Painel Vazio +Name[pt_BR]=Painel vazio +Name[ro]=Panou gol +Name[sk]=Prázdny panel +Name[sl]=Prazen pult +Name[sr]=празан панел +Name[sr@ijekavian]=празан панел +Name[sr@ijekavianlatin]=prazan panel +Name[sr@latin]=prazan panel +Name[sv]=Tom panel +Name[tr]=Boş Panel +Name[uk]=Порожня панель +Name[vi]=Thanh điều khiển trống +Name[x-test]=xxEmpty Panelxx +Name[zh_CN]=空面板 +Name[zh_TW]=空白面板 +Comment=A simple linear panel +Comment[ast]=Un panel llinial simple +Comment[bs]=Jednostavan linearni panel +Comment[ca]=Un plafó lineal senzill +Comment[ca@valencia]=Un plafó lineal senzill +Comment[cs]=Jednoduchý přímý panel +Comment[da]=Et simpelt lineært panel +Comment[de]=Eine einfache, gerade Kontrollleiste +Comment[el]=Ένας απλός γραμμικός πίνακας +Comment[en_GB]=A simple linear panel +Comment[es]=Un sencillo panel lineal +Comment[et]=Lihtne sirge paneel +Comment[eu]=Panel lineal xume bat +Comment[fi]=Yksinkertainen lineaarinen paneeli +Comment[fr]=Un tableau de bord linéaire simple +Comment[ga]=Painéal simplí líneach +Comment[gl]=Un panel simple +Comment[he]=לוח ישר פשוט +Comment[hr]=Jednostavan pravocrtni panel +Comment[hu]=Egy egyszerű, lineáris panel +Comment[ia]=Un simple pannello linear +Comment[is]=Einfalt línulegt spjald +Comment[it]=Un semplice pannello lineare +Comment[ja]=簡単なリニアパネル +Comment[kk]=Жәй панель +Comment[km]=បន្ទះ​លីនេអ៊ែរ​ធម្មតា +Comment[kn]=ಒಂದು ಸರಳ ಹಾಗು ರೇಖೀಯ ಫಲಕ +Comment[ko]=간단한 선형 패널 +Comment[lt]=Paprastas tiesinis pultas +Comment[lv]=Vienkāršs, lineārs panelis +Comment[mr]=सोपे रेषीय पटल +Comment[nb]=En enkelt lineært panel +Comment[nds]=En eenfach liekweg Paneel +Comment[nl]=Een eenvoudig lineair paneel +Comment[pa]=ਸਧਾਰਨ ਲੀਨੀਅਰ ਪੈਨਲ +Comment[pl]=Prosty, liniowy panel +Comment[pt]=Um painel linear simples +Comment[pt_BR]=Um painel linear simples +Comment[ro]=Un panou liniar simplu +Comment[ru]=Простая линейная панель +Comment[sk]=Jednoduchý lineárny panel +Comment[sl]=Preprost linearen pult +Comment[sr]=Једноставан линеарни панел +Comment[sr@ijekavian]=Једноставан линеарни панел +Comment[sr@ijekavianlatin]=Jednostavan linearni panel +Comment[sr@latin]=Jednostavan linearni panel +Comment[sv]=En enkel linjär panel +Comment[th]=พาเนลแนวยาวแบบพื้นฐาน +Comment[tr]=Basit bir doğrusal panel +Comment[ug]=ئاددىي بىر سىزىقلىق تاختا +Comment[uk]=Проста лінійна панель +Comment[wa]=On simpe et lineyåre sicriftôr +Comment[x-test]=xxA simple linear panelxx +Comment[zh_CN]=简单的线性面板 +Comment[zh_TW]=簡單的線性面板 +Icon= +Type=Service +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment +X-Plasma-ContainmentCategories=panel +NoDisplay=true + +X-KDE-Library=plasma_containment_panel +X-KDE-PluginInfo-Author=The Plasma Team +X-KDE-PluginInfo-Email=plasma-devel@kde.org +X-KDE-PluginInfo-Name=panel +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=http://plasma.kde.org/ +X-KDE-PluginInfo-Category=Containments +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/design/CHANGELOG-4.2 b/design/CHANGELOG-4.2 new file mode 100644 index 0000000000..0dead00351 --- /dev/null +++ b/design/CHANGELOG-4.2 @@ -0,0 +1,263 @@ +libplasma 3.0 / KDE 4.2 +======================= + +libplasma +--------- +* Build + * libplasma has moved to kdelibs from kdebase-workspace and as such there is no longer a need to use FindPlasma.cmake. FindKDE4 now provides all the necessary plasma CMake bits needed, and using libplasma does not create a kdebase-workspace dependency for your application anymore. + +* Features + * Symbol Versioning: limit loading of plugins to those that match the libplasma version + * NEW: ToolTipManager for Plasma style tooltips + * NEW: Wallpaper plugins for Containments + * NEW: Context for coordinating and publishing Context information, such as the current Activity or phsyical location + * Applet + * sizeHintChanged() signal. A containment (such as a panel) can adjust its size based on changed sizehints of an applet + * ContextConstraint events and access to the current Context object + * Shortcut editor in Applet configuration dialog allows setting of global shortcuts per-widget + * NEW: Plasma::Extender and Plasma::ExtenderItem, allowing visual, relocatable extensions to Applet + * NEW: PopupApplet, which switches between an icon with a popup in a Horizontal/Vertical containment (such as a panel) and the widget directly on the canvas in a Planer containment (such as a desktop), the icon can be replaced with custom graphics or widgets by the implementation + * Containment + * Support dropping of arbitrary data if the mimetypes are supported by an applet + * Activity naming and Context support + * Toolbox can be positioned in any corner or edge + * Toolbox actions are displayed as an always open toolbar when zoomed out for improved usability + * Corona + * addOffscreenWidget/removeOffscreenWidget: manage canvas items that should not appear in a primary view (e.g. Extenders) + * "screen" information querying added to Corona to allow different approaches for viewports; e.g. using KWindowSystem and QDesktopWidget for the Plasma shell, but window coordinates for plasmodviewer or other non-desktop apps. + * Package system + * PackageStructure supports arbitrary URIs (in addition to auto-discovery for package) structure description files. + * Package removal + * Package browsing interface can be provided by the PackageStructure plugin, allowing for 3rd party widget collectionsto offer online browsing and fetching customizations + * FrameSvg + * setting the margins via in-SVG hints + * paintPanel API additions to make it more like QPainter and Plasma::Svg + * Runners + * TryExec key in .desktop file now supported, allowing runners that rely on an external application to be conditional loaded at runtime + * Service + * Allow enabling/disabling of operations at runtime + * Associate widgets with operations + * Theme + * Support compressed SVGs (*.svgz) + * Supports a pixmap cache and a SVG element rects cache for faster startup + * Widgets + * NEW: Plasma::Slider based on QSlider + * NEW: Plasma::TabBar based on QTabBar, usable as both a standalone tabbar or as a tabwidget + * NEW: Plasma::Frame used to visually group widgets + * NEW: Plasma::TreeView used to put tree views or simple list in the canvas + * NEW: Plasma::BusyWidget used to indicate the loading of something + * NEW: Plasma::ToolButton based on QToolButton + * Plasma::PushButton is svg-themed + +* Significant Fixes + * WebContent painting update synchronization (fixes, among other things, scrolling) + * Prevent crash when Containment that AppletBrowser is associated with is deleted + * Applet handles work with all icon sizes (user system settings) + * Fix View so that when a new activity is added, don't move more than necessary; + fixes dragging lock + * Dramatically fewer full paints by properly watching the exposed rect area, which for full screen Containments or large/complex Applets is quite significant in terms of performance + * Fix on-exit crashes related to focus handling issues in QGraphicsView + * Prevention of premature calls to Applet::constraintsEvent + * Applets now resize in "real time" without the pixelation + * Numerous crash fixes pushed upstream to QGraphicsView as well (focus resetting, scene queries on deletion..) + +Developer Documentation +----------------------- +* Design documentation + * NEW: dataengine + * NEW: tooltips + * NEW: wallpaper + * NEW: widgets + * NEW: javascript + +* Tutorials + * NEW: Extenders http://techbase.kde.org/Development/Tutorials/Plasma/UsingExtenders + +Desktop Theme +------------- +* NEW: widgets/button for theming pushbuttons +* NEW: widgets/frame for theming Plasma::Frame used also in other widgets +* NEW: widgets/scrollbars to theme Plasma::ScrollBar and views scrollbars in libplasma +* NEW: widgets/configuration-icons: little icons used for configuration purposes across Plasma +* NEW: widgets/extender-background, widgets/extender-dragger: SVGs for new Extender widget +* NEW: widgets/tooltip: tooltip SVGa +* NEW: widgets/calendar +* NEW: widgets/containment-controls: icons and buttons for sizing panels and other containment tasks +* NEW: widgets/monitor: monitor for use in displaying mockups of on-screen content +* NEW: widget/glowbar: a glow for use by offscreen panels to indicate their location +* NEW: dialogs/kickoff: theming for the kickoff launcher menu +* color roles for buttons text and background added +* Plasma::Slider follows theme +* Applet handle all svg-themed +* The default Oxygen desktop theme radically improved across the board + +Plasma Desktop Shell +-------------------- +* Features + * Desktop + * New Containment configuration for all desktop containments: wallpapers, activity type, theme + * Panel + * Resizes itself when an applet changes its sizehint according to its maximum and minimum sizes + * Autohide and windows-cover-panel options + * Easier drag and drop to panel by showing an empty area between the two applets nearest to the mouse cursor + +* Significant Fixes + * Reposition toolbox when zooming out/in to avoid panel overlap + * Draw the missing part of the toolbox when repositioned because of a panel + * MS Windows compile fixes + +Other Shells +------------ +* plasmoidviewer new command line options: + * --list lists known applets + * --formfactor (-f) + * --containment (-c) + * --wallpaper (-w) + * --pixmapcache (-p) + +Tools +----- +* Plasma Engine Explorer + * Remove/update sources via context menu + * NEW: Plasma::Service interaction UI, using DataEngine::serviceForSource +* plasmapkg + * now supports listing, updating and removing packages +* NEW: Destkop Theme Details + * Customize and create new desktop themes with kcmshell4 destkopthemedetails + +Plugins/Addons +-------------- +* Features + * Plasmoids and Containments + * NEW: Incoming messages: listens to various F/OSS email and IM apps for messages arriving + * NEW: Leave A Note: let people leave you notes on your computer, particularly handy for Plasma widets on the screensaver + * NEW: Previewer: drop files to preview them right on your desktop or panels + * NEW: System monitor + * NEW: Web browser: a WebKit browser in a desktop widget! + * NEW: Activity bar: quick switcher for Plasma activities + * NEW: Conway's game of life + * NEW: RSSNow! fancy online news ticker + * NEW: News, simple online news ticker + * NEW: Pastebin, drop text/data to upload it to a pastebin server + * NEW: Text snippets generator + * NEW: Timer + * NEW: Bouncing ball, throw it and watch it bounce around! + * NEW: Calendar + * NEW: Character selector + * NEW: Eyes + * NEW: LCD Weather Station plasmoid + * NEW: Quicklaunch plasmoid + * Task bar + * Grouping: manually or by application + * Sorting: manually or by program name + * Limit windows shown by desktop, screen or minimized status + * Multiple rows of windows + * Theming improved to look more consistent with other buttons and generally be more sexy + * System tray + * Supports hiding of icons + * Supports non-Freedesktop.org icons (e.g. full plasmoids or future creations) through a protocol abstraction + * Listens for and displays popup notifications and application jobs (e.g. file transfers) + * Now Playing media player controls: play, pause, stop, switch tracks and more from Plasma + * Notes + * Custom colors + * Export notes to file + * Text formatting, such as alignment, bold, italic, etc + * Clocks + * Custom colors for digital clock + * Standardized multi-timezone support in libplasmaclock + * Folder view + * Filtering improvements: mimetype and exclusion based. + * Sorting options, options for locking the icons in place and/or forcing grid alignment. + * Uses a top-to-bottom icon arrangement when used as a containment. + * Remembers icon positions. + * No longer blocks the GUI when loading large folders. + * Shows a busy animation while loading folders / laying out icons. + * Works in panels looking like a popup menu + * Default desktop containment + * Wheel mouse == switch desktop + * Creation of widgets on desktop containments puts them in empty areas, avoiding overlap + * Comics + * Respects web proxy and other relevant KDE settings + * Improved mouse interaction (middle mouse zooms, context menu, etc) + * Comicproviders can be written in any language supported by Kross + * Comicproviders can be downloaded via "Get Hot New Stuff" + * New comicproviders can be added on kde-files.org + * Lancelot + * Full keyboard support + * New lists with scrollbars + * Themes that match Plasma themes from kdeplasma-addons module + * Twitter + * When it is in the panel it is a popup, showing the avatar of the last person who sent a message and the numer of unread tweets as icon + * Trashcan + * Lets you configure system wide trash behaviour like size limits + + * DataEngines + * NEW: Web site icons (favicons) engine + * NEW: Desktop notifications engines + * NEW: System monitor engine based on ksysguard (cpu, memory, heat, sensors, etc) + * NEW: Network engine + * NEW: Application jobs (aka kuiserver) engine; shows any published KJobs such as file operations + * NEW: Executables: run commands and get their output from stdout and stderr + * NEW: RSS + * NowPlaying + * Plasma::Service for controlling media players + * supports MPRIS + * Powermanagement + * Added a new source for managing PowerDevil profiles + + * Runners + * NEW: Kate sessions runner + * NEW: Recent documents runner + * NEW: Spellcheck runner + * NEW: Konqueror history runner + * NEW: Konqueror sessions runner + * NEW: Konsole sessions runner + * NEW: Nepomuk search runner + * NEW: Power management controller (via PowerDevil) + * NEW: Places (as seen in dolphin, file dialog, kickoff, etc) runner + * Converter runner now does speed, masses and nautical distances; conversion code moved to a shared lib + + * ScriptEngines + * NEW: Google Gadgets + * NEW: Python Plasmoids + * NEW: Ruby Plasmoids + * MacOS Dashboard widgets: improved JS API compliance + * JavaScript: + * DataEngines + * Services + * layouts + * configuration data + * much more featureful plasmoid object replaces applet object + + * Wallpapers + * NEW: Image, Slideshow and Color plugins, based on previous code + * Directories recursed for images in Image and Slideshow plugin + * Slideshow images are randomized + +* Significant Fixes + * Various clocks ported to AppletClock for calendar and timezone consistency + * Pager crash fixes + * MS Windows compile fixes + * Taskbar shows thumbnails of windows on hover (4.1 regression) + * TwitterEngine respects web proxies and provides a proper Plasma::Service + * Kickoff + * proper menu size restoration + * Hide Shutdown and Restart options when those actions are not possible. + * Make Sleep and Hibernate entries appear and work + * use Plasma::Theme to improve visual harmony with the rest of Plasma + +KRunner +------- +* Features + * Plasma Screensaver: Plasma widget overlay for screensavers + * D-Bus autoactivation, so even if it has perma-crashed, selecting Run Command from Plasma will resurect it + * Alternate user interface: QuickSand + +* Significant Fixes + * Screensaver activation fixed to compensate for system clock changes + * Several crashes related to access of non thread safe code + +Platforms +--------- +Compile and runtime fixes for Microsoft Windows and Maemo + diff --git a/design/CHANGELOG-4.3 b/design/CHANGELOG-4.3 new file mode 100644 index 0000000000..6cb984cd85 --- /dev/null +++ b/design/CHANGELOG-4.3 @@ -0,0 +1,246 @@ +Significant changes in libplasma 3.1 / KDE 4.3 +============================================== +libplasma +--------- +* Features + * NEW: RunnerSyntax, class for describing query syntax + * AbstractRunners can now register their syntax for online documentation purposes + * Animator: no longer crashes when an animation is started/stopped on a widget already being animated during animation + * RunnerContext + * Introduced the isValid() method so runners can abort matches early + * Plasma::Dialogs now animate show/hide if composited window management is available + * Security + * Categories of applets can be excluded from the appletbrowser + * co-operative security + * Shortcuts + * Defaults changed + * NEW: Config dialog + * Theme + * Themes can now define options in the [Settings] group of the metadata.desktop file: + * UseNativeWidgetStyle=true and Plasma will default to native (QStyle) painting for all widgets + * FallbackTheme= to define which fallback theme to define which theme to use as the first fallback for missing elements + * Backgrounds can now have variable backgrounds + * Ability to list all known themes + * Extenders + * NEW: ExtenderGroup, class for grouping ExtenderItems. + * hasItem function in Extender to provide an easy way for applets to avoid duplication of ExtenderItems. + * Improved drag-and-drop + * Svg + * Ability to set Theme object to use + * Ability to turn off disk caching is not needed/desired + * Wallpaper: + * Cache for completed wallpaper renderings + * Threaded rendering of image files to specified size, scaling method and coloring + * Improved settings save/restore/management, including a configNeedsSaving signal + * DataEngine access modelled after the same facility in Applet + * Widgets + * NEW: SpinBox + * NEW: VideoWidget + * NEW: ScrollWidget. A widget that puts scrollbars when it contains a widget bigger than him. + * NEW: TextBrowser + * TabBar styling and interaction improvements (animations, click-n-flick, ..) + * WebView can scroll its contents by simply dragging a point in the page with the mouse + + +Other Libraries +--------------- +* NEW: KNotificationItem library added to kdelibs/experimental/ which provides access to the new system tray protocol (complete with legacy fallback support) +* NEW: ScreenPreviewWidget to libkworkspace that works with Plasma::Wallpaper or pixmaps that provides a nicely themed and appropriately sized "monitor" to show screen previews in +* NEW: RememberTheMilk library added to kdeplasma-addons/libs +* NEW: Conversion librarya dd to kdeplasma-addons/libs + * Add fuel efficiency + * Add time + * Add power + * Add currency + * Add energy + * Add pressure + * More SI Units + * Add beaufort scale to velocity + * Add delisle, newton, rankine, reaumur & romer to temperature +* libplasmaclock + * Text to speech service (KTTSD) to read aloud the time + * Holidays shown in pop-up calendar +* libtaskmanager: signficant stability fixes, particularly crashes in closeGroup or add(Task) caused by changes in the grouping strategy + +Developer Documentation +----------------------- +* Simple JavaScript API documentation +* System tray design + +Desktop Theme +------------- +* Features + * FrameSvg + * NEW: overlays for a filigrane effect, can be either random positioned, fixed, scaled or tiled + * configuration-icons + * NEW: collapse/restore icons for use in ExtenderGroup + * Desktop and panel toolboxes are now svg themed + +* Themes must now set the X-KDE-PluginInfo-Name entry in the metadata.desktop correctly, namely to the name of the directory containing the theme (e.g. default or Clean-Blend). While this was always the case, it was never relied on by libplasma. As of 4.3 there is now code that does rely on this being properly set. + +Plasma Desktop Shell +-------------------- +* Features + * Renamed binary from plasma to plasma-desktop; config files also changed as a result + * Desktop Toolbox: it's smaller and shows the activity name if any is set + * ZUI: the global actions such as add activity and lock desktop are on a global toolbox when the desktop is zoomed out. + * Configuration gui to activate a different activity for each desktop and to decouple the dashboard contents with desktop contents + * Panels + * access to spacers to control how widgets arrange themselves + * 'Windows go below' mode + + +Other Shells +------------ +* plasmoidviewer + * supports loading uninstalled packages by passing the path to the package + * can define the wallpaper to load + * can use a theme different from the one used in the workspace + * improved containment usage, so loaded plasmoids behave more like they do in plasma-desktop + * --theme switch to allow viewing plasmoids with different stylings + +Tools +----- +* NEW: plasmawallpaperviewer: view and configure wallpapers in a window, perfect for testing new wallpaper plugin features +* plasmaengineexplorer provides a --list command line switch + +Plugins/Addons +-------------- +* Features + * Plasmoids and Containments + * NEW: Bubble Monitor: track system status via a lava lamp style visualization + * NEW: Magnifique: View and magnify sections of the Plasma canvas + * NEW: Media player: Play video and audio files by dragging them on the desktop + * NEW: openDesktop Plasmoid: Basic interaction with openDesktop contacts + * NEW: RememberTheMilk: interface for the online time management service of the same name + * NEW: System Load Viewer: simple yet effective retro-style system load and memory usage display + * NEW: Unit converter + * NEW: Weather: 5 day forecasts and more in a visually stunning outfit + * Bouncing Ball: + * Theme support, including a new soccer ball + * Comics + * Added optional Nepomuk support when manually saving a comic strip. + * Added per comic setting to store the current position. + * File Watcher + * Added support for filtering (both regular expressions and exact matches are supported). + * Microblogging (was KDE Twitter) + * Added support for other Twitter API based services like identi.ca (that is based on laconi.ca) + * Pager + * User can choose to do nothing, toogle the desktop or the dashboard when he click on the current desktop. + * Notes + * Highlight current line + * Change font size with Control + Scrollwheel + * Folder View + * Added keyboard navigation to iconview. User can navigate using direction keys and launch using Enter/Return. + * Pastebin + * New look and feel + * Pasting through context menu + * Using knotification to tell the user that the data has been saved online + * History of pastebin URLs + * System tray + * New D-Bus based system tray protocol + * Ability to show/hide icons based on category + * Jobs show file/dir progress and ETA + * Jobs are grouped together: one group for running jobs and one for completed jobs. + * Completed jobs have a different appearence and allow the user to open the destination file/directory. + * Notifications now show custom images like kopete's contact images. + * Multiple trays in the same Plasma application supported. + * Can configure whether or not notifications/jobs should autohide shortly after appearing. + * Taskbar + * New popup menu for grouped tasks with animation and tooltips for each item + * Tooltips of grouped tasks can show tooltips of multiple windows + * Hovering of tasks (grouped or not) will request the window manager (at its discretion) to highlight those windows + * Web browser + * Real page zoom instead of just font scaling + * Can scroll the page by dragging the contents themselves + * LCD Weather Station + * New config dialog + * Use geolocation dataengine for initial city if available. + * Use Condition Icon for current weather and fallback to pressure if not available. + * Show pressure tendency arrows. BUG:184457 + * System monitor + * Handle new/removed network interfaces in network monitor + * NEW: Memory monitor. BUG:182704 + * temperature plotter uses now same range as analog meter + * Temperature widget now defaults to plotter in panel + * Show tooltips when in panel. BUG:180102 + * Advanced config page added + * Plotter labels added + * Timer + * A timer can have a title + * Count down is restored after a plasma shutdown/restart + * User can hide seconds. + * Lancelot + * theming: Scrollbars follow Plasma style + * actions for system buttons are configurable + * highlighted path in application browser + * clear documents history + * places are loaded from Dolphin + * applications in menu are sorted + * Quicklaunch + * Icons can be re-ordered with drag-and-drop, show tooltips and icon size is configurable, + retained even with resizing + + * DataEngines + * NEW: Akonadi engine: emails, contacts and microblogs from Akonadi + * NEW: Calendar: holiday and event information + * NEW: Geolocation: publishes location information based on GPS input or IP address; sports plugins + * NEW: Keystate: keyboard modifier and mouse button state information + * NEW: Metadata: access to Nepomuk data on files + * NEW: OCS engine: interface for scripted applets to Open Collaboration Services + * NEW: PotD: Grabs the "picture of the day" from various online sources + * NEW: RememberTheMilk: support for the online time management service + * Time engine can also report sun positioning, sunrise, sunset and moon phase information + + * Wallpapers + * NEW: Virus: artificial life simulation, watch it colonize and eat your wallpaper! + * NEW: Weather: selects wallpapers based on weather station information + * NEW: Mandelbrot: draws fractals on the desktop; click and drag or zoom in/out to explore the Mandelbrot set + * NEW: Marble: interactive desktop globe as your desktop background; powered by KDE's Marble + + * Runners + * Converter + * If not exact unit found offer possible mathes. + + * ScriptEngines + * Simple JavaScript API + * Support for Url objects + * Added new Plasma::Widget entries + * Completed LinearLayout API + * file() method to get paths from the Plasmoid's package + +* Bugfixes + * Plasmoids and Containments + * Show Desktop + * Fix keyboard shorcuts BUG:180661 + * News + * Better alternate background color for generated css. BUG: 185527 + * correctly use feed urls with parameters BUG:178936 + * LCD Weather Station + * Replace "N/A" with "-" (Looks better in lcd letters). BUG:186610 + * Make credit clickable. BUG:183984 + * Fix wind speed and unit on some data sources. + * System monitor + * Allow smaller minimum size. BUG:186501 + * Limit initial temperature count to 5. BUG:178314 + * Lock buttons when desktop is locked. BUG:178469 + * Fix config. BUG:181378 + * don't crash when removing an applet by it's own context menu BUG:178860 + * Display an IconWidget when there are no available sources. BUG: 178192 + * activate layout to calculate fixed size ratio correctly on applet start. BUG: 176268 + * Walpapers + * Slideshow mode of Image plugin takes ~97% less time to build the wallpaper list and allocates a fraction of the memory in the process. BUG: 190632 + +KRunner +------- +* Features + * Display all known syntax in the results area when Help button pressed + * Vertical display of results with new selection style and animation + * Configure parameters on matches by clicking on the configuration wrench, for matches that support options + +* Bugfixes + * Race conditions affecting proper selection of default item fixed (often known as the "I pressed and nothing happened!" bug) + + +Platforms +--------- diff --git a/design/CHANGELOG-4.4 b/design/CHANGELOG-4.4 new file mode 100644 index 0000000000..8abfc3eb45 --- /dev/null +++ b/design/CHANGELOG-4.4 @@ -0,0 +1,169 @@ +Significant changes in libplasma 3.2.0 / KDE SC 4.4 +=================================================== +libplasma +--------- +* Features + * ToolTipManager and ToolTipContent: Tips can now be set as clickable, allowing the mouse to enter them and select an item + * Corona + * Supports defining default ContainmentAction sets + * DataEngine: + * DataEngine::forceImmediateUpdateOfAllVisualizations() allows an engine to push the current data in sources out to all + visualizations without waiting for their timeout period. Useful for changes that can not wait, such as time updates + or data that becomes available with network availability. + * DataContainer::forceImmediateUpdate() allows engines to do a forced update on just one source. + * ContainmentActions plugins: allows context menus and mouse interaction to be defined separately from the containment + * Added Separator widget. + * Wallpapers can now accept drops by defining X-Plasma-DropMimeTypes in their .desktop file and connecting to the + urlDropped(KUrl) signal + * Widgets can be published on the network and be accessed remotely. In the case of scripted plasmoids the entire plasmoid package will be sent over, and in any case, dataengines and services will be routed to the remote machine. This is all completely transparent to applets. + * The applet handle supports resize, rotate and move with multitouch gestures on platforms that supports them + * WindowEffects: a class that permits to control KWin effects in an eas way, hiding the Xatom management + * Adding new Plasma::Animator based on Qt 4.6 Animation API (a.k.a. kinetic). The following animations are implemented: fade, grow, slide, rotation, pulser. + * Package: contentsHash() provides access to a SHA1 hash of the contents of the package + * Applet::SetAssociatedApplication(): It's possible to associate an application and/or a list of urls to an applet, a new icon will appear in the applet handle. + It will open that application or urls. + * KRunner: X-Plasma-Args= can be used by runners to specify plugin-specific setup (used in mediawiki runner to point to a mediawiki instance) + * Wallpapers can now specify a list of wallpaper-plugin specific contextual actions to be added to the context menu. +* Bugfixes + * Context menu handling on PopupApplets now matches what Applet gets + +Developer Documentation +----------------------- + +Desktop Theme +------------- + * NEW widgets/action-overlays used for overlays for icon actions, for instance select/deselect files in folderview + * NEW widgets/line-edit used to theme lineedits slider and editable comboboxes + * slider: new focus and hover indicators elements + * widgets/background: hint-overlay-pos-right and hint-overlay-pos-bottom to control where the overlay will be anchored + +Generic shell +------------- + * NEW library in workspace called plasmagenericshell that contains elements shared by the desktop shell, the netbook shell and the screensaver one, such as: + * Wallpaper setup UI + * Mouse plugins configuratin UI + * NEW Widgets Explorer that replaces the old applet browser. + It shows all the available widgets in an horizontal or vertical list + with a tabbar to chose the available categories and an input field to search among all installed widgets. + +Plasma Desktop Shell +-------------------- +* Features + * D-Bus + * NEW showDashboard bool + * NEW perVirtualDesktopViews bool and setPerVirtualDesktopViews bool + * Desktop Settings + * Desktop theme configuration has been moved to System Settings -> Style + * Workspace actions moved to System Settings -> Desktop + * Ability to select between plasma-desktop and plasma-netbook added in system settings + * ECMA Script; see kdebase/workspace/plasma/design/plasma-desktop-scripting for more info + * the plasma-desktop configuration can be interacted with via the interactive scripting console + * the initial layout can be set with scripts in share/apps/plasma-desktop/defaults/ + * updates to plasma-desktop can be pushed by placing scripts in share/apps/plasma-desktop/updates + * Uses the new Widgets Explorer +* Bugfixes + + +Plasma Netbook Shell +------------------- +New components: + * plasma-netbook shell: can be enabled instead of the Plasma Desktop Shell from a Systemsettings module + * Containments: + * Newspaper: desktop widgets ordered in two or more vertical columns + * Search and Launch: full screen krunner interface with bookmarks support for the search results + * Netpanel: a simple panel + * Applets: + * Searchbox: lauches searches on the Search and launch containment + * Current app control: provides a titlebar in the panel with a button to quickly switch windows + * Dataengines: + * Searchandlaunch: communication between searchbox and search and launch containment + * Shares the Widgets Explorer, wallpaper and mouse plugis settings with the other shells + + * Desktopthemes: + * A simpler version of air to be used there + + +Other Shells +------------ + +Tools +----- + +Plugins/Addons +-------------- +* Features + * Plasmoids and Containments + * NEW Blackboard: a simple paint canvas, it's possible to paint with the mouse or with multitouch events on platforms that supports them + * NEW KDE Observatory: keep track of the development of your favorite KDE projects + * NEW KIMPanel: KDE Input Method Panel for multibyte input + * NEW Knowledge Base: query the knowledgebase of opendesktop.org + * NEW On-screen keyboard + * NEW OpenDesktop.org Activities: activity information from opendesktop.org + * NEW Qalculate math expression evaluator, based on libqalculate + * NEW Spellcheck: a quick spell checking area + * NEW Webslice: a plasmoid displaying a part of a web page + * NEW Window list + * Battery: Improved layout, better support for bigger fonts + * Device notifier + * Visual revamp to make it look and feel more Plasma + * Supports non-removable volumes + * Can show/hide devices from the interface + * Popup autohides after 10s of innactivity + * Picture Frame: now supports loading and displaying of remote images + * Microblog: can view replies and direct messages + * Quicklaunch: ascending and descending sort for applications in quicklaunch applet. + * SystemTray + * now can embed other plasmoids + * supports the finalized version of the StatusNotifier specification + * old notifications remains accessible for a while, separed by application name, browseable with a tabbar widget + * Tasks + * clicking on a window preview now activates that window + * mouse over on a window preview highlights the window if the highlight windows KWin effect is activated + * control+click on a window group launches the KWin present windows effect if activated + * new taskbar animations + + * DataEngines + * NEW Pastebin + * Weather + * NEW wetter.com plugin allowing weather to be fetched from wetter.com + + * Runners + * NEW audioplayercontrol: control any MPRIS enabled audio application (amarok is the default) + * NEW plasma-desktop: "desktop console [script file]" triggers the interactive scripting window + * NEW solid: devices and hotplug support using Solid + * NEW mediawiki: generic mediawiki searching with .desktop files for wikipedia, techbase, wikitravel and userbase by default + * NEW windows: switch between and control windows and desktops + * Service runner: a query on a freedesktop application category, like "network" will show all applications of that category + * Bookmarks runner: the query "bookmarks" (or its translation) lists all the bookmarks + * Contacts runner: the query "contacts" shows all contacts + + * Wallpapers + * Image wallpaper combobox has been replaced with a nicer view. + * Image wallpaper offers 'Next Wallpaper Image' contextual action in slide-show mode. + + * ScriptEngines + * NEW JavaScript DataEngines + + * NEW ContainmentActions(es) + * NEW Switch Activity + * NEW Switch Desktop + * NEW Switch Window + * NEW Context Menu + * NEW Paste + * NEW App Launcher + +* Bugfixes + +KRunner +------- +* Features + +* Bugfixes + + +Platforms +--------- +* Features + +* Bugfixes + diff --git a/design/CHANGELOG-4.5 b/design/CHANGELOG-4.5 new file mode 100644 index 0000000000..4470900da6 --- /dev/null +++ b/design/CHANGELOG-4.5 @@ -0,0 +1,80 @@ +Significant changes in libplasma 3. / KDE SC 4. +================================================= +libplasma +--------- +* Features + +* Bugfixes + +Other Libraries +--------------- +* NEW: liblancelot-dataengines library added to kdeplasma-addons/libs + * data models: krunner, files, applications, devices, kopete, kmail + currently open documents, office apps, recent documents + kmail, kopete, basic xbel +* MOVED: liblancelot moved to kdeplasma-addons/libs + * new widget: TabBar + * animations support + +* plasmagenericshell: + * widget explorer's list and icons factored out into abstract classes for use in the activity manager + +Developer Documentation +----------------------- + +Desktop Theme +------------- + +Plasma Desktop Shell +-------------------- +* Features + * Desktop scripting + * Panel and Activity objects gain config read/write capabilities, same API that Widget has + * NEW: activity manager + * activities and containments are no longer 1:1 +* Bugfixes + + +Other Shells +------------ + +Tools +----- + +Plugins/Addons +-------------- +* Features + * Plasmoids and Containments + * Lancelot + * UI Animations + * Search history + * Section buttons can be made smaller (vertical tab-like) + * Runners configuration + * New icon + * Disk usage pie-charts + * Akonadi integration for unread messages + * Option to use Lancelot as an old-school menu with cascading popups + * Tab key completion + +KRunner +------- +* Features + * Drag and drop support + * Added optional libqalculate support to the calculator runner, enabling advanced operations such as unit/currency conversion, equation solving and more + +* Bugfixes + +Scripting +--------- +JavaScript + * Runners support QScript extensions and file and network I/O + * DataEngine support QScript extensions and file and network I/O + * Plasmoids + * Extender and ExtenderItem bindings + +Platforms +--------- +* Features + +* Bugfixes + diff --git a/design/CHANGELOG-4.6 b/design/CHANGELOG-4.6 new file mode 100644 index 0000000000..80054bb55b --- /dev/null +++ b/design/CHANGELOG-4.6 @@ -0,0 +1,53 @@ +Significant changes in libplasma 3. / KDE SC 4. +================================================= +libplasma +--------- +* Features + * ToolBox is a plugin and is dependent from the Corona + Widgets + * NEW: DeclarativeWidget, used to load QML files in any C++ plasmoid + +* Bugfixes + +Developer Documentation +----------------------- +* Plasma Shell design documentation http://techbase.kde.org/Development/Tutorials/Plasma/ShellDesign + +Desktop Theme +------------- + +Plasma Desktop Shell +-------------------- +* Features + * Activity Templates + +* Bugfixes + + +Other Shells +------------ + * Netbook + * Newspaper is generalized and is now in runtime to be used as application dashboard + +Tools +----- + +Plugins/Addons +-------------- + * DesktopToolbox and PanelToolBox plugins for Plasma-desktop + * NetToolBox plugin for the Netbook shell + * new dataengine: activities + +KRunner +------- +* Features + +* Bugfixes + + +Platforms +--------- +* Features + +* Bugfixes + diff --git a/design/CHANGELOG-template b/design/CHANGELOG-template new file mode 100644 index 0000000000..c8a52fc4d0 --- /dev/null +++ b/design/CHANGELOG-template @@ -0,0 +1,43 @@ +Significant changes in libplasma 3. / KDE SC 4. +================================================= +libplasma +--------- +* Features + +* Bugfixes + +Developer Documentation +----------------------- + +Desktop Theme +------------- + +Plasma Desktop Shell +-------------------- +* Features + +* Bugfixes + + +Other Shells +------------ + +Tools +----- + +Plugins/Addons +-------------- + +KRunner +------- +* Features + +* Bugfixes + + +Platforms +--------- +* Features + +* Bugfixes + diff --git a/design/README b/design/README new file mode 100644 index 0000000000..63299a9ae1 --- /dev/null +++ b/design/README @@ -0,0 +1,9 @@ +The files in this directory contain design notes on various part of the Plasma architecture. They are written using the "markdown" syntax and automatically sync'd to the plasma.kde.org website. + + +Markdown homepage: +* http://daringfireball.net/projects/markdown/ + +Markdown syntax reference: +* http://daringfireball.net/projects/markdown/syntax + diff --git a/design/activities b/design/activities new file mode 100644 index 0000000000..58c3bbc256 --- /dev/null +++ b/design/activities @@ -0,0 +1,85 @@ + +Overview +-------- + +Activities are the "what I'm doing" of context. +They provide a way to organize stuff by project - semantically, not spatially. They have many things +in common with virtual desktops, but are separate; see http://chani.wordpress.com/2010/02/20/the-limits-of-virtual-desktops/ for why. + +An activity has a name and icon, and is always tied to one or more containments; this gives an "anchor" to start with. From there, the user can customize the containment(s), associate windows in kwin, and associate other things (eg. files) in nepomuk. Applications could offer their own activity-specific settings (eg. a "show in this activity" option for dolphin Places) and/or use nepomuk's associations (eg. when a document associated with some activity is opened, notify the activities API so that kwin can automatically associate the window it's in with the activtiy it belongs to). + +While desktop containments are tied to one and only one activity, windows and nepomuk objects can be associated with as many as the user likes. + +an activity can be stopped; this saves out and quits all the activity's containments (and will later save the windows too). +each activity has a containment for each screen; if widgets-per-desktop is enabled, it has a containment for each desktop too. This way, the activity is fully global; after all, humans really aren't good at multitasking. activities are supposed to help you focus on the task at hand. + + +UI +--- + +in 4.5 +------ + +KWin's titlebar contextmenu has a submenu for setting which activities a window is on, much like its desktops submenu. +Plasma has an Activity Manager which allows you to create, stop, destroy, and activate activities. + +Future +------ + +* More convenient ways of associating windows with containments would be nice; drag&drop? +* Can the activity manager use compositing to get thumbnails instead of icons? + +Implementation +-------------- + +this is a general overview; more detailed and short-term plans are at http://techbase.kde.org/Projects/Plasma/ZUI + +In 4.5 +------ +all important code so far has been in kdebase. + +runtime/activitymanager/ is a kded service that manages activities; it controls the list of activities and which one is currrent. It uses a config file to store the IDs and names, so that this information is available even when nepomuk is down; other data, like the icon, is fetched through nepomuk. + +workspace/libs/kworkspace contains KActivityController, KActivityConsumer, and KActivityInfo - classes that talk to the activity manager over dbus and provide an easy API. Controller inherits Consumer, and is meant only for workspace (kwin and plasma); the other two are meant for use by any application, and will move to kdelibs when they've had a chance to mature. + +workspace/plasma/desktop/shell/ contains Activity, which manages both a KActivityInfo and all the associated containments. some management of activities is done in deskopcorona too. It also contains activitymanager/ - the activity management UI. + + +Future +------- + +* Chani needs to write some examples of activity-aware applications, and put them in kdeexamples/ +* we need to decide how plasma-netbook can best use activities (there's not as much multitasking on those devices..) + +* kwin needs to automatically associate a window with the activities for its resources. +* applications need to start advertising what resources they've got in what windows +* it needs to be easy to associate documents with activites (in dolphin?) + +* the taskbar & pager shouldn't show windows from other activities (but perhaps there should be a way to reach them in case you forget where one is) +* I'd like to see a plasmoid that can go in the panel for quick activity switching +* plasma needs a tool for managing which containments belong to which screens/desktops (the most common use case is someone unplugging a monitor and then remembering they need a widget from it) +* stopping an activity should stop all of it, not just plasma; we need to close down the applications in it, like a mini-session, so that they can be restored again when the activity is restored. +* when we have location available, it should be possible to associate activities with locations +* it'd be nice to set other settings based on activity, like IM availability or whether to show (non-critical) notifications + +and I'm sure applications can come up with all sorts of other useful features :) + + +On Sessions +----------- + +closing and restoring windows may be the hardest part of activities. It depends, at least in part, on the application being able to restore itself. +there is already a session protocol for restoring apps when you log in. it's per-process, not per-window. I've heard tell that a lot of applications do not implement it particularly well; iirc, saving and quitting are often done together. I forget whether this means that telling the application to save will make it quit or just do nothing. Each process gets a session key, and on login the application is passed this key and expectted to restore itself from whatever data it has saved for that key. + +There are plenty of warts to that old protocol, and there was an effort to replace it once (by some gnome people iirc?) but it's there and lots of applications use it (at least to *some* extent). + +I think it's worth trying to re-use it. If it works, it gives us some basic support for free, and it's much easier to get applications to be better at using an existing protocol than to try and talk them into supporting a brand new protocol. Remember, we want to save/restore not just kde apps or gtk apps but any other random apps the user needs, even proprietary ones. + +So, how do we re-use it? well, for an application that's on exactly one activity, that's easy: tell it to quit, store its sssion key somewhere along with the activity id, and when it's time to restore that activity you start it again with that key. +If an application is on >1 running activity, merely tell it that one of its activities has quit. if it's activity-aware it should close down stuff that belongs to that activity; if not, oh well, at least nothing is lost. When its last aactivity is closed, we tell it to quit, and save the key with every activity it was associated with, so that as soon as one of them is opened it gets restored. +We will have to be careful that logging out doesn't duplicate any session stuff that activites handle, of course; and once this is in place we may also be able to offer the option of only opening a single activity on login. + +Oh, and we'll have to watch that the windows created as an application is restored get associated with the correct activities. I'm not sure how much work that is; depends how hard it is to go from a WID to a PID to a session key. + +Bonus points if we can get these mini-sessions saving regularly too ;) + diff --git a/design/containmentactions b/design/containmentactions new file mode 100644 index 0000000000..c1c201d130 --- /dev/null +++ b/design/containmentactions @@ -0,0 +1,86 @@ +ContainmentActions +========== + +Overview +-------- +"ContainmentActions" are components that respond to mouse events, usually by either performing an +action or showing a menu with multiple actions. + +Timeline +-------- +Introduced in: libplasma x.x (KDE 4.4.0) + +Component Type +-------------- +ContainmentActions are plugins of ServiceType Plasma/ContainmentActions. + +Component Description +--------------------- +ContainmentAction plugins are registered using .desktop files. These files should be +named using the following naming scheme: + + plasma-containmentactions-.desktop + +If a containmentactions plugin provides a configuration UI, +it should include the line X-Plasma-HasConfigurationInterface=true. + +All other entries should follow the standard .desktop specification, +supplemented by the standard KPluginInfo keys. + +Component API +------------- +Subclasses: QObject + +*** Key Methods *** + + void contextEvent(QEvent *event); +Implement this method to get events. you'll probably want to check the event type and send different +events to your own methods; see the plugins in workspace for examples. +Currently you can expect to get mouse press, release, and wheel events. +If you're showing a menu you should use MousePress and ignore MouseRelease. +If you're performing an immediate action you should ignore MousePress and use MouseRelease. + +The incoming event will always have the buttons and modifiers that the user configured as the trigger, so +there's no sense in checking those values. + + void init(const KConfigGroup &config); +Do whatever initialization is needed here (not in the constructor). + +A configuration UI can optionally be provided by overloading the configuration methods: + QWidget* createConfigurationInterface(QWidget* parent); + void configurationAccepted(); + void save(KConfigGroup &config); + +when configurationAccepted is called, you should read from your config UI and extract all your data +from it; the UI may be deleted after the function returns. +when save is called, save that data to the provided config group. + +if your plugin needs to be configured before it is useful, call setConfigurationRequired() from +init(). + +Containment Interface +--------------------- +The Containment class supports loading and using containmentactions plugins. +Subclasses need do nothing to get this support. If a subclass has extra actions it wants in the +contextmenu, it can provide them in contextualActions() the same as before - but there's no longer +any need to return standard actions like "add widgets" and "run command". + +ContextActions plugins to use are set using the setContainmentActions(const QString &trigger, const QString &pluginName) +method. an empty string removes any plugin set for the given trigger. +Trigg format is determined by the static function ContextActions::eventToString(). + +User Configuration +------------------ +It is up to the host application to provide a configuration interface, such +as a dialog, to the user. + +*** Plasma Desktop Implementation *** + +A settings dialog is provided for the DesktopView (a second page in the same dialog as the wallpaper) + +This dialog allows selecting one trigger for each installed plugin. + +Future Work +----------- +* Current UI in shells/desktop/ needs work. + diff --git a/design/context b/design/context new file mode 100644 index 0000000000..c0571fc2cb --- /dev/null +++ b/design/context @@ -0,0 +1,33 @@ + +Context is about who i am, where i am, and what i'm doing. It's my identiity, my environment and my activities. + +Identity +--------- + +[note: I don't think I really grok this. someone please find a better way to explain it] +My identity is... me. And my various hats. My logins for IM, facebook, email, etc. My status. The ability to have a separate identity for work? social desktop stuff? +It's also your login - files, config, etc. + + +Environment +----------- + +My environment affects a lot of little things. The room lighting affects the ideal brightness of my screen. Battery state affects how aggressive the laptop's powersaving should be. Time of day and location can be good predictors of what i'm working on (or whether silent mode is a good idea). Location can also indicate what timezone and weather is most useful. Network availabiity and my device formfactor affect what i can do. Who's with me is important too- i don't want my private stuff out when there are people looking over my shoulder. The computer can't detect all of those, but it should take advantage of whatever information it *does* have. + + +Activities +---------- + +My activities affect how i work. Each project tends to have its own files, resources and tools - with some degree of overlap, of course. When i'm working on one, i want everything for that task at my fingertips, and all unrelated things out of my way. I may even want a separate identity for some of them... + +The current activity is what i'm doing *now*, and the others are things i have been doing (and probably will return to). + + +Implementation +-------------- + +Activities have been implemented; identity and environment have not. + +in libplasma, there is a Context class; this has become a class to hold context information that is *local* to a containment (like which activity it belongs to); each containment has its own Context*. global context, such as location, will probably be held elsewhere when it is implemented. + + diff --git a/design/dataengine b/design/dataengine new file mode 100644 index 0000000000..95242dc564 --- /dev/null +++ b/design/dataengine @@ -0,0 +1,62 @@ +DataEngine +========== + +Overview +-------- +A Plasma::DataEngine provides read access to data via a standardized API. DataEngine is design specifically with visualizations in mind: the data structure is simple, the data is implicitly shared amongst all visualizations and many requirements are provided by the infrastructure "for free" to a DataEngine to support this mission. + +The data is exposed as a simple two level tree; the first level is made up of "sources", each of which contains a dictionary of key/value pairs. The model is purposefully simplified in this manner to prevent complicated arbitrary data structures from being exported in ways that would make it more difficult than necessary for writers of visualizations (e.g. Plasmoids). + +Users of DataEngines are usually refered to as "visualizations" as they most often provide a user visible (or audible) representation of the data. + + +Timeline +-------- +DataEngine was a part of the original libplasma release. + + +Component Type +-------------- +DataEngines are plugins of ServiceType Plasma/DataEngine. + + +Component Management +-------------------- +The Plasma::DataEngineManager class provides a loader for DataEngine plugins as well as handles reference counting of them. DataEngineManager follows the singleton pattern. + +When a DataEngine's reference count becomes zero, the DataEngine is deleted by the DataEngineManager. Plasma::Applet uses the DataEngineManager internally to provide access to DataEngines for Plasmoids. DataEngines may also be loaded directly without using DataEngineManager, but then are not otherwise managed. + +If an engine is requested of DataEngineManager that either does not exist or can not be loaded (e.g. due to a flaw in the plugin library), then a NullEngine is returned. In all cases, however, DataEngineManager will return a valid object. + + +Sources +------- +Each source is represented internally by a Plasma::DataContainer. The DataContainer does two things: it holds the key/value dictionary and it manages multiple connections drawing data at different points in time (See: Polling). Each source in a DataEngine must have a unique name. + +Often a DataEngine implementation will not handle DataContainers directly, but let the DataEngine class manage them implicitly with calls to setData. However, a DataEngine can create it's own DataContainers; this is usually done when each DataContainer represents an asynchronous job, allowing for the job to be encapsulated. + +A DataEngine may create as many (or as few) sources on its own as is reasonable. This is useful for DataEngines which represent sets of data such as hotplugged devices which do not make sense as queryable items. + +Engines may also place an upper limit on the number of sources that may be active at any time. When this limit is reached, the least recently used source is dropped in favour of newer sources. + + +Visualizations +-------------- +Visualizations are notified via signals of new sources being added, existing sources being removed and the data in existing sources being changed. Visualizations may also query for the current state of a source without connecting to it. + +When connecting to a source, the object passed to the conntectSource method as the visualization will have its dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data) slot called whenever the source changes or when the polling interval expires (whichever is later). + +When a source that doesn't exist is requested, the DataEngine is prompted to create a matching source via a sourceRequestEvent. The DataEngine is not required to create a source, however. Sources created in response to a sourceRequestEvent are automatically discarded when the last visualization connected to it disconnects. + + +Polling +------- +DataEngine allows both engine-wide polling as well as per-source/visualization polling. This is handled completely internally and results in updateSourceEvent calls. A visualization may request to be updated every N milliseconds; this will be rounded to the nearest 50ms and matched with other similar calls. Additionally, a DataEngine may request an all-sources-update every N millseconds. Multiple polling intervals on the same source are handled internally, and a minimum polling frequency may be set by the DataEngine to prevent too many calls to updateSourceEvent for the same source. + + +Services +-------- +A Plasma::Service may be associated with any source. Visualizations can retrieve the Service by calling serviceForSource. The returned service should already be associated by the DataEngine with the source in question. Therefore a Service to post to an online blogging service that is requested using the source that represents the account would post to that account. This prevents visualizations from having to load Services themselves from plugins or pre-configure them. It also allows DataEngines a way to provide mutators for sources. + +Unlike sources, Services are not shared. They are unique to the caller of serviceForSource and therefore do not need to be made multi-user safe. + diff --git a/design/ideas.txt b/design/ideas.txt new file mode 100644 index 0000000000..4ca3f0950f --- /dev/null +++ b/design/ideas.txt @@ -0,0 +1,53 @@ +Random ideas for animations usage + +== Animation code == + + - improve the design (in progress) + - rewind the animation ............................................ done! + - cache the animations ............................................ done! + - cache the animation parameters + - pulser using pixmap ............................................. done! + - fix opacity in pulser + - ZoomAnimation + - GeoAnimation + + +== Pimp my plasmoid == + +1) ublog + - edit box moved to inside of scrollwidget scrollwidget::ensureitemvisible() + - rotation when changing Timeline --> Replies --> Messages + - pulse on link selection + - fade in when selecting edit box + - drop shadow on photo + + +2) dictionary + - new busy widget + - kinetic scrolling + + +3) rss reader + - slide for post paging + - pulse for selection + + +4) weather plasmoid + - slide left <----> right for details X "3 days views" + - animated objects (sun, clouds, etc) + + +== plasma-netbook == + + - pulse the whole applet when click the maximize button in the +titlebar of the applets in the newspaper (maybe reuse it in plasma-desktop) + - grow/shrink on selection + - pulse on click + - animated layouts .................................................. done! + - kinetic scrolling: for scrollwheel events in panel ................ done! + + +== plasma widgets == + + - port then to new plasma::Animator + - tune up busy widget (small circles expand and fade out) diff --git a/design/notifications b/design/notifications new file mode 100644 index 0000000000..0939c82f76 --- /dev/null +++ b/design/notifications @@ -0,0 +1,67 @@ +Notifications visualization +=========================== + +Overview +-------- +Plasma-desktop offers facilities to display the visual notifications confirming to the Freedesktop's notification specification, this document describes the component used to achieve the visualization in the most informative, while less distracting way. + +Components +---------- +* Notification dataengine: this exposes the org.freedesktop.Notifications interface, collecting notifications arriving from all applications, in a cross-desktop fashion. + As all dataengines, it organizes the notification datafields in a Plasma::Dataengine::Data structure +* Systemtray widget: (TODO: move out of systray) the widget connects to the Notifications dataengine and provides a proper visualization for the arriving notifications. An important distinction is done between the "alert" aspect of them and a pure informative one, providing two different user interfaces for them. + +Visualization components +------------------------ + +Alert + +As soon as notifications arrive, they are placed in the NotificationStack widget: it is an automatic popup, so in order to disturb the user workflow as less as possible, the size is kept fairly small, limiting the maximum nuber of notification displayed. For the same reason ony one notification at a time is displayed: the oldest (newest?) one, with the others partially covered, but permitting to still see the title. +Moving the mouse over the title of the semi-hidden notifications will show them completely. +The NotificationStack window is always on top of every other window. +When a new notification arrives it is added in the NotificationStack with the following strategy: +* Let MAX be the maximum number of allowed notifications, usually 4 +* If the current displayed notifications is less than MAX place the new notification partially covered in the last position +* If MAX notifications are already there + * If there i at least one notification of the same application remove it (or the oldest if more than one) + * Elese remove the oldest notification from the stack +* Place the new notification partially covered in the last position + +To view partially hidden notifications is enough to move the mouse over its title. the notification will show, hiding the previously showing one. +Clicking on an action button, removes a notification from the stack. +Clicking anywhere in the stack area hides the whole stack, but in order to prevent accidental closing, only after a second of timeout after +* A new notification appeared +* A notification was open, closing the old one + + +Information + +If the user missed some notification, it's possible to view the old ones by manually invoking a different ui by hitting the "i" button. +The notifications are contained in a widget called NotificationScroller, that appears as a popup window. A tabbar lets the user filter the notifications by the sender application (useful for instance if the user is looking for informations about completition of an old running file tranfer or battery warning notices). +Notifications are in a scrolling view that permits a fairly large amount of those to be displayed without using too much screen space. +It features also the the following behaviour: +* If a new notification arrives the current status of the widget is not changed, to not interrupt the user workflow. (current selected tab and scrollbar value) +* If the user manually dismisses a notification, bot there and in the NotificationStack it goes away both in the NotificationStack and in the NotificationScroller + + +Notifications and jobs +---------------------- + +Alert + +* Completed jobs are treated as any other notification. They appear in the NotificationStack at first and are left available for a while in the NotificationScroller +* When there is at least one running job a tiny popup containing only a progressbar and a brief summary is always shown, unless the main popup with controls for each individual job is open. It shows the average of progress of all jobs, we will now refer to it as JobsOverview +* JobsOverview is shown on top of every other window at first, but other windows that gain focus after it can cover the JobsOverview window +* If the main popup is opened, JobsOverview is hidden, shown automatically again as soon as the main popup is closed. +* When the NotificationStack appears, the JobOverview is automatically moved to make room for the notifications, and moved back again when NotificationStack is hidden. + + +Information + +When the user opens the main widget popup that contains the NotificationScroller, if there are running jobs, they are shown individually as ExtenderItems in the same popup. The extenderItem contains: +* A title with the application icon, the job type (such as copying, moving or deleting) and controls to pause/stop or resume the job +* The body contains + * The source and destination URLs + * A progressbar to show the job status + * Optional extra informations such as file size, transfer speed and size already transfered + diff --git a/design/plasma-desktop-screen-handling b/design/plasma-desktop-screen-handling new file mode 100644 index 0000000000..d86078daf3 --- /dev/null +++ b/design/plasma-desktop-screen-handling @@ -0,0 +1,14 @@ +How Screens Get Managed +----------------------- +* PlasmaApp handles the views +* DesktopCorona handles the containments with the help of Activity classes + +So: +* When a screen is added: DesktopCorona tells the current Activity, which adds a new containment if needed. PlasmaApp responds to their creation +* When a screen is removed: PlasmaApp removes the associated view(s); we don't want the Containment to go away, however, since the screen may return +* When a screen changes geometry: the Views (DesktopView, PanelView) change their geometry and that of their containments if needed + +Future work: +* Migration of panels from non-existent screens to existing screens (perhaps asking if the user would like them to migrate?) +* 'hibernation' of containments from non-existent screens (Activity will handle this) + diff --git a/design/plasmoids b/design/plasmoids new file mode 100644 index 0000000000..701857317e --- /dev/null +++ b/design/plasmoids @@ -0,0 +1,82 @@ +Plasmoids +========= + +What is a widget? +================ +In the context of Plasma, a "widget" is a single, self-contained graphical object on the canvas. They can be added and removed individually, configured separately from other widgets, etc. These "mini appliations" are sometimes referred to in other widget platforms as "applets", "apps", "gadgets", "karambas", "desklets". We chose the word "widget" for Plasma as it is used by other some other existing systems. + +The API of a widget is defined by the host "ScriptEngine", with the exception of native Plasma widgets written in C++ which allows plugins in loadable librareis which use the API of the Plasma library directly. Currently Plasma supports both such "native" widgets written in C++, ones written in various dynamic languages (Plasmoids) as well as: + +* SuperKaramba's karambas +* Enlightenment 17 edje content +* MacOS dashboard widgets (though not all) + +These are loaded via host AppletScriptEngine plugins that bridge between the widget itself and Plasma's canvas. + +What is a Plasmoid? +================== +A Plasmoid is a widget that can be loaded into Plasma that uses the native Plasma API and comes packaged in a single archive file that includes the code, metadata, images, configuration defintions, etc. Plasmoids may be currently written in the following languages / APIs: + +* HTML/JavaScript widgets (essentially "web pages for Plasma"): they are limited to a small subset of the Plasma library API and DataEngines +* Simplified JavaScript: these are written in JavaScript but use a managed set of API from the Plasma library and elsewhere; these are the most likely to be widely portable between Plasma instances and are the most securable. +* Full JavaScript: a work in progress, this provides full access to the Plasma, KDE and Qt APIs +* Ruby: full access to the Plasma, KDE, Qt and other Ruby APIs are provided +* Python: full access to the Plasma, KDE, Qt and other Python APIs are provided + +There are tutorials for the various APIs avaiable here: http://techbase.kde.org/Development/Tutorials/Plasma + +The Plasmoid Package +==================== +A Plasmoid package is a single archive file compressed using the zip compression algorithm. By convention they have a .plasmoid suffix. + +The layout of a Plasmoid package is as follows, starting with the root (/) being the top of the zip archive: + +metadata.desktop: a .desktop file (INI format) following the standard KPluginInfo layout. See more about this file below. +contents/ + code/ -> where all the scripts are kept + main -> by default, the name of the script to start the Plasmoid from + config/ -> KConfigXT and INI files describing configuration variables + main.xml -> main config file, KConfigXT xml + default-configrc -> default configuration, INI file + configui/ -> Qt Designer files describing configuration dialog pages + config.ui -> main configuration UI + images/ -> image files of various formats + translations/ -> po files for individual translations; each translation should appear in subdirectories named after the language code + ui/ -> user interface description files, usually Qt Designer + +The Metadata File +================= +The metadata.desktop file contains information about the widget that is critical to proper function. This file is a standard "INI format" file with one group in it called [Desktop Entry]. A typical such file appears below: + +[Desktop Entry] +Name=JavaScript Animations +Comment=An example showing how animations can be used from JavaScript +Encoding=UTF-8 +Icon=plasma +ServiceTypes=Plasma/Applet +Type=Service +X-KDE-PluginInfo-Author=Aaron Seigo +X-KDE-PluginInfo-Category=Examples +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-Email=plasma-devel@kde.org +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-PluginInfo-License=BSD +X-KDE-PluginInfo-Name=javascript-animations-example +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website=http://plasma.kde.org/ +X-Plasma-API=javascript + +The icon can either refer to a file in the root of the package by the same name, or a standard icon theme icon name. + +Important fields include: + + * X-KDE-PluginInfo-Name= what the widget will be referred to internally and MUST be unique; as such a "reverse domain name" approach is a good idea, e.g. "org.foo.somewidget". + * X-Plasma-API entry defines which language and API the widget is written for and dictates which ScriptEngine will be loaded for it. + +Useful optional fields include: + * X-Plasma-MainScript= path relative to the contents/ dir of the package; used to override the default of "contents/code/main" + +Installing and Replacing +======================== +Plasmoid packages can be installed and removed from the Add Widgets interfaces of various Plasma applications, such as plasma desktop, or directly using the plasmapkg binary. See the output of `plasmapkg --help` for more information on using that utility. + diff --git a/design/remotewidgets b/design/remotewidgets new file mode 100644 index 0000000000..27d501da02 --- /dev/null +++ b/design/remotewidgets @@ -0,0 +1,40 @@ +Overview +-------- + +Since KDE 4.4, libplasma supports publishing and accessing plasma applets across the network. This allows users to use plasma applets as visualization on remote data. What happens when accessing a remote applet is the following: + +* A connection is established, and the server can check the clients credentials and decide whether or not to allow access. The plasma shell running on the server can decide which policies to use to determine whether or not access is granted. See the section 'security' in this document for more information. +* In the case of a scripted plasmoid, the entire plasmoid package is sent to the client. The client puts the package in a temporary directory, and loads the scripted plasmoid from there. In the case of a binary applet, just the plugin name is sent over so the client can just load the appropriate plugin. This unfortunately requires the client to have the same plugin installed as the server, so scripted plasmoids are obviously preferred. +* Whenever this remoted plasmoid calls dataEngine() or serviceForSource() these calls return a special subclass of DataEngine/Service that actually invokes the dataengine or service on the remote machine. This is all completely transparent to the applet. These subclasses are located in the "private/" directory in libplasma and are called RemoteDataEngine and RemoteService. On the first call to dataEngine/serviceForSource, the server is notified and publishes the requested dataEngine or Service, if that hasn't already happened. Publishing happens through RemoteDataEngine and RemoteService's counterparts: ServiceProvider and DataEngineProvider, also both private classes. + +Public API +---------- + +The remote widgets feature adds some new public API to libplasma, mostly aimed at plasma shells. They allow a shell to: +* Access remote widgets. (AccessManager/AccessAppletJob) +* Obtain a list of remote widgets that are announced on the network, and receive notifications when widgets appear/disappear. (AccessManager) +* Publish widgets on the network. (Applet::publish(), Applet::unpublish(), Applet::isPublished()) +* Set one of the sensible default security policy on incoming connections... (AuthorizationManager) +* ... or provide an own implementation of a security policy by implementing an AuthorizationInterface and setting it as customAuthorizationInterface in AuthorizationManager. + +Security +-------- + +The plasma shell can control the security aspects of remote widgets, combined with a system wide config file (/etc/plasma-remotewidgets.conf) containing rules that can allow/disallow certain machines access to certain published widgets/services/engines. As a plasma shell you've got the following options: + +* Use one of the sensible presets in which case you don't have to case about security. The only thing you'll need to do is set the desired behavior using AuthorizationManager::self()->setAuthorizationPolicy(). This will set the AuthorizationInterface implementation to one of the ones built in into libplasma. Do note that even when you whish to use the default policy (DenyAll), you should still call this function, since that automatically locks this value so a potential malicious plasma plugin can't change this to serve it's own evil desires. +* Supply your own behavior by implementing AuthorizationInterface, set authorizationPolicy to custom, and use AuthorizationManager::self()->setAuthorizationInterface() to your own implementation. + +Security wise, every incoming message will get processed as follows: + +Every message that gets sent to the server get's a signature appended. Signing happens with an RSA key that is generated for you if it doesn't exist already, and then stored in the kde wallet, so your private key for signing doesn't change from that moment on. When starting a connection, the full credentials of the client are sent along, later messages just contain a hash of the key to identify the sender without having to put all the information in every message. +Whenever a message is received and the Credential's trustLevel is at least ValidCredentials (meaning, the signature of the message is at least valid for the public key), the combination service name and public key signature is used to see if there are any AuthorizationRules that already match this request. These are both rules defined in /etc/plasma-remotewidgets.conf and rules that are created for the current plasma session only. If a matching rule is found, it is applied (that is, the message is either accepted or denied). Else a new rule is created, that is at that moment still Undefined. This rule, containing an instance of Credential that can be used to obtain a trustLevel, will be passed to the active AuthorizationInterface's authorizationRequest. The interface can then decide to either accept or deny the request, or request PIN pairing, in which case the user will have to enter the same password on both client and server to establish a connection. +NOT YET IMPLEMENTED: additionally, a message can also be signed with a GPG key, in which case the trustLevel can go beyond ValidCredentials. Either by having that key in your trusted keyring, or, for UltimateCredentials, having it signed by the plasma overlords themselves. + +JOLIE +----- + +For the actual communication, the sodep protocol is used (JOLIE's native protocol), through the QtJolie library. At the moment, as the library has not yet been released, a branch private to libplasma is used. JOLIE itself is not yet a runtime dependency, QtJolie just claims a socket, listens to incoming sodep messages, and routes them to the appropriate destination. At some point in the near future, when QtJolie gets some more functionality, we'll want to use JOLIE's metaservice to route the messages, which adds the added benefit that we can communicate with metaservice through jolie, and have metaservice convert it into other protocols. A nice addition is that we can then use secure sodep so all the data will get encrypted. And with the addition of a new Service subclass it will also become very easy to access SOAP webservices. + + + diff --git a/design/screensaver b/design/screensaver new file mode 100644 index 0000000000..37c703335d --- /dev/null +++ b/design/screensaver @@ -0,0 +1,61 @@ +Plasma and the Screensaver +========================== + +There are three main pieces to the screensaver picture, two of which involve plasma. +first is lockprocess, which manages the other two. second is the [k|x]screensaver binaries which just draw +pretty pictures. third is plasma-overlay, which does the plasma stuff. + +lockprocess +----------- +This process manages plasma and the screensaver binaries, and ensures that once the screen is locked +it *stays* locked until proper authentication is provided. It provides the unlock dialog too. +If a screensaver or plasma-overlay +crashes or becomes unresponsive, lockprocess will keep going, ensuring that a locked screen stays locked and can +still be unlocked by the user. It's as small as possible to keep the chances of it itself crashing very small. +The screensaver binaries are brought in via XEmbed, while communication with plasma-overlay happens +over dbus. +lockprocess also filters all keyboard and mouse events, passing them on to plasma-overlay as +necessary. if it suspects that plasma-overlay is dead (ie. dbus failure), it won't pass on events, +ensuring that the user never ends up with an unresponsive screensaver. + +plasma-overlay +-------------- +This process is a plasma shell; it has a corona and a simple view, and loads its own containment. +The containment used by plasma-overlay is very basic; just a customcontainment with a cashew +and applet handles. + +the user can add applets, but not containments. there's configuration for transparency if you have +compositing, or wallpaper if not (random wallpaper slideshow is my poor-man's-screensaver). all +configuration and applet add/remove are disabled when the screen is locked. + +it communicates with lockprocess over dbus. lockprocess will send it into an idle mode after a while +(where any events trigger the unlock dialog, and plasma-overlay uses a different transparency +setting), and tell it to go into active mode when there's activity again. when an unlock action in +plasma is activated, plasma-overlay asks lockprocess for permission, and lockprocess will ask the +user for their password (or whatever other authentication method). + +when the screen is locked, plasma-overlay offers two actions to the user: leave screensaver and +configure widgets. the first is what you'd expect; you enter your password and all of lockprocess goes +away, letting you back into your desktop. the second is more like unlocking widgets in +plasma-desktop: it brings you into configuration mode where you can add, remove and configure +widgets. this config mode can also be reached from the screensaver kcm module. from config mode you +can exit at any time without entering a password; you can also re-lock the screen, of course. + +if lockprocess vanishes from dbus, plasma-overlay will assume that it missed a signal to quit and +will commit suicide. :) this is intentional; I got sick of dbus race conditions leaving me with a +plasma-overlay hanging around if I moved the mouse just after the screensaver began to run. + +security +--------- + +Plasmoids on the screensaver obviously present a security risk. this is being minimized as described +in the security document. +what makes it interesting is that we're less concerned with malicious +plasmoids and more with malicious (or mischevious) users; a locked screen left unattended should not +end up with goatse on it, or files deleted. + +Future Work +----------- +* Multi-screen support would be awesome +* Plasmify the unlock dialog (either by using plasma theming or by having plasma-overlay run it) + diff --git a/design/security b/design/security new file mode 100644 index 0000000000..f3dee85e43 --- /dev/null +++ b/design/security @@ -0,0 +1,57 @@ +Security +======== + +overview +-------- + +plasma will have three types of security: +-co-operative +-trust-based +-enforced + +co-operative security +--------------------- + +this is fairly basic security for plasmoids. +the idea is, shells (like plasma-overlay) can specify a list of constraints in their desktop file, and then all plasmoids that require the disabled feature don't show up in the appletbrowser. also, plasmoids that would like a certain feature but don't *need* it can check hasAuthorization() and turn it off when it's not allowed. +This type of security relies heavily on plasmoid developers. Each plasmoid's .desktop file must list what features the plasmoid requires, and be kept up to date with the code. X-Plasma-Requires- can be set to Required, Unused, or Optional. Example: X-Plasma-Requires-FileDialog=Required +Features the plasmoid would like but does not require should be guarded by hasAuthorization, and set to Optional in the .desktop. [example](http://websvn.kde.org/?view=rev&revision=967792). +All the plasmoids in workspace and kdeplasma-addons should probably be reviewed before release. some of them don't have an active maintainer to take responsibility. + +Currenly, plasmoids that don't mention a constraint are assumed to not use it. however, by release time the opposite should be true. this is just so that they don't *all* disappear from the screensaver before anyone has a chance to review their plasmoids. +Note that this will not remove existing instances of plasmoids. It only hides them from the appletbrowser. Users who really really want to can edit the config file to add unsafe applets. + +Existing constraints/features: +-FileDialog. When this is restricted, use of open or save dialogs is forbidden. Reason: users can delete files from there and cause all kinds of havoc. +-LaunchApp. When this is restricted, running other applications or opening urls (generally done through KRun) is forbidden. Reason: although they're not accessible from the screensaver, they can still pile up and need to be closed after the screen's unlocked. + +^^ this is obviously not a final list. more will be added soon. + +possible problems: +-the API isn't designed for constraints changing at runtime. does anyone think that's likely to matter someday? +-I have no idea how this will affect alien widgets. personally I'd be fine with banning those from the screensaver until proper, full security can be implemented. + + +trust-based security +-------------------- + +gpg signing of plasmoids + + +enforced security +---------------- + +This is security that doesn't rely on plasmoid developers. Mostly it's about disabling access to features via the scripting API, so that plasmoids using that API absolutely cannot use the disabled features. + +it'd be nice if we could use KAuthorized to enforce some stuff on the c++ applets... I tried, but didn't get anywhere. + + +misc +---- + +I'm not sure what categories these fall under. + +category exclusion: entire categories of plasmoids can be excluded from hte appletbrowser. for example, application launchers are excluded from the screensaver because they'd never ever be wanted there. + +configuration locking: on the screensaver, config dialogs can't be accessed while the screen is locked. however, it's possible for a c++ plasmoid to override showConfigurationInterface and circumvent this. + diff --git a/design/standardEffects b/design/standardEffects new file mode 100644 index 0000000000..6205bac978 --- /dev/null +++ b/design/standardEffects @@ -0,0 +1,22 @@ +Standard Graphical Effects +========================== + +This document lists the standard effects provided by (or yet to be implemented) in libplasma. + +CrossFade +--------- +Fades between two images. + +Parameters: + * Source image + * Destination image + * Timelapse (0 -> use standard time setting) + +Spin +---- +Creates the illusion of spinning an item to show its back. + +Parameters: + * Source QGraphicsItem + * Destination QGraphicsItem + diff --git a/design/systemtraywidget b/design/systemtraywidget new file mode 100644 index 0000000000..8bd9f689fe --- /dev/null +++ b/design/systemtraywidget @@ -0,0 +1,55 @@ +The system tray widget is divided into three parts: core, ui and protocols. + +Core +---- +Core provides the three basic concepts that drive the widget: + * Task: an icon in the tray + * Notification: a visual notification from an application + * Job: a long running job such as a file transfer + +Protocol implements a source for Tasks, Notifications and/or Jobs and has a set +of signals used to announce the coming and going of its items. At its discretion, +a Protocol can create (or destroy) Tasks, Notifications and Jobs at any time. + +A Manager class is provided in Core that looks after and tracks the three kinds of items +along with all the Protocols. The Manager instantiates the Protocols and relays the +critical signals, allowing the widget to simply pay attention to the Manager and +treat all Tasks, Notifications and Jobs as if they came from the same place. The Manager +is able to treat all Protocols generically using the Protocol API. + +All Task implementations provide a way to return a QGraphicsWidet for given host. +The mapping between hosts and widgets is handled automatically by the Task class, and +this, along with the central Manager, allows any Task to be shared simultaneously across +multiple widgets. + +Notifications and Jobs don't have this problem as they simply provide access to +data which the ui is reponsible for showing in some way. + +Also provided in the core is a Task specifically for managing the Extenders of a host +Plasma::Applet for Jobs and Notifications call, rather creatively, ExtenderTask. + + +Protocols +--------- +The following protocols currently exist: + +DBusSystemTrayProtocol: support for the dbus driven NotificationItem; it provides Tasks; + for information on the design of this protocol, consult the + KStatusNotifierItem documentation + +FdoProtocol: support for the XEmbed based freedesktop.org system tray specification; + it provides Tasks, though these tasks do not work properly in more than one + widget at a time due to limitations in the XEmbed system + +JobsProtocol: access to the applicationjobs DataEngine; it provides Jobs + +NotificationsProtocol: access to the notificadtions DataEngine; it provides Notifications + +PlasmoidProtocol: features Plasmoids-wrapped-in-Tasks; is not currently fully functional + + +The UI +------ +The user interface consists of a Plasma::Applet class, a layout class and a TasksArea which +contains the Tasks published. The UI features the ability to order as well as hide items, +register new Tasks, etc. diff --git a/design/tooltips b/design/tooltips new file mode 100644 index 0000000000..7d4bbb5874 --- /dev/null +++ b/design/tooltips @@ -0,0 +1,39 @@ +Tooltips +========== + +Overview +-------- +libplasma provides facilities to manage tooltips that display extended information in a visually pleasing and consistent manner for QGraphicsWidgets. These tooltip windows are themed using Plasma::Theme. + +Timeline +-------- +Introduced in: libplasma 2.1 (KDE 4.2.0) + +Component Type +-------------- +ToolTipManager is a QObject subclass in libplasma. +ToolTip is an internal QWidget subclass in libplasma. + +Registering Tooltips +-------------------- +ToolTipManger is quite simple to use: simply call setWidgetToolTipContent using the struct ToolTipContent. This causes the widget to be registered if it isn't already. Alternatively, for widgets that fill the data on demand, call registerWidget directly. + +Widgets are unregistered automatically upon destruction, but can also be manually unregsitered by calling ToolTipMnager::unregisterWidget. + +An event filter is registered on each registered widget and hover events that occur trigger calls to showToolTip and hideToolTip. The hover events are not, however, filtered out. + +Note: If another event filter is placed on registered widget that filters out these events, it may be necessary to manually call showToolTip and hideToolTip. + +ToolTip Updates +--------------- +Updating tooltip data is supported by means of invoking slots on the associated widget if they exist. In particular, just prior to being shown the toolTipAboutToShow method is invoked and when hiding the toolTipHidden method is invoked. + +ToolTip Visualization +--------------------- +The internal (non-exported) ToolTip class is used to actually display the tooltips. These are associated with widgets and created as necessary. + +ToolTip displays are only created when data is set, and are deleted if an empty ToolTipData struct is passed in. This keeps memory usage due to ToolTip objects down. + +Future Work +----------- +N/A diff --git a/design/wallpaper b/design/wallpaper new file mode 100644 index 0000000000..ae623266e4 --- /dev/null +++ b/design/wallpaper @@ -0,0 +1,99 @@ +Wallpapers +========== + +Overview +-------- +"Wallpapers" are components that paint the background for Containments that +do not provide their own background rendering. + +Timeline +-------- +Introduced in: libplasma 2.1 (KDE 4.2.0) + +Component Type +-------------- +Wallpapers are plugins of ServiceType Plasma/Wallpaper. + +Component Description +--------------------- +Wallpaper plugins are registered using .desktop files. These files should be +named using the following naming scheme: + + plasma-wallpaper-.desktop + +If a wallpaper plugin provides more than on mode (e.g. Single Image, Wallpaper) +it should include a Actions= entry in the .desktop file, listing the possible +actions. An actions group should be included to provide for translatable names. + +Example: + +> Actions=SingleImage,Slideshow +> +> [Desktop Action SingleImage] +> Name=Image +> Icon=image-jpg + +All other entries should follow the standard .desktop specification, +supplemented by the standard KPluginInfo keys. + +If a wallpaper plugin should be constrained to a specific set of FormFactors, +include a list of those FormFactors using the X-Plasma-FormFactors key. + +Defined categories for wallpapers include: + * Single Image + * Slideshow + * Animated + * OpenGL + * Interactive + +Component API +------------- +Subclasses: QObject + +*** Key Properties *** + +boundingRect: the geometry to paint the wallpaper into +formFactors: the Plasma::FormFactors this wallpaper supports (e.g. Planar) + +*** Key Methods *** + +The update(QRectF) signal must be emitted whenever the wallpaper needs a repaint. +This might happen in response to the wallpaper changing (such as in a slidshow) +or due to an animation update. Keeping the exposed area to a minimum is important. + +Painting is done in the paint(QPainter *painter, const QRectF &exposed area) +method, and will be called when a paint operation should be done, either due to +an external event or in response to an update request from the wallpaper. + +A configuration UI can optionally be provided by overloading the configuration +method. + +Containment Interface +--------------------- +The Containment class supports loading and using a wallpaper plugin. +Subclasses need do nothing to get this support, and can opt out of +wallpaper support by calling setDrawWallpaper(false). + +When a wallpaper plugin is used, Wallpaper::paint is called from +Applet::paint. + +The Wallpaper plugin to use is set using the setWallpaper(const QString &pluginName) +method. + +User Configuration +------------------ +It is up to the host application to provide a configuration interface, such +as a dialog, to the user to embed the wallpaper config in. + +*** Plasma Desktop Implementation *** + +A setting dialog is provided by the View (DesktopView, PanelView, etc). + +This dialog allows selecting both the Containment type for +the current Activity associated with the view as well as selecting the +Wallpaper plugin for the Containment. + +Future Work +----------- +* Wallpapers should be ScriptEngine-able +* Ability to define which wallpapers a Containment is compatible with? \ No newline at end of file diff --git a/design/widgets b/design/widgets new file mode 100644 index 0000000000..62be62987e --- /dev/null +++ b/design/widgets @@ -0,0 +1,43 @@ +Widgets +======= + +Overview +-------- + +Within libplasma there are a collection of widgets (Plasma::PushButton, Plasma::Slider, Plasma::TabWidget, Plasma::WebContent, etc.) which all follow a common API theme. The intention of this API is to provide a very simple way for users to create plasma applets. Plasma is not primarily a widget library, and so a simple wrapping of native Qt widgets is provided. + +By providing a consistent, simple API that hides details like QGraphicsProxyWidget, the bar is lowered to entry and the time to create Applets is reduced. + +The API can be used both for scripting and from C++. For the simple ECMAScript API for instance, this API will be all that is provided allowing untrusted applets to be run with a great measure of safety as they will not have access to any dangerous facilities. + +From C++ or more advanced scripting APIs (such as the 'full' ECMAScript bindings) you can gain access to a pointer to the underlying Qt widget contained within the QGraphicsProxy via the nativeWidget() method, allowing access all of its methods. For most simple uses however, this should be unnecessary as you can do most of the customisation using the Qt stylesheet facilities. + +All widgets have a stylesheet, which is a string property containing a Qt stylesheet. This allows for simple but powerful configuration of the widgets - for example you can configure the alignment of the text in the label using the text-align property, or the font using the font property. Using this you can do some extremely advanced interfaces without having to learn how Qt works. + +A nice side effect of the way this API is defined is that it is possible to implement the simple widget API inside a web browser using HTML, javascript and HTML stylesheets to provide the implementation. + +Standardized API +---------------- + +Properties, setters and getters are defined for: + + * parent widget + * text + * image (supports SVG as well as pixmap) + * stylesheet + * native widget + +Widgets may add to this standardized API the most commonly used or important signals, slots and properties from the widget in question. Plasma::Checkbox, for instance, adds the toggled(bool) signal and properties for the checked state. These additions should be kept the essentials only to keep within the spirit of the simplicity and safety of the API; remember that the full widget is available via nativeWidget(). + +Look and Feel +------------- + +For most widgets, we let the QStyle do the painting. + +For certain widgets (e.g. Plasma::PushButton), the painting is overridden to blend in better with a Plasma based interface. This can be done without reimplemeting the entire native Qt widget by reimplementing the paint methods in either the QGraphicsWidget or a subclass of the native Qt Widget. Another approach is to use a custom QStyle, which is how Plasma::Slider gets its look with minimal code. + +Creating a New Widget +--------------------- + +Included with the libplasma sources is a make_widget.sh script and a set of templates in the widgets/ directory. Running it with the name of the widget to create (e.g. make_widget.sh CoolWidget) will create a base implementation of a widget using the standard API. From there, only the widget specific signals, slots and properties need be added. + diff --git a/desktoppackage/Messages.sh b/desktoppackage/Messages.sh new file mode 100644 index 0000000000..aef1b02337 --- /dev/null +++ b/desktoppackage/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$XGETTEXT `find . -name \*.qml` -L Java -o $podir/plasma_shell_org.kde.desktop.pot +rm -f rc.cpp diff --git a/desktoppackage/contents/activitymanager/ActivityBrowser.qml b/desktoppackage/contents/activitymanager/ActivityBrowser.qml new file mode 100644 index 0000000000..aa6b36265c --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityBrowser.qml @@ -0,0 +1,160 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.activities 0.1 as Activities + +Item { + id: root + width: 208 + + property int spacing: 2 * units.smallSpacing + + property bool showingDialog: + newActivityDialog.visible || + activityList.showingDialog + + Connections { + target: parent + onVisibleChanged: { + newActivityDialog.close(); + activityList.closeDialogs(); + } + } + + signal closeRequested() + + focus: true + + Keys.onPressed: { + if (newActivityDialog.visible || activityList.showingDialog) { + event.accepted = false; + } else { + if (event.key == Qt.Key_Escape) { + if (heading.showSearch) + heading.showSearch = false; + else + root.closeRequested(); + + } else if (event.key == Qt.Key_Up) { + console.log("UP KEY"); + + } else if (event.key == Qt.Key_Down) { + console.log("DOWN KEY"); + + } else if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + console.log("ENTER KEY"); + + } else if (event.key == Qt.Key_Tab) { + console.log("TAB KEY"); + + } else { + console.log("OTHER KEY"); + heading.showSearch = true; + + } + } + } + + // Rectangle { + // anchors.fill : parent + // opacity : .4 + // color : "white" + // } + + Heading { + id: heading + + anchors { + top: parent.top + left: parent.left + right: parent.right + + topMargin: units.largeSpacing + } + } + + PlasmaExtras.ScrollArea { + anchors { + top: heading.bottom + bottom: bottomPanel.top + left: parent.left + right: parent.right + topMargin: root.spacing + } + + flickableItem: ActivityList { + id: activityList + + filterString: heading.searchString.toLowerCase() + + onShowingDialogChanged: + if (showingDialog) newActivityDialog.close() + } + } + + Item { + id: bottomPanel + + height: newActivityButton.height + units.largeSpacing + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + PlasmaComponents.ToolButton { + id: newActivityButton + + text: "Create activity..." + iconSource: "list-add" + + width: parent.width + + onClicked: { + activityList.closeDialogs(); + newActivityDialog.open(newActivityDialog.height / 2); + } + + opacity: 1 - newActivityDialog.opacity + } + + ActivityCreationDialog { + id: newActivityDialog + + z: 100 + + anchors.bottom: newActivityButton.bottom + anchors.left: newActivityButton.left + anchors.right: newActivityButton.right + + onAccepted: { + activityList.model.addActivity(activityName, function (id) { + activityList.model.setActivityIcon(id, newActivityDialog.activityIconSource, function() {}); + }) + } + } + } +} + diff --git a/desktoppackage/contents/activitymanager/ActivityCreationDialog.qml b/desktoppackage/contents/activitymanager/ActivityCreationDialog.qml new file mode 100644 index 0000000000..fc5ff78572 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityCreationDialog.qml @@ -0,0 +1,197 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.platformcomponents 2.0 as PlasmaPlatformComponents +import org.kde.activities 0.1 as Activities + +PlasmaCore.FrameSvgItem { + id: root + + signal accepted() + signal canceled() + + imagePath: "dialogs/background" + + property alias activityName: activityNameText.text + property alias activityIconSource: activityIconButton.iconSource + + property alias acceptButtonText: acceptButton.text + property alias cancelButtonText: cancelButton.text + + property string activityId: "" + + MouseArea { + anchors.fill: parent + hoverEnabled: true + } + + function open(verticalPosition) + { + y = verticalPosition - height / 2; + opacity = 1; + } + + function close() + { + opacity = 0; + iconDialog.closeDialog(); + } + + opacity: 0 + visible: false + Behavior on opacity { PropertyAnimation { duration: units.shortDuration } } + Behavior on y { PropertyAnimation { + duration: root.visible ? units.shortDuration : 0 + } } + onOpacityChanged: visible = (opacity > 0) + + PlasmaCore.FrameSvgItem { + id: shadowFrame + + imagePath: "dialogs/background" + prefix: "shadow" + + z: -1 + + anchors { + fill: parent + + leftMargin : -margins.left + topMargin : -margins.top + rightMargin : -margins.right + bottomMargin : -margins.bottom + } + } + + height: content.height + margins.top + margins.bottom + dialogButtons.height + + Item { + id: content + + anchors { + left : parent.left + right : parent.right + top : parent.top + + topMargin : root.margins.top + leftMargin : root.margins.left + bottomMargin : root.margins.bottom + rightMargin : root.margins.right + } + + PlasmaComponents.Button { + id: activityIconButton + + anchors { + left: parent.left + top: parent.top + } + + width: height + height: activityNameLabel.height + activityNameText.height + + PlasmaPlatformComponents.IconDialog { + id: iconDialog + } + + onClicked: { + var newIcon = iconDialog.openDialog() + if (newIcon != "") { + activityIconButton.iconSource = newIcon; + } + } + + } + + height: activityNamePanel.height + + Column { + id: activityNamePanel + anchors { + left: activityIconButton.right + top: parent.top + right: parent.right + + leftMargin: units.largeSpacing + } + + PlasmaComponents.Label { + id: activityNameLabel + text: i18n("Activity name:") + + elide: Text.ElideRight + + width: parent.width + } + + PlasmaComponents.TextField { + id: activityNameText + + width: parent.width + + // text: "" + } + + Item { + height: units.largeSpacing + width: parent.width + } + } + } + + PlasmaComponents.ButtonRow { + id: dialogButtons + + anchors { + right: parent.right + bottom: parent.bottom + rightMargin: root.margins.right + bottomMargin: root.margins.bottom + } + + exclusive: false + + PlasmaComponents.Button { + id: acceptButton + + text: i18n("Create") + iconSource: "list-add" + onClicked: { + root.close(); + root.accepted(); + } + } + + PlasmaComponents.Button { + id: cancelButton + + text: i18n("Cancel") + iconSource: "dialog-close" + onClicked: { + root.close(); + root.canceled(); + } + } + } +} diff --git a/desktoppackage/contents/activitymanager/ActivityDeletionDialog.qml b/desktoppackage/contents/activitymanager/ActivityDeletionDialog.qml new file mode 100644 index 0000000000..42197face4 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityDeletionDialog.qml @@ -0,0 +1,131 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +PlasmaCore.FrameSvgItem { + id: root + + signal accepted() + signal canceled() + + imagePath: "dialogs/background" + + property string activityId: "" + + + + + + + + MouseArea { + anchors.fill: parent + hoverEnabled: true + } + + function open(verticalPosition) + { + y = verticalPosition - height / 2; + opacity = 1; + } + + function close() + { + opacity = 0; + } + + opacity: 0 + visible: false + Behavior on opacity { PropertyAnimation { duration: units.shortDuration } } + Behavior on y { PropertyAnimation { + duration: root.visible ? units.shortDuration : 0 + } } + onOpacityChanged: visible = (opacity > 0) + + PlasmaCore.FrameSvgItem { + id: shadowFrame + + imagePath: "dialogs/background" + prefix: "shadow" + + z: -1 + + anchors { + fill: parent + + leftMargin : -margins.left + topMargin : -margins.top + rightMargin : -margins.right + bottomMargin : -margins.bottom + } + } + + height: content.height + margins.top + margins.bottom + Column { + id: content + + anchors { + left: parent.left + right: parent.right + + topMargin: root.margins.top + leftMargin: root.margins.left + bottomMargin: root.margins.bottom + rightMargin: root.margins.right + } + + PlasmaComponents.Label { + text: i18n("Are you sure you want to delete the activity?") + width: parent.width + } + + Item { + height: units.largeSpacing + width: parent.width + } + + PlasmaComponents.ButtonRow { + exclusive: false + + PlasmaComponents.Button { + text: i18n("Delete") + iconSource: "list-remove" + onClicked: { + root.close(); + root.accepted(); + } + } + PlasmaComponents.Button { + text: i18n("Cancel") + iconSource: "dialog-close" + onClicked: { + root.close(); + root.canceled(); + } + } + + } + + } +} diff --git a/desktoppackage/contents/activitymanager/ActivityItem.qml b/desktoppackage/contents/activitymanager/ActivityItem.qml new file mode 100644 index 0000000000..6cb436ef40 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityItem.qml @@ -0,0 +1,186 @@ +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + id: root + + property int innerPadding : units.largeSpacing + + property bool current : false + property bool inFocus : false + + property alias title : title.text + property alias icon : icon.source + + property string background : "" + + onBackgroundChanged: if (background[0] != '#') { + // We have a proper wallpaper, hurroo! + backgroundWallpaper.source = background + backgroundColor.visible = false + + } else { + // We have only a color + backgroundColor.color = background + backgroundColor.visible = true + } + + signal clicked + signal configureClicked + signal stopClicked + + width : 200 + height : width * 1 / units.displayAspectRatio + + // Background until we get something real + PlasmaCore.FrameSvgItem { + id: highlight + imagePath: "widgets/viewitem" + visible: root.current || root.inFocus + + anchors { + fill: parent + rightMargin: -highlight.margins.right + bottomMargin: -highlight.margins.bottom + } + + prefix: + root.current ? "selected" : "normal" + + } + + Item { + anchors { + fill: parent + + leftMargin: highlight.margins.left + topMargin: highlight.margins.top + } + + // Background until we get something real + Rectangle { + id: backgroundColor + anchors.fill: parent + color: "black" + + opacity: .5 + } + + Image { + id: backgroundWallpaper + anchors.fill: parent + + visible: !backgroundColor.visible + } + + // Title and the icon + + Rectangle { + id: shade + + color : "black" + opacity : .5 + + height : title.height + 2 * title.anchors.margins + width : parent.width + + anchors { + bottom : parent.bottom + left : parent.left + right : parent.right + } + } + + PlasmaCore.IconItem { + id: icon + + width : units.iconSizes.medium + height : width + + anchors { + bottom : parent.bottom + left : parent.left + margins : root.innerPadding + } + } + + PlasmaComponents.Label { + id: title + + color : "white" + elide : Text.ElideRight + + anchors { + bottom : parent.bottom + left : icon.right + right : parent.right + margins : root.innerPadding * 2 + } + } + + // Controls + + MouseArea { + id: rootArea + + anchors.fill : parent + hoverEnabled : true + + onClicked : root.clicked() + } + + ControlButton { + id: stopButton + + onClicked: root.stopClicked() + + icon: "close" + + anchors { + right : parent.right + top : parent.top + margins : root.innerPadding + } + } + + ControlButton { + id: configButton + + onClicked: root.configureClicked() + + icon: "configure" + + anchors { + left : parent.left + top : parent.top + margins : root.innerPadding + } + } + + states: [ + State { + name: "plain" + PropertyChanges { target: stopButton; opacity: 0 } + PropertyChanges { target: configButton; opacity: 0 } + }, + State { + name: "showControls" + PropertyChanges { target: stopButton; opacity: 1 } + PropertyChanges { target: configButton; opacity: 1 } + } + ] + + transitions: [ + Transition { + NumberAnimation { + properties : "opacity" + duration : units.shortDuration + } + } + ] + + state: rootArea.containsMouse ? "showControls" : "plain" + } +} diff --git a/desktoppackage/contents/activitymanager/ActivityList.qml b/desktoppackage/contents/activitymanager/ActivityList.qml new file mode 100644 index 0000000000..8871647b53 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityList.qml @@ -0,0 +1,199 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.activities 0.1 as Activities + + +Flickable { + id: root + + /*contentWidth: content.width*/ + contentHeight: content.height + + property var model: activitiesModel + property string filterString: "" + property bool showingDialog: activityDeletionDialog.visible || activityConfigurationDialog.visible + + function closeDialogs() + { + activityDeletionDialog.close(); + activityConfigurationDialog.close(); + } + + Activities.ActivityModel { + id: activitiesModel + + shownStates: "Running,Stopping" + } + + Activities.ActivityModel { + id: stoppedActivitiesModel + + shownStates: "Stopped,Starting" + } + + ActivityDeletionDialog { + id: activityDeletionDialog + z: 10 + + width: parent.width + + onAccepted: { + activitiesModel.removeActivity(activityDeletionDialog.activityId, function () {}) + } + } + + ActivityCreationDialog { + id: activityConfigurationDialog + z: 10 + + acceptButtonText: i18n("Apply") + + width: parent.width + + onAccepted: { + var id = activityConfigurationDialog.activityId + activitiesModel.setActivityName(id, + activityConfigurationDialog.activityName, + function () {}); + activitiesModel.setActivityIcon(id, + activityConfigurationDialog.activityIconSource, + function () {}); + } + } + + Column { + id: content + + width: parent.width + + // Running activities + + Repeater { + model: activitiesModel + + ActivityItem { + + width: parent.width + + visible : (root.filterString == "") || + (title.toLowerCase().indexOf(root.filterString) != -1) + + title : model.name + icon : model.iconSource + background : model.background + current : model.current + innerPadding : 2 * units.smallSpacing + + onClicked : { + activitiesModel.setCurrentActivity(model.id, function () {}) + } + + onStopClicked : { + activitiesModel.stopActivity(model.id, function () {}); + } + + onConfigureClicked : { + activityConfigurationDialog.activityId = model.id; + activityConfigurationDialog.activityName = title; + activityConfigurationDialog.activityIconSource = icon; + + var selfLocation = mapToItem(root, width / 2, height / 2); + activityConfigurationDialog.open(selfLocation.y); + activityDeletionDialog.close(); + } + } + } + + // Stopped activities + + Item { + // spacer + width : parent.width + height : units.largeSpacing + } + + PlasmaExtras.Heading { + text: "Stopped activities:" + level: 3 + visible: stoppedActivitiesList.count > 0 + } + + Repeater { + id: stoppedActivitiesList + + model: stoppedActivitiesModel + + delegate: StoppedActivityItem { + id: stoppedActivityItem + + width: parent.width + + visible : (root.filterString == "") || + (title.toLowerCase().indexOf(root.filterString) != -1) + + title : model.name + icon : model.iconSource + innerPadding : 2 * units.smallSpacing + + onClicked : { + stoppedActivitiesModel.setCurrentActivity(model.id, function () {}) + } + + onDeleteClicked : { + activityDeletionDialog.activityId = model.id; + + var selfLocation = mapToItem(root, width / 2, height / 2); + activityDeletionDialog.open(selfLocation.y); + activityConfigurationDialog.close(); + } + + onConfigureClicked : { + activityConfigurationDialog.activityId = model.id; + activityConfigurationDialog.activityName = title; + activityConfigurationDialog.activityIconSource = icon; + + var selfLocation = mapToItem(root, width / 2, height / 2); + activityConfigurationDialog.open(selfLocation.y); + activityDeletionDialog.close(); + } + } + } + + add: Transition { + NumberAnimation { + properties: "x" + from: -100 + duration: units.shortDuration + } + } + + move: Transition { + NumberAnimation { + id: animation + properties: "y" + duration: units.longDuration + } + } + } +} + diff --git a/desktoppackage/contents/activitymanager/ActivityManager.qml b/desktoppackage/contents/activitymanager/ActivityManager.qml new file mode 100644 index 0000000000..ca10713735 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ActivityManager.qml @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Marco Martin + * Copyright 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.activities 0.1 as Activities + +Item { + id: main + signal closed() + + function parentClosed() { + activityBrowser.parentClosed(); + } + + //this is used to perfectly align the filter field and delegates + property int cellWidth: theme.mSize(theme.defaultFont).width * 30 + property int spacing: 2 * units.smallSpacing + + property int minimumWidth: cellWidth + 4 * 2 + property int minimumHeight: 0 + + property bool showingDialog: activityBrowser.showingDialog + + width: minimumWidth + + ActivityBrowser { + id: activityBrowser + + onCloseRequested: main.closed() + + anchors.fill: parent + } +} + diff --git a/desktoppackage/contents/activitymanager/ControlButton.qml b/desktoppackage/contents/activitymanager/ControlButton.qml new file mode 100644 index 0000000000..1e9854f5a1 --- /dev/null +++ b/desktoppackage/contents/activitymanager/ControlButton.qml @@ -0,0 +1,59 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.SvgItem { + id: root + + signal clicked + + property alias icon: innerIcon.elementId + + width : units.iconSizes.smallMedium + height : units.iconSizes.smallMedium + /*flat : false*/ + + svg: PlasmaCore.Svg { imagePath: "widgets/actionbutton" } + elementId : mouseArea.pressed ? "pressed" : "normal" + + PlasmaCore.SvgItem { + id: innerIcon + + anchors.centerIn: parent + + // TODO: Ugly X + width : units.iconSizes.small - (root.icon == "close" ? 4 : 0) + height : width + + svg: PlasmaCore.Svg { imagePath: "widgets/configuration-icons" } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + + onClicked: root.clicked() + } +} + + diff --git a/desktoppackage/contents/activitymanager/Heading.qml b/desktoppackage/contents/activitymanager/Heading.qml new file mode 100644 index 0000000000..ea0560ecb1 --- /dev/null +++ b/desktoppackage/contents/activitymanager/Heading.qml @@ -0,0 +1,118 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + id: root + + property alias showSearch : searchButton.checked + property alias searchString : searchText.text + + Keys.onPressed: { + if (event.key == Qt.Key_Escape) { + if (root.showSearch) { + event.accepted = true; + searchButton.checked = false; + } + } + } + + onShowSearchChanged: if (showSearch) searchText.focus = true + + height : heading.height + width : parent.width + + PlasmaComponents.ToolButton { + id: searchButton + iconSource: "edit-find" + + checkable: true + checked: root.showSearch + + anchors { + right : parent.right + top : parent.top + bottom : parent.bottom + } + + onCheckedChanged: { + if (!checked) searchText.text = ""; + } + } + + PlasmaExtras.Title { + id: heading + text: "Activities" + + anchors { + left : parent.left + right : searchButton.left + bottom : parent.bottom + top : parent.top + } + } + + PlasmaComponents.TextField { + id: searchText + + focus: true + + anchors { + left : parent.left + right : searchButton.left + bottom : parent.bottom + top : parent.top + } + + onTextChanged: { + if (text.length > 0) { + root.showSearch = true + } + } + } + + states: [ + State { + name: "normal" + PropertyChanges { target: searchText; opacity: 0 } + PropertyChanges { target: heading; opacity: 1 } + }, + State { + name: "search" + PropertyChanges { target: searchText; opacity: 1 } + PropertyChanges { target: heading; opacity: 0 } + } + ] + + transitions: [ + Transition { + NumberAnimation { + properties : "opacity" + duration : units.shortDuration + } + } + ] + + state: showSearch ? "search" : "normal" +} diff --git a/desktoppackage/contents/activitymanager/StoppedActivityItem.qml b/desktoppackage/contents/activitymanager/StoppedActivityItem.qml new file mode 100644 index 0000000000..c5dcb980d6 --- /dev/null +++ b/desktoppackage/contents/activitymanager/StoppedActivityItem.qml @@ -0,0 +1,158 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + id: root + + property int innerPadding: units.smallSpacing + + property alias title : title.text + property alias icon : icon.source + + /*signal clicked*/ + signal configureClicked + signal deleteClicked + signal clicked + + width : 200 + height : icon.height + innerPadding * 2 + + // Background until we get something real + PlasmaCore.FrameSvgItem { + id: highlight + imagePath: "widgets/viewitem" + visible: rootArea.containsMouse + + anchors { + fill: parent + rightMargin: -highlight.margins.right + bottomMargin: -highlight.margins.bottom + } + + prefix: "normal" + } + + Item { + anchors { + fill: parent + + leftMargin: highlight.margins.left + topMargin: highlight.margins.top + } + + // Title and the icon + + PlasmaCore.IconItem { + id: icon + + width : units.iconSizes.medium + height : width + + anchors { + left : parent.left + margins : root.innerPadding + verticalCenter: parent.verticalCenter + } + } + + PlasmaComponents.Label { + id: title + + elide : Text.ElideRight + + anchors { + left : icon.right + right : parent.right + margins : root.innerPadding * 2 + verticalCenter: parent.verticalCenter + } + } + + // Controls + + MouseArea { + id: rootArea + + anchors.fill : parent + hoverEnabled : true + + onClicked : root.clicked() + } + + ControlButton { + id: deleteButton + + onClicked: root.deleteClicked() + + icon: "delete" + + anchors { + right : parent.right + top : parent.top + margins : root.innerPadding + } + } + + ControlButton { + id: configButton + + onClicked: root.configureClicked() + + icon: "configure" + + anchors { + left : parent.left + top : parent.top + margins : root.innerPadding + } + } + + states: [ + State { + name: "plain" + PropertyChanges { target: deleteButton; opacity: 0 } + PropertyChanges { target: configButton; opacity: 0 } + }, + State { + name: "showControls" + PropertyChanges { target: deleteButton; opacity: 1 } + PropertyChanges { target: configButton; opacity: 1 } + } + ] + + transitions: [ + Transition { + NumberAnimation { + properties : "opacity" + duration : units.shortDuration + } + } + ] + + state: rootArea.containsMouse ? "showControls" : "plain" + } +} + + diff --git a/desktoppackage/contents/activitymanager/WindowPreview.qml b/desktoppackage/contents/activitymanager/WindowPreview.qml new file mode 100644 index 0000000000..295a1637dc --- /dev/null +++ b/desktoppackage/contents/activitymanager/WindowPreview.qml @@ -0,0 +1,75 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2014 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 + +Image { + id: main + + width: 480 + height: 425 + + source: "images/window2.png" + anchors.centerIn: parent + + Item { + id: title + + + width: titleText.width + 32 + height: titleText.height + 32 + + Rectangle { + anchors.fill: parent + color: "black" + radius: 8 + opacity: .7 + } + + Text { + id: titleText + color: "white" + text: "Firefox" + font.pointSize: 24 + + anchors.centerIn: parent + } + + + + } + + Drag.active: mouseArea.drag.active + Drag.hotSpot.x: 32 + Drag.hotSpot.y: 32 + + MouseArea { + id: moudeArea + anchors.fill: parent + + drag { + target: title + } + + onClicked: console.log("Clicked") + } +} + + diff --git a/desktoppackage/contents/applet/AppletError.qml b/desktoppackage/contents/applet/AppletError.qml new file mode 100644 index 0000000000..790b57c7a6 --- /dev/null +++ b/desktoppackage/contents/applet/AppletError.qml @@ -0,0 +1,45 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Row { + id: root + + property alias reason: messageText.text + + clip: true + + PlasmaCore.IconItem { + id: icon + anchors.verticalCenter: parent.verticalCenter + width: units.iconSizes.huge + height: width + source: "dialog-error" + } + + PlasmaComponents.Label { + id: messageText + anchors.verticalCenter: parent.verticalCenter + width: parent.width - icon.width + wrapMode: Text.Wrap + } +} diff --git a/desktoppackage/contents/applet/CompactApplet.qml b/desktoppackage/contents/applet/CompactApplet.qml new file mode 100644 index 0000000000..5a15bdda1b --- /dev/null +++ b/desktoppackage/contents/applet/CompactApplet.qml @@ -0,0 +1,134 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +PlasmaCore.ToolTipArea { + id: root + objectName: "org.kde.desktop-CompactApplet" + anchors.fill: parent + + icon: plasmoid.icon + mainText: plasmoid.toolTipMainText + subText: plasmoid.toolTipSubText + location: plasmoid.location + active: !plasmoid.expanded + property Item fullRepresentation + property Item compactRepresentation + property Item expandedFeedback: expandedItem + + onCompactRepresentationChanged: { + compactRepresentation.parent = root; + compactRepresentation.anchors.fill = root; + compactRepresentation.visible = true; + root.visible = true; + } + + onFullRepresentationChanged: { + + //if the fullRepresentation size was restored to a stored size, or if is dragged from the desktop, restore popup size + if (fullRepresentation.width > 0) { + popupWindow.mainItem.width = fullRepresentation.width; + } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredWidth > 0) { + popupWindow.mainItem.width = fullRepresentation.Layout.preferredWidth + } else if (fullRepresentation.implicitWidth > 0) { + popupWindow.mainItem.width = fullRepresentation.implicitWidth + } else { + popupWindow.mainItem.width = theme.mSize(theme.defaultFont).width * 35 + } + + if (fullRepresentation.height > 0) { + popupWindow.mainItem.height = fullRepresentation.height; + } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredHeight > 0) { + popupWindow.mainItem.height = fullRepresentation.Layout.preferredHeight + } else if (fullRepresentation.implicitHeight > 0) { + popupWindow.mainItem.height = fullRepresentation.implicitHeight + } else { + popupWindow.mainItem.width = theme.mSize(theme.defaultFont).height * 25 + } + + fullRepresentation.parent = appletParent; + fullRepresentation.anchors.fill = fullRepresentation.parent; + } + + PlasmaCore.FrameSvgItem { + id: expandedItem + anchors.fill: parent + imagePath: "widgets/tabbar" + prefix: { + var prefix; + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + prefix = "west-active-tab"; + break; + case PlasmaCore.Types.TopEdge: + prefix = "north-active-tab"; + break; + case PlasmaCore.Types.RightEdge: + prefix = "east-active-tab"; + break; + default: + prefix = "south-active-tab"; + } + if (!hasElementPrefix(prefix)) { + prefix = "active-tab"; + } + return prefix; + } + opacity: plasmoid.expanded ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: theme.shortDuration + easing: Easing.InOutQuad + } + } + } + + PlasmaCore.Dialog { + id: popupWindow + objectName: "popupWindow" + flags: Qt.WindowStaysOnTopHint + visible: plasmoid.expanded && fullRepresentation + visualParent: compactRepresentation ? compactRepresentation : null + location: plasmoid.location + hideOnWindowDeactivate: plasmoid.hideOnWindowDeactivate + + mainItem: Item { + id: appletParent + Layout.minimumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumWidth : 0 + Layout.minimumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumHeight: 0 + Layout.maximumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumWidth : Infinity + Layout.maximumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumHeight: Infinity + } + + onVisibleChanged: { + if (!visible) { + plasmoid.expanded = false + } else { + // This call currently fails and complains at runtime: + // QWindow::setWindowState: QWindow::setWindowState does not accept Qt::WindowActive + popupWindow.requestActivate(); + } + } + + } +} diff --git a/desktoppackage/contents/applet/DefaultCompactRepresentation.qml b/desktoppackage/contents/applet/DefaultCompactRepresentation.qml new file mode 100644 index 0000000000..ecfb5f880b --- /dev/null +++ b/desktoppackage/contents/applet/DefaultCompactRepresentation.qml @@ -0,0 +1,36 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.IconItem { + id: icon + + source: plasmoid.icon ? plasmoid.icon : "plasma" + active: mouseArea.containsMouse + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + onClicked: plasmoid.expanded = !plasmoid.expanded + } +} \ No newline at end of file diff --git a/desktoppackage/contents/configuration/AppletConfiguration.qml b/desktoppackage/contents/configuration/AppletConfiguration.qml new file mode 100644 index 0000000000..cf8b7862f4 --- /dev/null +++ b/desktoppackage/contents/configuration/AppletConfiguration.qml @@ -0,0 +1,262 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 +import org.kde.plasma.configuration 2.0 + + +//TODO: all of this will be done with desktop components +Rectangle { + id: root + +//BEGIN properties + color: syspal.window + width: units.gridUnit * 40 + height: units.gridUnit * 30 +//END properties + +//BEGIN model + property ConfigModel globalConfigModel: globalAppletConfigModel + + ConfigModel { + id: globalAppletConfigModel + ConfigCategory { + name: "Keyboard shortcuts" + icon: "preferences-desktop-keyboard" + source: "ConfigurationShortcuts.qml" + } + } +//END model + +//BEGIN functions + function saveConfig() { + if (main.currentItem.saveConfig) { + main.currentItem.saveConfig() + } else { + for (var key in plasmoid.configuration) { + if (main.currentItem["cfg_"+key] !== undefined) { + plasmoid.configuration[key] = main.currentItem["cfg_"+key] + } + } + } + } + + function restoreConfig() { + for (var key in plasmoid.configuration) { + if (main.currentItem["cfg_"+key] !== undefined) { + main.currentItem["cfg_"+key] = plasmoid.configuration[key] + } + } + } +//END functions + + +//BEGIN connections + Component.onCompleted: { + if (configDialog.configModel && configDialog.configModel.count > 0) { + main.sourceFile = configDialog.configModel.get(0).source + main.title = configDialog.configModel.get(0).name + } else { + main.sourceFile = globalConfigModel.get(0).source + main.title = globalConfigModel.get(0).name + } + root.restoreConfig() +// root.width = mainColumn.implicitWidth +// root.height = mainColumn.implicitHeight + } +//END connections + +//BEGIN UI components + SystemPalette {id: syspal} + + ColumnLayout { + id: mainColumn + anchors { + fill: parent + margins: mainColumn.spacing //margins are hardcoded in QStyle we should match that here + } + property int implicitWidth: Math.max(contentRow.implicitWidth, buttonsRow.implicitWidth) + 8 + property int implicitHeight: contentRow.implicitHeight + buttonsRow.implicitHeight + 8 + + RowLayout { + id: contentRow + anchors { + left: parent.left + right: parent.right + } + spacing: units.largeSpacing + Layout.fillHeight: true + Layout.preferredHeight: parent.height - buttonsRow.height + + QtControls.ScrollView { + id: categoriesScroll + frameVisible: true + anchors { + top: parent.top + bottom: parent.bottom + } + visible: (configDialog.configModel ? configDialog.configModel.count : 0) + globalConfigModel.count > 1 + width: visible ? 100 : 0 + implicitWidth: width + Flickable { + id: categoriesView + contentWidth: width + contentHeight: childrenRect.height + anchors.fill: parent + + property Item currentItem: categoriesColumn.children[1] + + Item { + id: categories + width: parent.width + height: categoriesColumn.height + + Item { + width: parent.width + height: categoriesView.currentItem.height + y: categoriesView.currentItem.y + Rectangle { + color: syspal.highlight + radius: 3 + anchors { + fill: parent + margins: 2 + } + } + Behavior on y { + NumberAnimation { + duration: units.longDuration + easing.type: "InOutQuad" + } + } + } + Column { + id: categoriesColumn + width: parent.width + Repeater { + model: configDialog.configModel + delegate: ConfigCategoryDelegate { + onClicked: { + categoriesView.currentIndex = index + pageTitle.text = name; + } + + } + } + Repeater { + model: globalConfigModel + delegate: ConfigCategoryDelegate {} + } + } + } + } + } + Item { + anchors { + top: parent.top + bottom: parent.bottom + } + Layout.fillWidth: true + height: Math.max(categoriesScroll.height, main.currentItem != null ? main.currentItem.implicitHeight : 0) + + QtControls.Label { + id: pageTitle + anchors { + top: parent.top + left: parent.left + right: parent.right + } + font.pointSize: theme.defaultFont.pointSize*2 + text: main.title + } + + QtControls.StackView { + id: main + property string title: "" + anchors { + top: pageTitle.bottom + topMargin: units.largeSpacing / 2 + left: parent.left + right: parent.right + bottom: parent.bottom + } + clip: true + property string sourceFile + Timer { + id: pageSizeSync + interval: 100 + onTriggered: { + // root.width = mainColumn.implicitWidth + // root.height = mainColumn.implicitHeight + } + } + onImplicitWidthChanged: pageSizeSync.restart() + onImplicitHeightChanged: pageSizeSync.restart() + onSourceFileChanged: { + print("Source file changed in flickable" + sourceFile); + replace(Qt.resolvedUrl(sourceFile)) + /* + * This is not needed on a desktop shell that has ok/apply/cancel buttons, i'll leave it here only for future reference until we have a prototype for the active shell. + * root.pageChanged will start a timer, that in turn will call saveConfig() when triggered + + for (var prop in currentPage) { + if (prop.indexOf("cfg_") === 0) { + currentPage[prop+"Changed"].connect(root.pageChanged) + } + }*/ + } + } + } + } + RowLayout { + id: buttonsRow + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + QtControls.Button { + iconName: "dialog-ok" + text: i18n("Ok") + onClicked: { + if (main.currentItem.saveConfig !== undefined) { + main.currentItem.saveConfig(); + } else { + root.saveConfig(); + } + configDialog.close(); + } + } + QtControls.Button { + iconName: "dialog-ok-apply" + text: i18n("Apply") + onClicked: { + if (main.currentItem.saveConfig !== undefined) { + main.currentItem.saveConfig(); + } else { + root.saveConfig(); + } + } + } + QtControls.Button { + iconName: "dialog-cancel" + text: i18n("Cancel") + onClicked: configDialog.close() + } + } + } +//END UI components +} diff --git a/desktoppackage/contents/configuration/ConfigCategoryDelegate.qml b/desktoppackage/contents/configuration/ConfigCategoryDelegate.qml new file mode 100644 index 0000000000..76dd30d923 --- /dev/null +++ b/desktoppackage/contents/configuration/ConfigCategoryDelegate.qml @@ -0,0 +1,85 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import org.kde.plasma.core 2.0 as PlasmaCore + +MouseArea { + id: delegate + + anchors { + left: parent.left + right: parent.right + } +//BEGIN properties + width: childrenRect.width + height: childrenRect.height + 4 + property bool current: model.source == main.sourceFile + property string name: model.name +//END properties + +//BEGIN connections + onClicked: { + print("model source: " + model.source + " " + main.sourceFile); + if (delegate.current) { + return + } else { + if (typeof(categoriesView.currentItem) !== "undefined") { + categoriesView.currentItem = delegate; + } + main.sourceFile = model.source + main.title = model.name + root.restoreConfig() + } + } + onCurrentChanged: { + if (current) { + categoriesView.currentItem = delegate; + } + } +//END connections + +//BEGIN UI components + Column { + spacing: 4 + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + topMargin: _m + } + PlasmaCore.IconItem { + anchors.horizontalCenter: parent.horizontalCenter + width: theme.IconSizeHuge + height: width + source: model.icon + } + QtControls.Label { + anchors { + left: parent.left + right: parent.right + } + text: model.name + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + color: current ? syspal.highlightedText : syspal.text + } + } +//END UI components +} diff --git a/desktoppackage/contents/configuration/ConfigurationContainmentActions.qml b/desktoppackage/contents/configuration/ConfigurationContainmentActions.qml new file mode 100644 index 0000000000..9985356fd1 --- /dev/null +++ b/desktoppackage/contents/configuration/ConfigurationContainmentActions.qml @@ -0,0 +1,119 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 + +Item { + id: root + + implicitWidth: childrenRect.width + implicitHeight: childrenRect.height + + property var prettyStrings: { + "LeftButton": i18n("Left-Button"), + "RightButton": i18n("Right-Button"), + "MidButton": i18n("Middle-Button"), + + "wheel:Vertical": i18n("Vertical-Scroll"), + "wheel:Horizontal": i18n("Horizontal-Scroll"), + + "ShiftModifier": i18n("Shift"), + "ControlModifier": i18n("Ctrl"), + "AltModifier": i18n("Alt"), + "MetaModifier": i18n("Meta") + } + + function saveConfig() { + configDialog.currentContainmentActionsModel.save(); + } + + Column { + anchors { + top: parent.top + topMargin: 25 + horizontalCenter: parent.horizontalCenter + } + + Repeater { + model: configDialog.currentContainmentActionsModel + delegate: RowLayout { + width: root.width + MouseEventInputButton { + defaultText: prettyStrings ? (prettyStrings[model.action.split(';')[1]] ? prettyStrings[model.action.split(';')[1]] + "+" : "") + prettyStrings[model.action.split(';')[0]] : "" + eventString: model.action + onEventStringChanged: { + configDialog.currentContainmentActionsModel.update(index, eventString, model.pluginName); + } + } + + QtControls.ComboBox { + id: pluginsCombo + Layout.fillWidth: true + model: configDialog.containmentActionConfigModel + textRole: "name" + property bool initialized: false + Component.onCompleted: { + for (var i = 0; i < configDialog.containmentActionConfigModel.count; ++i) { + if (configDialog.containmentActionConfigModel.get(i).pluginName == pluginName) { + pluginsCombo.currentIndex = i; + break; + } + } + pluginsCombo.initialized = true; + } + onCurrentIndexChanged: { + if (initialized && configDialog.containmentActionConfigModel.get(currentIndex).pluginName != pluginName) { + configDialog.currentContainmentActionsModel.update(index, action, configDialog.containmentActionConfigModel.get(currentIndex).pluginName); + } + } + } + QtControls.Button { + iconName: "configure" + width: height + enabled: model.hasConfigurationInterface + onClicked: { + configDialog.currentContainmentActionsModel.showConfiguration(index); + } + } + QtControls.Button { + iconName: "dialog-information" + width: height + onClicked: { + configDialog.currentContainmentActionsModel.showAbout(index); + } + } + QtControls.Button { + iconName: "list-remove" + width: height + onClicked: { + configDialog.currentContainmentActionsModel.remove(index); + } + } + } + } + MouseEventInputButton { + defaultText: i18n("Add Action"); + onEventStringChanged: { + configDialog.currentContainmentActionsModel.append(eventString, "org.kde.contextmenu"); + } + } + } + +} diff --git a/desktoppackage/contents/configuration/ConfigurationContainmentAppearance.qml b/desktoppackage/contents/configuration/ConfigurationContainmentAppearance.qml new file mode 100644 index 0000000000..09a0c942f7 --- /dev/null +++ b/desktoppackage/contents/configuration/ConfigurationContainmentAppearance.qml @@ -0,0 +1,106 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.configuration 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 + +ColumnLayout { + id: root + + property int formAlignment: pluginComboBox.x + (units.largeSpacing/2) + property string currentWallpaper: "" + +//BEGIN functions + function saveConfig() { + for (var key in configDialog.wallpaperConfiguration) { + if (main.currentItem["cfg_"+key] !== undefined) { + configDialog.wallpaperConfiguration[key] = main.currentItem["cfg_"+key] + } + } + configDialog.currentWallpaper = root.currentWallpaper; + configDialog.applyWallpaper() + } + + function restoreConfig() { + for (var key in configDialog.wallpaperConfiguration) { + if (main.currentItem["cfg_"+key] !== undefined) { + main.currentItem["cfg_"+key] = configDialog.wallpaperConfiguration[key] + } + } + } +//END functions + + Component.onCompleted: { + for (var i = 0; i < configDialog.wallpaperConfigModel.count; ++i) { + var data = configDialog.wallpaperConfigModel.get(i); + for(var j in data) print(j) + if (configDialog.currentWallpaper == data.pluginName) { + pluginComboBox.currentIndex = i + break; + } + } + } + + Row { + spacing: units.largeSpacing / 2 + Item { + width: units.largeSpacing + height: parent.height + } + QtControls.Label { + anchors.verticalCenter: pluginComboBox.verticalCenter + text: i18n("Wallpaper Type:") + } + QtControls.ComboBox { + id: pluginComboBox + model: configDialog.wallpaperConfigModel + width: theme.mSize(theme.defaultFont).width * 24 + textRole: "name" + onCurrentIndexChanged: { + var model = configDialog.wallpaperConfigModel.get(currentIndex) + root.currentWallpaper = model.pluginName + main.sourceFile = model.source + configDialog.currentWallpaper = model.pluginName + root.restoreConfig() + } + } + } + + Item { + id: emptyConfig + } + + QtControls.StackView { + id: main + Layout.fillHeight: true; + anchors { + left: parent.left; + right: parent.right; + } + property string sourceFile + onSourceFileChanged: { + if (sourceFile != "") { + replace(Qt.resolvedUrl(sourceFile)) + } else { + replace(emptyConfig); + } + } + } +} diff --git a/desktoppackage/contents/configuration/ConfigurationShortcuts.qml b/desktoppackage/contents/configuration/ConfigurationShortcuts.qml new file mode 100644 index 0000000000..4f004dc47a --- /dev/null +++ b/desktoppackage/contents/configuration/ConfigurationShortcuts.qml @@ -0,0 +1,33 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.kquickcontrols 2.0 + +Item { + width: childrenRect.width + height: childrenRect.height + + KeySequenceItem { + id: button + keySequence: plasmoid.globalShortcut + onKeySequenceChanged: { + plasmoid.globalShortcut = button.keySequence + } + } +} diff --git a/desktoppackage/contents/configuration/ContainmentConfiguration.qml b/desktoppackage/contents/configuration/ContainmentConfiguration.qml new file mode 100644 index 0000000000..af3903dadc --- /dev/null +++ b/desktoppackage/contents/configuration/ContainmentConfiguration.qml @@ -0,0 +1,47 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 + + +AppletConfiguration { + id: root + +//BEGIN model + globalConfigModel: globalContainmentConfigModel + + ConfigModel { + id: globalContainmentConfigModel + ConfigCategory { + name: "Wallpaper" + icon: "preferences-desktop-wallpaper" + source: "ConfigurationContainmentAppearance.qml" + } + ConfigCategory { + name: "Mouse Actions" + icon: "preferences-desktop-mouse" + source: "ConfigurationContainmentActions.qml" + } + } +//END model + +} diff --git a/desktoppackage/contents/configuration/MouseEventInputButton.qml b/desktoppackage/contents/configuration/MouseEventInputButton.qml new file mode 100644 index 0000000000..61b5c019c0 --- /dev/null +++ b/desktoppackage/contents/configuration/MouseEventInputButton.qml @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 as QtControls +import QtQuick.Layouts 1.0 +import org.kde.plasma.core 2.0 as PlasmaCore + + +QtControls.Button { + id: mouseInputButton + property string defaultText: i18n("Add Action") + text: defaultText + checkable: true + property string eventString + + implicitWidth: theme.mSize(theme.defaultFont).width * 15 + Layout.minimumWidth: implicitWidth + Layout.maximumWidth: implicitWidth + + onCheckedChanged: { + if (checked) { + text = i18n("Input Here"); + mouseInputArea.enabled = true; + } + } + MouseArea { + id: mouseInputArea + anchors.fill: parent + acceptedButtons: Qt.AllButtons + enabled: false + + onClicked: { + var newEventString = configDialog.currentContainmentActionsModel.mouseEventString(mouse.button, mouse.modifiers); + + if (eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) { + eventString = newEventString; + mouseInputButton.text = defaultText; + mouseInputButton.checked = false; + enabled = false; + } + } + + onWheel: { + var newEventString = configDialog.currentContainmentActionsModel.wheelEventString(wheel.pixelDelta, wheel.buttons, wheel.modifiers); + + if (eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) { + eventString = newEventString; + mouseInputButton.text = defaultText; + mouseInputButton.checked = false; + enabled = false; + } + } + } +} + diff --git a/desktoppackage/contents/configuration/PanelConfiguration.qml b/desktoppackage/contents/configuration/PanelConfiguration.qml new file mode 100644 index 0000000000..cf532d7db7 --- /dev/null +++ b/desktoppackage/contents/configuration/PanelConfiguration.qml @@ -0,0 +1,172 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons +import "panelconfiguration" + + +//TODO: all of this will be done with desktop components +PlasmaCore.FrameSvgItem { + id: dialogRoot + +//BEGIN Properties + width: 640 + height: 64 + imagePath: "dialogs/background" + + state: { + switch (panel.location) { + //TopEdge + case PlasmaCore.Types.TopEdge: + return "TopEdge" + //LeftEdge + case PlasmaCore.Types.LeftEdge: + return "LeftEdge" + //RightEdge + case PlasmaCore.Types.RightEdge: + return "RightEdge" + //BottomEdge + case PlasmaCore.Types.BottomEdge: + default: + return "BottomEdge" + } + } + + property bool vertical: (panel.location == PlasmaCore.Types.LeftEdge || panel.location == PlasmaCore.Types.RightEdge) +//END properties + +//BEGIN Connections + Connections { + target: panel + onOffsetChanged: ruler.offset = panel.offset + onMinimumLengthChanged: ruler.minimumLength = panel.minimumLength + onMaximumLengthChanged: ruler.maximumLength = panel.maximumLength + } +//END Connections + + +//BEGIN UI components + + Ruler { + id: ruler + state: dialogRoot.state + } + + ToolBar { + id: toolBar + } +//END UI components + +//BEGIN Animations + //when EdgeHandle is released animate to old panel position + ParallelAnimation { + id: panelResetAnimation + NumberAnimation { + target: panel + properties: (panel.location == PlasmaCore.Types.LeftEdge || panel.location == PlasmaCore.Types.RightEdge) ? "x" : "y" + to: { + switch (panel.location) { + case PlasmaCore.Types.TopEdge: + return panel.screenGeometry.y + panel.distance + case PlasmaCore.Types.LeftEdge: + return panel.screenGeometry.x + panel.distance + case PlasmaCore.Types.RightEdge: + return panel.screenGeometry.x + panel.screenGeometry.width - panel.width - panel.distance + case PlasmaCore.Types.BottomEdge: + default: + return panel.screenGeometry.y + panel.screenGeometry.height - panel.height - panel.distance + } + } + duration: units.shortDuration * 3 + } + + NumberAnimation { + target: configDialog + properties: (panel.location == PlasmaCore.Types.LeftEdge || panel.location == PlasmaCore.Types.RightEdge) ? "x" : "y" + to: { + switch (panel.location) { + case PlasmaCore.Types.TopEdge: + return panel.screenGeometry.y + panel.height + panel.distance + case PlasmaCore.Types.LeftEdge: + return panel.screenGeometry.x + panel.width + panel.distance + case PlasmaCore.Types.RightEdge: + return panel.screenGeometry.x + panel.screenGeometry.width - panel.width - configDialog.width - panel.distance + case PlasmaCore.Types.BottomEdge: + default: + return panel.screenGeometry.y + panel.screenGeometry.height - panel.height - configDialog.height - panel.distance + } + } + duration: units.shortDuration * 3 + } + } +//END Animations + +//BEGIN States +states: [ + State { + name: "TopEdge" + PropertyChanges { + target: dialogRoot + enabledBorders: "TopBorder|BottomBorder" + } + PropertyChanges { + target: dialogRoot + implicitHeight: ruler.implicitHeight + toolBar.implicitHeight + } + }, + State { + name: "BottomEdge" + PropertyChanges { + target: dialogRoot + enabledBorders: "TopBorder|BottomBorder" + } + PropertyChanges { + target: dialogRoot + implicitHeight: ruler.implicitHeight + toolBar.implicitHeight + } + }, + State { + name: "LeftEdge" + PropertyChanges { + target: dialogRoot + enabledBorders: "LeftBorder|RightBorder" + } + PropertyChanges { + target: dialogRoot + implicitWidth: ruler.implicitWidth + toolBar.implicitWidth + } + }, + State { + name: "RightEdge" + PropertyChanges { + target: dialogRoot + enabledBorders: "LeftBorder|RightBorder" + } + PropertyChanges { + target: dialogRoot + implicitWidth: ruler.implicitWidth + toolBar.implicitWidth + } + } + ] +//END States +} diff --git a/desktoppackage/contents/configuration/panelconfiguration/EdgeHandle.qml b/desktoppackage/contents/configuration/panelconfiguration/EdgeHandle.qml new file mode 100644 index 0000000000..38e0915d27 --- /dev/null +++ b/desktoppackage/contents/configuration/panelconfiguration/EdgeHandle.qml @@ -0,0 +1,101 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +PlasmaComponents.Button { + text: i18n("Screen Edge") + + KQuickControlsAddons.MouseEventListener { + anchors.fill: parent + property int lastX + property int lastY + property int startMouseX + property int startMouseY + onPressed: { + lastX = mouse.screenX + lastY = mouse.screenY + startMouseX = mouse.x + startMouseY = mouse.y + } + onPositionChanged: { + panel.screen = mouse.screen; + + var newLocation = panel.location; + //If the mouse is in an internal rectangle, do nothing + if ((mouse.screenX < panel.screenGeometry.x + panel.screenGeometry.width/3 || + mouse.screenX > panel.screenGeometry.x + panel.screenGeometry.width/3*2) || + (mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height/3 || + mouse.screenY > panel.screenGeometry.y + panel.screenGeometry.height/3*2)) + { + var screenAspect = panel.screenGeometry.height / panel.screenGeometry.width; + + if (mouse.screenY < panel.screenGeometry.y+(mouse.screenX-panel.screenGeometry.x)*screenAspect) { + if (mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height-(mouse.screenX-panel.screenGeometry.x)*screenAspect) { + newLocation = PlasmaCore.Types.TopEdge; + } else { + newLocation = PlasmaCore.Types.RightEdge; + } + + } else { + if (mouse.screenY < panel.screenGeometry.y + panel.screenGeometry.height-(mouse.screenX-panel.screenGeometry.x)*screenAspect) { + newLocation = PlasmaCore.Types.LeftEdge; + } else { + newLocation = PlasmaCore.Types.BottomEdge; + } + } + } + panel.location = newLocation + + switch (newLocation) { + case PlasmaCore.Types.TopEdge: + var y = Math.max(mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y, panel.height); + configDialog.y = y; + panel.distance = Math.max(y - panel.height - panel.screen.geometry.y, 0); + break + case PlasmaCore.Types.LeftEdge: + var x = Math.max(mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x, panel.width); + configDialog.x = x; + panel.distance = Math.max(x - panel.width - panel.screen.geometry.x, 0); + break; + case PlasmaCore.Types.RightEdge: + var x = Math.min(mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x, mouse.screen.geometry.x + mouse.screen.size.width - panel.width - configDialog.width); + configDialog.x = x; + panel.distance = Math.max(mouse.screen.size.width - (x - mouse.screen.geometry.x) - panel.width - configDialog.width, 0); + break; + case PlasmaCore.Types.BottomEdge: + default: + var y = Math.min(mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y, mouse.screen.geometry.y + mouse.screen.size.height - panel.height - configDialog.height); + configDialog.y = y; + panel.distance = Math.max(mouse.screen.size.height - (y - mouse.screen.geometry.y) - panel.height - configDialog.height, 0); + } + + lastX = mouse.screenX + lastY = mouse.screenY + } + onReleased: { + panel.distance = 0 + panelResetAnimation.running = true + } + } +} diff --git a/desktoppackage/contents/configuration/panelconfiguration/Ruler.qml b/desktoppackage/contents/configuration/panelconfiguration/Ruler.qml new file mode 100644 index 0000000000..6cdccc997d --- /dev/null +++ b/desktoppackage/contents/configuration/panelconfiguration/Ruler.qml @@ -0,0 +1,356 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + + +PlasmaCore.FrameSvgItem { + id: root + + property alias offset: offsetHandle.value + property alias minimumLength: minimumLengthHandle.value + property alias maximumLength: maximumLengthHandle.value + + imagePath: "widgets/containment-controls" + state: "BottomEdge" + implicitWidth: offsetHandle.width + minimumLengthHandle.width + implicitHeight: offsetHandle.height + minimumLengthHandle.height + + onMinimumLengthChanged: leftMinimumLengthHandle.value = minimumLength + onMaximumLengthChanged: leftMaximumLengthHandle.value = maximumLength + + Component.onCompleted: { + offsetHandle.value = panel.offset + minimumLengthHandle.value = panel.minimumLength + maximumLengthHandle.value = panel.maximumLength + leftMinimumLengthHandle.value = panel.minimumLength + leftMaximumLengthHandle.value = panel.maximumLength + } + + PlasmaCore.Svg { + id: containmentControlsSvg + imagePath: "widgets/containment-controls" + } + PlasmaCore.SvgItem { + id: centerMark + svg: containmentControlsSvg + elementId: dialogRoot.vertical ? "vertical-centerindicator" : "horizontal-centerindicator" + visible: panel.alignment == Qt.AlignCenter + width: dialogRoot.vertical ? parent.width : naturalSize.width + height: dialogRoot.vertical ? naturalSize.height : parent.height + anchors.centerIn: parent + } + + SliderHandle { + id: offsetHandle + graphicElementName: "offsetslider" + onValueChanged: panel.offset = value + } + SliderHandle { + id: minimumLengthHandle + alignment: panel.alignment | Qt.AlignLeft + visible: panel.alignment != Qt.AlignRight + offset: panel.offset + graphicElementName: "minslider" + onValueChanged: panel.minimumLength = value + minimumValue: (panel.alignment == Qt.AlignCenter) ? ((dialogRoot.vertical) ? root.height/2 : root.width/2) : ((dialogRoot.vertical) ? -height/2 : -width/2); + } + SliderHandle { + id: maximumLengthHandle + alignment: panel.alignment | Qt.AlignLeft + visible: panel.alignment != Qt.AlignRight + offset: panel.offset + graphicElementName: "maxslider" + onValueChanged: panel.maximumLength = value + minimumValue: (panel.alignment == Qt.AlignCenter) ? ((dialogRoot.vertical) ? root.height/2 : root.width/2) : ((dialogRoot.vertical) ? -height/2 : -width/2); + } + SliderHandle { + id: leftMinimumLengthHandle + alignment: panel.alignment | Qt.AlignRight + visible: panel.alignment != Qt.AlignLeft + offset: panel.offset + graphicElementName: "maxslider" + onValueChanged: panel.minimumLength = value + maximumValue: (panel.alignment == Qt.AlignCenter) ? ((dialogRoot.vertical) ? root.height/2-height : root.width/2-width) : ((dialogRoot.vertical) ? root.height-height/2 : root.width-width/2); + } + SliderHandle { + id: leftMaximumLengthHandle + alignment: panel.alignment | Qt.AlignRight + visible: panel.alignment != Qt.AlignLeft + offset: panel.offset + graphicElementName: "minslider" + onValueChanged: panel.maximumLength = value + maximumValue: (panel.alignment == Qt.AlignCenter) ? ((dialogRoot.vertical) ? root.height/2-height : root.width/2-width) : ((dialogRoot.vertical) ? root.height-height/2 : root.width-width/2); + } + + states: [ + State { + name: "TopEdge" + PropertyChanges { + target: root + prefix: "north" + height: root.implicitHeight + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: undefined + left: root.parent.left + right: root.parent.right + } + } + AnchorChanges { + target: offsetHandle + anchors { + top: undefined + bottom: root.bottom + left: undefined + right: undefined + } + } + AnchorChanges { + target: minimumLengthHandle + anchors { + top: root.top + bottom: undefined + left: undefined + right: undefined + } + } + AnchorChanges { + target: maximumLengthHandle + anchors { + top: undefined + bottom: root.bottom + left: undefined + right: undefined + } + } + AnchorChanges { + target: leftMinimumLengthHandle + anchors { + top: root.top + bottom: undefined + left: undefined + right: undefined + } + } + AnchorChanges { + target: leftMaximumLengthHandle + anchors { + top: undefined + bottom: root.bottom + left: undefined + right: undefined + } + } + }, + State { + name: "BottomEdge" + PropertyChanges { + target: root + prefix: "south" + height: root.implicitHeight + } + AnchorChanges { + target: root + anchors { + top: undefined + bottom: root.parent.bottom + left: root.parent.left + right: root.parent.right + } + } + AnchorChanges { + target: offsetHandle + anchors { + top: root.top + bottom: undefined + left: undefined + right: undefined + } + } + AnchorChanges { + target: minimumLengthHandle + anchors { + top: undefined + bottom: root.bottom + left: undefined + right: undefined + } + } + AnchorChanges { + target: maximumLengthHandle + anchors { + top: root.top + bottom: undefined + left: undefined + right: undefined + } + } + AnchorChanges { + target: leftMinimumLengthHandle + anchors { + top: undefined + bottom: root.bottom + left: undefined + right: undefined + } + } + AnchorChanges { + target: leftMaximumLengthHandle + anchors { + top: root.top + bottom: undefined + left: undefined + right: undefined + } + } + }, + State { + name: "LeftEdge" + PropertyChanges { + target: root + prefix: "west" + width: root.implicitWidth + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: root.parent.bottom + left: root.parent.left + right: undefined + } + } + AnchorChanges { + target: offsetHandle + anchors { + top: undefined + bottom: undefined + left: undefined + right: root.right + } + } + AnchorChanges { + target: minimumLengthHandle + anchors { + top: undefined + bottom: undefined + left: root.left + right: undefined + } + } + AnchorChanges { + target: maximumLengthHandle + anchors { + top: undefined + bottom: undefined + left: undefined + right: root.right + } + } + AnchorChanges { + target: leftMinimumLengthHandle + anchors { + top: undefined + bottom: undefined + left: root.left + right: undefined + } + } + AnchorChanges { + target: leftMaximumLengthHandle + anchors { + top: undefined + bottom: undefined + left: undefined + right: root.right + } + } + }, + State { + name: "RightEdge" + PropertyChanges { + target: root + prefix: "east" + width: root.implicitWidth + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: root.parent.bottom + left: undefined + right: root.parent.right + } + } + AnchorChanges { + target: offsetHandle + anchors { + top: undefined + bottom: undefined + left: parent.left + right: undefined + } + } + AnchorChanges { + target: minimumLengthHandle + anchors { + top: undefined + bottom: undefined + left: undefined + right: parent.right + } + } + AnchorChanges { + target: maximumLengthHandle + anchors { + top: undefined + bottom: undefined + left: parent.left + right: undefined + } + } + AnchorChanges { + target: leftMinimumLengthHandle + anchors { + top: undefined + bottom: undefined + left: undefined + right: parent.right + } + } + AnchorChanges { + target: leftMaximumLengthHandle + anchors { + top: undefined + bottom: undefined + left: parent.left + right: undefined + } + } + } + ] +} diff --git a/desktoppackage/contents/configuration/panelconfiguration/SizeHandle.qml b/desktoppackage/contents/configuration/panelconfiguration/SizeHandle.qml new file mode 100644 index 0000000000..87b9c546d7 --- /dev/null +++ b/desktoppackage/contents/configuration/panelconfiguration/SizeHandle.qml @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +PlasmaComponents.Button { + text: panel.location == PlasmaCore.Types.LeftEdge || panel.location == PlasmaCore.Types.RightEdge ? i18n("Width") : i18n("Height") + KQuickControlsAddons.MouseEventListener { + anchors.fill: parent + property int startMouseX + property int startMouseY + onPressed: { + startMouseX = mouse.x + startMouseY = mouse.y + } + onPositionChanged: { + switch (panel.location) { + case PlasmaCore.Types.TopEdge: + configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y + panel.thickness = configDialog.y - panel.y + break; + case PlasmaCore.Types.LeftEdge: + configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x + panel.thickness = configDialog.x - panel.x + break; + case PlasmaCore.Types.RightEdge: + configDialog.x = mouse.screenX - mapToItem(dialogRoot, startMouseX, 0).x + panel.thickness = (panel.x + panel.width) - (configDialog.x + configDialog.width) + break; + case PlasmaCore.Types.BottomEdge: + default: + configDialog.y = mouse.screenY - mapToItem(dialogRoot, 0, startMouseY).y + panel.thickness = (panel.y + panel.height) - (configDialog.y + configDialog.height) + } + } + } +} diff --git a/desktoppackage/contents/configuration/panelconfiguration/SliderHandle.qml b/desktoppackage/contents/configuration/panelconfiguration/SliderHandle.qml new file mode 100644 index 0000000000..5a5c350776 --- /dev/null +++ b/desktoppackage/contents/configuration/panelconfiguration/SliderHandle.qml @@ -0,0 +1,173 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + + +PlasmaCore.SvgItem { + id: root + svg: containmentControlsSvg + state: parent.state + width: naturalSize.width + height: naturalSize.height + + //value expressed by this slider + property int value + //name of the graphics to load + property string graphicElementName + //where the point "0" is + property int offset: 0 + //handle type: behave in different ways based on the alignment + property int alignment: panel.alignment + + property int minimumValue: (dialogRoot.vertical) ? -root.height/2 : -root.width/2 + property int maximumValue: (dialogRoot.vertical) ? root.parent.height : root.parent.width + + function syncPos() { + if (dialogRoot.vertical) { + if (alignment == Qt.AlignRight) { + y = root.parent.height - (value + offset + root.height/2) + } else if (alignment == Qt.AlignLeft) { + y = value + offset - root.height/2 + } else { + if (root.alignment & Qt.AlignRight) { + y = root.parent.height/2 - value/2 - offset + root.height/2 + } else if (root.alignment & Qt.AlignLeft) { + y = root.parent.height/2 + value/2 + offset - root.height/2 + } else { + y = root.parent.height/2 + value + offset -root.height/2 + } + } + } else { + if (alignment == Qt.AlignRight) { + x = root.parent.width - (value + offset + root.width/2) + } else if (alignment == Qt.AlignLeft) { + x = value + offset - root.width/2 + } else { + if (root.alignment & Qt.AlignRight) { + x = root.parent.width/2 - value/2 + offset - root.width/2 + } else if (root.alignment & Qt.AlignLeft) { + x = root.parent.width/2 + value/2 + offset -root.width/2 + } else { + x = root.parent.width/2 + value + offset -root.width/2 + } + } + } + } + onValueChanged: syncPos() + onOffsetChanged: syncPos() + onAlignmentChanged: syncPos() + Connections { + target: root.parent + onWidthChanged: syncPos() + onHeightChanged: syncPos() + } + + MouseArea { + drag { + target: parent + axis: (dialogRoot.vertical) ? Drag.YAxis : Drag.XAxis + minimumX: root.minimumValue + minimumY: root.minimumValue + maximumX: root.maximumValue + maximumY: root.maximumValue + } + anchors.fill: parent + onPositionChanged: { + if (dialogRoot.vertical) { + if (root.alignment == Qt.AlignRight) { + root.value = root.parent.height - (parent.y + offset + root.height/2) + } else if (alignment == Qt.AlignLeft) { + root.value = parent.y - offset + root.height/2 + //Center + } else { + if (root.alignment & Qt.AlignRight) { + root.value = (root.parent.height/2 - parent.y + offset)*2 + root.height/2 + } else if (root.alignment & Qt.AlignLeft) { + root.value = (parent.y - offset - root.parent.height/2)*2 + root.height/2 + } else { + var value = parent.y - root.parent.height/2 - offset + root.height/2 + //Snap + if (Math.abs(value) < 5) { + root.value = 0 + } else { + root.value = value + } + } + } + } else { + if (root.alignment == Qt.AlignRight) { + root.value = root.parent.width - (parent.x + offset + root.width/2) + } else if (alignment == Qt.AlignLeft) { + root.value = parent.x - offset + root.width/2 + //Center + } else { + if (root.alignment & Qt.AlignRight) { + root.value = (root.parent.width/2 - parent.x + offset)*2 + root.width/2 + } else if (root.alignment & Qt.AlignLeft) { + root.value = (parent.x - offset - root.parent.width/2)*2 + root.width/2 + } else { + var value = parent.x - root.parent.width/2 - offset + root.width/2 + //Snap + if (Math.abs(value) < 5) { + root.value = 0 + } else { + root.value = value + } + } + } + } + } + } + + states: [ + State { + name: "TopEdge" + PropertyChanges { + target: root + elementId: "north-" + graphicElementName + } + }, + State { + name: "BottomEdge" + PropertyChanges { + target: root + elementId: "south-" + graphicElementName + } + }, + State { + name: "LeftEdge" + PropertyChanges { + target: root + elementId: "west-" + graphicElementName + } + }, + State { + name: "RightEdge" + PropertyChanges { + target: root + elementId: "east-" + graphicElementName + } + } + ] +} diff --git a/desktoppackage/contents/configuration/panelconfiguration/ToolBar.qml b/desktoppackage/contents/configuration/panelconfiguration/ToolBar.qml new file mode 100644 index 0000000000..59a88ead03 --- /dev/null +++ b/desktoppackage/contents/configuration/panelconfiguration/ToolBar.qml @@ -0,0 +1,365 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.configuration 2.0 + +Item { + id: root + state: parent.state + implicitWidth: units.gridUnit * 10 + implicitHeight: row.height + 20 + + GridLayout { + id: row + columns: dialogRoot.vertical ? 1 : 2 + rows: dialogRoot.vertical ? 2 : 1 + anchors.centerIn: parent + + rowSpacing: units.smallSpacing + columnSpacing: units.smallSpacing + + EdgeHandle {} + SizeHandle {} + } + + PlasmaComponents.Label { + id: placeHolder + visible: false + text: i18n("Add Widgets...") + i18n("Add spacer") + i18n("More Settings...") + } + + GridLayout { + id: buttonsLayout + rows: 1 + columns: 1 + flow: plasmoid.formFactor == PlasmaCore.Types.Horizontal ? GridLayout.TopToBottom : GridLayout.LeftToRight + + anchors.margins: rowSpacing + + property bool showText: plasmoid.formFactor == PlasmaCore.Types.Vertical || (row.x + row.width < root.width - placeHolder.width - units.iconSizes.small*4 - units.largeSpacing*5) + + rowSpacing: units.smallSpacing + columnSpacing: units.smallSpacing + + PlasmaComponents.Button { + text: buttonsLayout.showText ? i18n("Add Widgets...") : "" + iconSource: "list-add" + Layout.preferredWidth: panel.formFactor == PlasmaCore.Types.Vertical ? Math.max(implicitWidth, parent.width) : implicitWidth + onClicked: { + configDialog.showAddWidgetDialog(); + } + } + + PlasmaComponents.Button { + iconSource: "distribute-horizontal-x" + text: buttonsLayout.showText ? i18n("Add spacer") : "" + Layout.preferredWidth: panel.formFactor == PlasmaCore.Types.Vertical ? Math.max(implicitWidth, parent.width) : implicitWidth + onClicked: { + configDialog.addPanelSpacer(); + } + } + + PlasmaComponents.Button { + id: settingsButton + iconSource: "configure" + text: buttonsLayout.showText ? i18n("More Settings...") : "" + Layout.preferredWidth: panel.formFactor == PlasmaCore.Types.Vertical ? Math.max(implicitWidth, parent.width) : implicitWidth + onClicked: { + contextMenu.visible = !contextMenu.visible; + } + } + + PlasmaComponents.ToolButton { + iconSource: "window-close" + onClicked: { + configDialog.close() + } + } + + PlasmaCore.Dialog { + id: contextMenu + visualParent: settingsButton + location: plasmoid.location + type: PlasmaCore.Dialog.PopupMenu + flags: Qt.Popup | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus + mainItem: Column { + id: menuColumn + width: units.gridUnit * 5 + height: units.gridUnit * 10 + Layout.minimumWidth: menuColumn.implicitWidth + Layout.minimumHeight: menuColumn.implicitHeight + spacing: units.smallSpacing + PlasmaExtras.Heading { + level: 3 + text: i18n("Panel Alignment") + } + PlasmaComponents.ButtonColumn { + spacing: 0 + anchors { + left: parent.left + right: parent.right + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Left") + checkable: true + checked: panel.alignment == Qt.AlignLeft + onClicked: panel.alignment = Qt.AlignLeft + flat: false + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Center") + checkable: true + checked: panel.alignment == Qt.AlignCenter + onClicked: panel.alignment = Qt.AlignCenter + flat: false + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Right") + checkable: true + checked: panel.alignment == Qt.AlignRight + onClicked: panel.alignment = Qt.AlignRight + flat: false + } + } + + PlasmaExtras.Heading { + level: 3 + text: i18n("Visibility") + } + PlasmaComponents.ButtonColumn { + spacing: 0 + anchors { + left: parent.left + right: parent.right + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Always visible") + checkable: true + checked: configDialog.visibilityMode == 0 + onClicked: configDialog.visibilityMode = 0 + flat: false + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Auto Hide") + checkable: true + checked: configDialog.visibilityMode == 1 + onClicked: configDialog.visibilityMode = 1 + flat: false + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Windows can cover") + checkable: true + checked: configDialog.visibilityMode == 2 + onClicked: configDialog.visibilityMode = 2 + flat: false + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Windows go below") + checkable: true + checked: configDialog.visibilityMode == 3 + onClicked: configDialog.visibilityMode = 3 + flat: false + } + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Maximize Panel") + iconSource: panel.formFactor == PlasmaCore.Types.Vertical ? "zoom-fit-height" : "zoom-fit-width" + onClicked: panel.maximize(); + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Lock Widgets") + iconSource: "document-encrypt" + onClicked: { + plasmoid.action("lock widgets").trigger(); + configDialog.close(); + } + } + PlasmaComponents.ToolButton { + anchors { + left: parent.left + right: parent.right + } + text: i18n("Remove Panel") + iconSource: "window-close" + onClicked: { + contextMenu.visible = false; + plasmoid.action("remove").trigger(); + } + } + } + } + + } +//BEGIN States + states: [ + State { + name: "TopEdge" + PropertyChanges { + target: root + height: root.implicitHeight + } + AnchorChanges { + target: root + anchors { + top: undefined + bottom: root.parent.bottom + left: root.parent.left + right: root.parent.right + } + } + AnchorChanges { + target: buttonsLayout + anchors { + verticalCenter: root.verticalCenter + top: undefined + bottom: undefined + left: undefined + right: root.right + } + } + PropertyChanges { + target: buttonsLayout + width: buttonsLayout.implicitWidth + } + }, + State { + name: "BottomEdge" + PropertyChanges { + target: root + height: root.implicitHeight + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: undefined + left: root.parent.left + right: root.parent.right + } + } + AnchorChanges { + target: buttonsLayout + anchors { + verticalCenter: root.verticalCenter + top: undefined + bottom: undefined + left: undefined + right: root.right + } + } + PropertyChanges { + target: buttonsLayout + width: buttonsLayout.implicitWidth + } + }, + State { + name: "LeftEdge" + PropertyChanges { + target: root + width: root.implicitWidth + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: root.parent.bottom + left: undefined + right: root.parent.right + } + } + AnchorChanges { + target: buttonsLayout + anchors { + verticalCenter: undefined + top: undefined + bottom: root.bottom + left: root.left + right: root.right + } + } + }, + State { + name: "RightEdge" + PropertyChanges { + target: root + width: root.implicitWidth + } + AnchorChanges { + target: root + anchors { + top: root.parent.top + bottom: root.parent.bottom + left: root.parent.left + right: undefined + } + } + AnchorChanges { + target: buttonsLayout + anchors { + verticalCenter: undefined + top: undefined + bottom: root.bottom + left: root.left + right: root.right + } + } + } + ] +//END States +} diff --git a/desktoppackage/contents/defaults b/desktoppackage/contents/defaults new file mode 100644 index 0000000000..08f4288c4f --- /dev/null +++ b/desktoppackage/contents/defaults @@ -0,0 +1,16 @@ +[Desktop] +Containment=org.kde.desktopcontainment +ToolBox=org.kde.desktoptoolbox +RuntimePlatform= + +[Desktop][ContainmentActions] +RightButton;NoModifier=org.kde.contextmenu +MidButton;NoModifier=org.kde.paste +wheel:Vertical;NoModifier=org.kde.switchdesktop + +[Panel] +Containment=org.kde.panel +ToolBox=org.kde.paneltoolbox +[Panel][ContainmentActions] +RightButton;NoModifier=org.kde.contextmenu +MidButton;NoModifier=org.kde.paste diff --git a/desktoppackage/contents/explorer/AppletDelegate.qml b/desktoppackage/contents/explorer/AppletDelegate.qml new file mode 100644 index 0000000000..872ca38127 --- /dev/null +++ b/desktoppackage/contents/explorer/AppletDelegate.qml @@ -0,0 +1,116 @@ +/* + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.0 + +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.draganddrop 2.0 +import org.kde.kquickcontrolsaddons 2.0 + +PlasmaCore.FrameSvgItem { + id: background + width: list.delegateWidth + height: list.delegateHeight + + property variant icon: decoration + property string title: name + property string description: model.description + property string author: model.author + property string email: model.email + property string license: model.license + property string pluginName: model.pluginName + property bool local: model.local + + imagePath: "widgets/viewitem" + prefix: mouseArea.containsMouse ? "hover" : "normal" + + DragArea { + anchors.fill: parent + supportedActions: Qt.MoveAction | Qt.LinkAction + //onDragStarted: tooltipDialog.visible = false + delegateImage: decoration + mimeData { + source: parent + } + Component.onCompleted: mimeData.setData("text/x-plasmoidservicename", pluginName) + + QIconItem { + id: iconWidget + anchors.verticalCenter: parent.verticalCenter + x: y + width: units.iconSizes.huge + height: width + icon: background.icon + } + ColumnLayout { + anchors { + top: parent.top + bottom: parent.bottom + left: iconWidget.right + right: parent.right + + topMargin: background.margins.top + bottomMargin: background.margins.bottom + leftMargin: background.margins.left + rightMargin: background.margins.right + } + spacing: 4 + PlasmaExtras.Heading { + id: titleText + level: 4 + text: name + elide: Text.ElideRight + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + PlasmaComponents.Label { + text: description + font.pointSize: theme.smallestFont.pointSize + wrapMode: Text.WordWrap + elide: Text.ElideRight + maximumLineCount: 3 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + QIconItem { + icon: running ? "dialog-ok-apply" : undefined + visible: running + width: units.iconSizes.small + height: width + anchors { + top: iconWidget.top + left: iconWidget.left + topMargin: units.smallSpacing + leftMargin: units.smallSpacing + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onDoubleClicked: widgetExplorer.addApplet(pluginName) + //onEntered: tooltipDialog.appletDelegate = background + //onExited: tooltipDialog.appletDelegate = null + } + } +} diff --git a/desktoppackage/contents/explorer/Tooltip.qml b/desktoppackage/contents/explorer/Tooltip.qml new file mode 100644 index 0000000000..0eafa19690 --- /dev/null +++ b/desktoppackage/contents/explorer/Tooltip.qml @@ -0,0 +1,148 @@ +/* + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 as Layouts +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras + +MouseArea { + id: main + + hoverEnabled: true + onEntered: toolTipHideTimer.running = false + onExited: toolTipHideTimer.running = true + + width: theme.mSize(theme.defaultFont).width * 35 + height: theme.mSize(theme.defaultFont).height * 16 + + property variant icon + property string title + property string description + property string author + property string email + property string license + property string pluginName + property bool local + + onClicked: tooltipDialog.visible = false + Connections { + target: tooltipDialog + onAppletDelegateChanged: { + if (!tooltipDialog.appletDelegate) { + return + } + icon = tooltipDialog.appletDelegate.icon + title = tooltipDialog.appletDelegate.title + description = tooltipDialog.appletDelegate.description + author = tooltipDialog.appletDelegate.author + email = tooltipDialog.appletDelegate.email + license = tooltipDialog.appletDelegate.license + pluginName = tooltipDialog.appletDelegate.pluginName + local = tooltipDialog.appletDelegate.local + } + } + PlasmaCore.IconItem { + id: tooltipIconWidget + anchors { + left: parent.left + top: parent.top + margins: 8 + } + width: units.iconSizes.huge + height: width + source: main.icon + } + Column { + id: nameColumn + spacing: 8 + anchors { + left: tooltipIconWidget.right + margins: 8 + top: parent.top + right: parent.right + } + + PlasmaExtras.Heading { + text: title + level: 2 + anchors.left: parent.left + anchors.right: parent.right + height: paintedHeight + wrapMode: Text.Wrap + } + PlasmaComponents.Label { + text: description + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.Wrap + } + } + Layouts.GridLayout { + columns: 2 + anchors { + top: (nameColumn.height > tooltipIconWidget.height) ? nameColumn.bottom : tooltipIconWidget.bottom + topMargin: 16 + horizontalCenter: parent.horizontalCenter + } + PlasmaComponents.Label { + text: i18n("License:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + PlasmaComponents.Label { + id: licenseText + text: license + wrapMode: Text.Wrap + } + PlasmaComponents.Label { + text: i18n("Author:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + PlasmaComponents.Label { + text: author + wrapMode: Text.Wrap + } + PlasmaComponents.Label { + text: i18n("Email:") + Layouts.Layout.alignment: Qt.AlignVCenter|Qt.AlignRight + } + PlasmaComponents.Label { + text: email + } + } + + PlasmaComponents.Button { + id: uninstallButton + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + } + opacity: local ? 1 : 0 + Behavior on opacity { + NumberAnimation { duration: units.longDuration } + } + iconSource: "application-exit" + text: i18n("Uninstall") + onClicked: { + widgetExplorer.uninstall(pluginName) + tooltipDialog.visible = false + } + } +} diff --git a/desktoppackage/contents/explorer/WidgetExplorer.qml b/desktoppackage/contents/explorer/WidgetExplorer.qml new file mode 100644 index 0000000000..a95214d398 --- /dev/null +++ b/desktoppackage/contents/explorer/WidgetExplorer.qml @@ -0,0 +1,287 @@ +/* + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.kquickcontrolsaddons 2.0 +import QtQuick.Window 2.1 +import org.kde.plasma.private.shell 2.0 + +Item { + id: main + + width: 240 + height: 800//Screen.height + //this is used to perfectly align the filter field and delegates + property int cellWidth: theme.mSize(theme.defaultFont).width * 10 + + property int minimumWidth: theme.mSize(theme.defaultFont).width * 12 + property int minimumHeight: 800//topBar.height + list.delegateHeight + (widgetExplorer.orientation == Qt.Horizontal ? scrollBar.height : 0) + 4 + + property alias containment: widgetExplorer.containment + + property Item getWidgetsButton + property Item categoryButton + + signal closed() + + WidgetExplorer { + id: widgetExplorer + //view: desktop + onShouldClose: main.closed(); + } + + PlasmaComponents.ModelContextMenu { + id: categoriesDialog + visualParent: main.categoryButton + model: widgetExplorer.filterModel + + onClicked: { + list.contentX = 0 + list.contentY = 0 + main.categoryButton.text = model.display + widgetExplorer.widgetsModel.filterQuery = model.filterData + widgetExplorer.widgetsModel.filterType = model.filterType + } + } + + PlasmaComponents.ModelContextMenu { + id: getWidgetsDialog + visualParent: main.getWidgetsButton + model: widgetExplorer.widgetsMenuActions + onClicked: model.trigger() + } + /* + PlasmaCore.Dialog { + id: tooltipDialog + property Item appletDelegate + location: PlasmaCore.Types.RightEdge //actually we want this to be the opposite location of the explorer itself + + type: PlasmaCore.Dialog.Tooltip + flags:Qt.Window|Qt.WindowStaysOnTopHint|Qt.X11BypassWindowManagerHint + + onAppletDelegateChanged: { + if (!appletDelegate) { + toolTipHideTimer.restart() + toolTipShowTimer.running = false + } else if (tooltipDialog.visible) { + tooltipDialog.visualParent = appletDelegate + } else { + tooltipDialog.visualParent = appletDelegate + toolTipShowTimer.restart() + toolTipHideTimer.running = false + } + } + mainItem: Tooltip { id: tooltipWidget } + + Behavior on y { + NumberAnimation { duration: units.longDuration } + } + } + Timer { + id: toolTipShowTimer + interval: 500 + repeat: false + onTriggered: { + tooltipDialog.visible = true + } + } + Timer { + id: toolTipHideTimer + interval: 1000 + repeat: false + onTriggered: tooltipDialog.visible = false + } + */ + + Loader { + id: topBar + property Item categoryButton + + sourceComponent: verticalTopBarComponent + anchors { + top: parent.top + left: parent.left + right: parent.right + topMargin: 0 + leftMargin: units.smallSpacing + rightMargin: units.smallSpacing + } + } + Component { + id: verticalTopBarComponent + + Column { + anchors.top: parent.top + anchors.left:parent.left + anchors.right: parent.right + spacing: 4 + + PlasmaComponents.ToolButton { + anchors.right: parent.right + iconSource: "window-close" + onClicked: main.closed() + } + PlasmaComponents.TextField { + anchors { + left: parent.left + right: parent.right + } + clearButtonShown: true + placeholderText: i18n("Enter search term...") + onTextChanged: { + list.contentX = 0 + list.contentY = 0 + widgetExplorer.widgetsModel.searchTerm = text + } + Component.onCompleted: forceActiveFocus() + } + PlasmaComponents.Button { + anchors { + left: parent.left + right: parent.right + } + id: categoryButton + text: i18n("Categories") + onClicked: categoriesDialog.open(0, categoryButton.height) + } + Component.onCompleted: { + main.categoryButton = categoryButton + } + } + } + + PlasmaExtras.ScrollArea { + anchors { + top: topBar.bottom + left: parent.left + right: parent.right + bottom: bottomBar.top + topMargin: units.smallSpacing + leftMargin: units.smallSpacing + bottomMargin: units.smallSpacing + } + flickableItem: ListView { + id: list + + property int delegateWidth: list.width + property int delegateHeight: theme.defaultFont.pixelSize * 7 - 4 + + snapMode: ListView.SnapToItem + model: widgetExplorer.widgetsModel + + clip: true //TODO work out why this is this needed + + delegate: AppletDelegate {} + + //slide in to view from the left + add: Transition { + NumberAnimation { + properties: "x" + from: -list.width + to: 0 + duration: units.shortDuration * 3 + + } + } + + //slide out of view to the right + remove: Transition { + NumberAnimation { + properties: "x" + to: list.width + duration: units.shortDuration * 3 + } + } + + //if we are adding other items into the view use the same animation as normal adding + //this makes everything slide in together + //if we make it move everything ends up weird + addDisplaced: list.add + + //moved due to filtering + displaced: Transition { + NumberAnimation { + properties: "y" + duration: units.shortDuration * 3 + } + } + } + } + + Loader { + id: bottomBar + + sourceComponent: verticalBottomBarComponent + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + leftMargin: units.smallSpacing + rightMargin: units.smallSpacing + bottomMargin: units.smallSpacing + } + } + + Component { + id: verticalBottomBarComponent + Column { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + + spacing: units.smallSpacing + + PlasmaComponents.Button { + anchors { + left: parent.left + right: parent.right + } + id: getWidgetsButton + iconSource: "get-hot-new-stuff" + text: i18n("Get new widgets") + onClicked: getWidgetsDialog.open() + } + + Repeater { + model: widgetExplorer.extraActions.length + PlasmaComponents.Button { + anchors { + left: parent.left + right: parent.right + } + iconSource: widgetExplorer.extraActions[modelData].icon + text: widgetExplorer.extraActions[modelData].text + onClicked: { + widgetExplorer.extraActions[modelData].trigger() + } + } + } + + Component.onCompleted: { + main.getWidgetsButton = getWidgetsButton + } + } + } +} + diff --git a/desktoppackage/contents/layout.js b/desktoppackage/contents/layout.js new file mode 100644 index 0000000000..303e7af164 --- /dev/null +++ b/desktoppackage/contents/layout.js @@ -0,0 +1,18 @@ + +var panel = new Panel +panel.screen = 0 +panel.location = 'bottom' +panel.addWidget("org.kde.plasma.kickoff") +panel.addWidget("org.kde.plasma.pager") +panel.addWidget("org.kde.plasma.taskmanager") +panel.addWidget("org.kde.plasma.systemtray") +panel.addWidget("org.kde.plasma.digitalclock") + +for (var i = 0; i < screenCount; ++i) { + var desktop = new Activity + desktop.name = i18n("Desktop") + desktop.screen = i + desktop.wallpaperPlugin = 'org.kde.image' + + //var clock = desktop.addWidget("org.kde.analogclock"); +} diff --git a/desktoppackage/contents/loader.qml b/desktoppackage/contents/loader.qml new file mode 100644 index 0000000000..316a736f44 --- /dev/null +++ b/desktoppackage/contents/loader.qml @@ -0,0 +1,51 @@ +/* vim:set foldmethod=marker: + * + * Copyright (C) 2013 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2 +import org.kde.solid 1.0 as Solid +import org.kde.plasma.platformcomponents 2.0 as Platform + +Item { + id: main + + property string shell : "org.kde.desktop" + property bool willing : true + property int priority : 1 + + // This is not needed, but allows the + // handler to know whether its shell is loaded + property bool loaded : false + + default property var children + /* This seems to crash not being able to address Solid.Device, which seems to be + a problem with latest Qt 5.3. Pleas re-enable only after consultation with + plasma-devel@kde.org */ + /* + Solid.Devices { + id: keyboards + query: "IS Keyboard" + } + */ + Platform.Application { + application: "krunner" + running: main.loaded + } +} + diff --git a/desktoppackage/contents/views/Desktop.qml b/desktoppackage/contents/views/Desktop.qml new file mode 100644 index 0000000000..5c7087993e --- /dev/null +++ b/desktoppackage/contents/views/Desktop.qml @@ -0,0 +1,194 @@ +/* + * Copyright 2012 Marco Martin + * Copyright 2014 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import "../activitymanager" +import "../explorer" + + +Rectangle { + id: root + color: Qt.rgba(0, 0, 0, 0.2) + width: 1024 + height: 768 + + property Item containment + + function toggleWidgetExplorer(containment) { + console.log("Widget Explorer toggled"); + + if (sidePanelStack.state == "widgetExplorer") { + sidePanelStack.state = "closed"; + } else { + sidePanelStack.setSource(Qt.resolvedUrl("../explorer/WidgetExplorer.qml"), {"containment": containment}) + sidePanelStack.state = "widgetExplorer"; + } + } + + function toggleActivityManager() { + console.log("Activity manger toggled"); + if (sidePanelStack.state == "activityManager") { + sidePanelStack.state = "closed"; + } else { + sidePanelStack.setSource(Qt.resolvedUrl("../activitymanager/ActivityManager.qml")) + sidePanelStack.state = "activityManager"; + } + } + + PlasmaCore.Dialog { + id: sidePanel + location: PlasmaCore.Types.LeftEdge + type: PlasmaCore.Dialog.Dock + flags: Qt.Window|Qt.WindowStaysOnTopHint|Qt.X11BypassWindowManagerHint + + hideOnWindowDeactivate: true + + onVisibleChanged: { + if (!visible) { + sidePanelStack.state = "closed"; + } else { + sidePanel.requestActivate(); + // get the current available screen geometry and subtract the dialog's frame margins + sidePanelStack.height = containment ? containment.availableScreenRect(containment.screen).height - sidePanel.margins.top - sidePanel.margins.bottom : 1000; + } + } + + mainItem: Loader { + id: sidePanelStack + asynchronous: true + height: 1000 //start with some arbitrary height, will be changed from onVisibleChanged + width: item ? item.width: 0 + onLoaded: { + if (sidePanelStack.item) { + item.closed.connect(function(){sidePanelStack.state = "closed";}); + + if (sidePanelStack.state == "activityManager") { + sidePanel.hideOnWindowDeactivate = Qt.binding(function() { return !sidePanelStack.item.showingDialog; }) + } else { + sidePanel.hideOnWindowDeactivate = true; + } + } + sidePanel.visible = true; + } + onStateChanged: { + if (sidePanelStack.state == "closed") { + sidePanelStack.source = ""; //unload all elements + sidePanel.visible = false; + } + } + } + } + + onContainmentChanged: { + print("New Containment: " + containment); + print("Old Containment: " + internal.oldContainment); + //containment.parent = root; + + internal.newContainment = containment; + + if (containment != null) { + containment.visible = true; + } + if (containment != null) { + if (internal.oldContainment != null && internal.oldContainment != containment) { + if (internal.newContainment != null) { + switchAnim.running = true; + } + } else { + containment.anchors.left = root.left; + containment.anchors.top = root.top; + containment.anchors.right = root.right; + containment.anchors.bottom = root.bottom; + internal.oldContainment = containment; + } + } + } + + //some properties that shouldn't be accessible from elsewhere + QtObject { + id: internal; + + property Item oldContainment: null; + property Item newContainment: null; + } + + SequentialAnimation { + id: switchAnim + ScriptAction { + script: { + if (containment) { + containment.anchors.left = undefined; + containment.anchors.top = undefined; + containment.anchors.right = undefined; + containment.anchors.bottom = undefined; + } + internal.oldContainment.anchors.left = undefined; + internal.oldContainment.anchors.top = undefined; + internal.oldContainment.anchors.right = undefined; + internal.oldContainment.anchors.bottom = undefined; + + if (containment) { + internal.oldContainment.z = 0; + internal.oldContainment.x = 0; + containment.z = 1; + containment.x = root.width; + } + } + } + ParallelAnimation { + NumberAnimation { + target: internal.oldContainment + properties: "x" + to: internal.newContainment != null ? -root.width : 0 + duration: 400 + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: internal.newContainment + properties: "x" + to: 0 + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + ScriptAction { + script: { + if (containment) { + containment.anchors.left = root.left; + containment.anchors.top = root.top; + containment.anchors.right = root.right; + containment.anchors.bottom = root.bottom; + internal.oldContainment.visible = false; + internal.oldContainment = containment; + } + } + } + } + + + Component.onCompleted: { + //configure the view behavior + desktop.stayBehind = true; + desktop.fillScreen = true; + print("View org.kde.desktop QML loaded") + } +} diff --git a/desktoppackage/contents/views/Panel.qml b/desktoppackage/contents/views/Panel.qml new file mode 100644 index 0000000000..5ed55e54ca --- /dev/null +++ b/desktoppackage/contents/views/Panel.qml @@ -0,0 +1,181 @@ +/* + * Copyright 2012 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 + +import org.kde.plasma.core 2.0 as PlasmaCore + + +PlasmaCore.FrameSvgItem { + id: root + width: 640 + height: 32 + imagePath: "widgets/panel-background" + visible: false //adjust borders is run during setup. We want to avoid painting till completed + + property Item containment + + function adjustBorders() { + //print("PANEL ADJUST BORDERS"); + var borders = PlasmaCore.FrameSvg.AllBorders; + if (!containment) { + // Containment seems to never be set + // (or not reachable from here at least) + // FIXME: investigate why containment is null + print("FIXME Panel borders hardcoded in Panel.qml"); + borders = borders & ~PlasmaCore.FrameSvg.BottomBorder; + borders = borders & ~PlasmaCore.FrameSvg.LeftBorder; + borders = borders & ~PlasmaCore.FrameSvg.RightBorder; + root.enabledBorders = borders; + return; + } + + switch (containment.location) { + case PlasmaCore.Types.TopEdge: + borders = borders & ~PlasmaCore.FrameSvg.TopBorder; + break; + case PlasmaCore.Types.LeftEdge: + borders = borders & ~PlasmaCore.FrameSvg.LeftBorder; + break; + case PlasmaCore.Types.RightEdge: + borders = borders & ~PlasmaCore.FrameSvg.RightBorder; + break; + case PlasmaCore.Types.BottomEdge: + default: + print("PANEL disable bottom border"); + borders = borders & ~PlasmaCore.FrameSvg.BottomBorder; + break; + } + + if (panel.x <= panel.screen.geometry.x) { + borders = borders & ~PlasmaCore.FrameSvg.LeftBorder; + } + if (panel.x + panel.width >= panel.screen.geometry.x + panel.screen.geometry.width) { + borders = borders & ~PlasmaCore.FrameSvg.RightBorder; + } + if (panel.y <= panel.screen.geometry.y) { + borders = borders & ~PlasmaCore.FrameSvg.TopBorder; + } + if (panel.y + panel.height >= panel.screen.geometry.y + panel.screen.geometry.height) { + borders = borders & ~PlasmaCore.FrameSvg.BottomBorder; + } + + root.enabledBorders = borders; + + containmentParent.anchors.topMargin = Math.min(root.margins.top, Math.max(1, root.height - units.iconSizes.smallMedium)); + + containmentParent.anchors.bottomMargin = Math.min(root.margins.bottom, Math.max(1, root.height - units.iconSizes.smallMedium)); + + //Base the left/right margins on height as well, to have a good radial simmetry + containmentParent.anchors.leftMargin = Math.min(root.margins.left, Math.max(1, root.height - units.iconSizes.smallMedium)); + + containmentParent.anchors.leftMargin = Math.min(root.margins.left, Math.max(1, root.height - units.iconSizes.smallMedium)); + } + + onContainmentChanged: { + print("New panel Containment: " + containment); + containment.parent = containmentParent; + containment.visible = true; + containment.anchors.fill = containmentParent; + + containment.locationChanged.connect(adjustBorders); + if (containment.Layout) { + containment.Layout.minimumWidthChanged.connect(minimumWidthChanged); + containment.Layout.maximumWidthChanged.connect(maximumWidthChanged); + containment.Layout.preferredWidthChanged.connect(preferredWidthChanged); + + containment.Layout.minimumHeightChanged.connect(minimumHeightChanged); + containment.Layout.maximumHeightChanged.connect(maximumHeightChanged); + containment.Layout.preferredHeightChanged.connect(preferredHeightChanged); + } + adjustBorders(); + } + + function minimumWidthChanged() { + if (containment.formFactor === PlasmaCore.Types.Horizontal) { + panel.width = Math.max(panel.width, containment.Layout.minimumWidth); + } + } + function maximumWidthChanged() { + if (containment.formFactor === PlasmaCore.Types.Horizontal) { + panel.width = Math.min(panel.width, containment.Layout.maximumWidth); + } + } + function preferredWidthChanged() { + if (containment.formFactor === PlasmaCore.Types.Horizontal) { + panel.width = Math.min(panel.maximumLength, Math.max(containment.Layout.preferredWidth, panel.minimumLength)); + } + } + + function minimumHeightChanged() { + if (containment.formFactor === PlasmaCore.Types.Vertical) { + panel.height = Math.max(panel.height, containment.Layout.minimumWidth); + } + } + function maximumHeightChanged() { + if (containment.formFactor === PlasmaCore.Types.Vertical) { + panel.height = Math.min(panel.height, containment.Layout.maximumWidth); + } + } + function preferredHeightChanged() { + if (containment.formFactor === PlasmaCore.Types.Vertical) { + panel.height = Math.min(panel.maximumLength, Math.max(containment.Layout.preferredHeight, panel.minimumLength)); + } + } + + Item { + id: containmentParent + anchors { + fill: parent + leftMargin: root.margins.left + topMargin: root.margins.top + rightMargin: root.margins.right + bottomMargin: root.margins.bottom + } + } + + Connections { + target: panel + onXChanged: { + adjustBorders(); + } + onYChanged: { + adjustBorders(); + } + onWidthChanged: { + adjustBorders(); + } + onHeightChanged: { + adjustBorders(); + } + } + + Connections { + target: panel.screen + onGeometryChanged: { + adjustBorders(); + } + } + + Component.onCompleted: { + print("PanelView QML loaded") + adjustBorders(); + visible = true + } +} diff --git a/desktoppackage/metadata.desktop b/desktoppackage/metadata.desktop new file mode 100644 index 0000000000..8d6468dbe4 --- /dev/null +++ b/desktoppackage/metadata.desktop @@ -0,0 +1,131 @@ +[Desktop Entry] +Comment=Desktop view furniture +Comment[ca]=Accessori de vista d'escriptori +Comment[ca@valencia]=Accessori de vista d'escriptori +Comment[da]=Møbler til skrivebordsvisning +Comment[de]=Einrichtung der Ansicht einer Arbeitsfläche +Comment[el]=Παρουσίαση της επιφάνειας εργασίας +Comment[es]=Accesorio de vista del escritorio +Comment[fi]=Työpöydän sisustus +Comment[fr]=Aménagement du bureau +Comment[gl]=Contidos da vista do escritorio +Comment[hu]=Munkaasztal nézet bútor +Comment[ia]=Vista de accessorio de Scriptorio +Comment[kk]=Үстел көрініс әбзелдері +Comment[ko]=데스크톱 보기 가구 +Comment[nb]=Skrivebord, vis møbler +Comment[nds]=Möbelmang för Schriefdisch-Ansicht +Comment[nl]=Meubilair voor weergave op het bureaublad +Comment[pa]=ਡੈਸਕਟਾਪ ਵੇਖਣ ਫਰਨੀਚਰ +Comment[pl]=Widok umeblowania pulpitu +Comment[pt]=Disposição do ecrã +Comment[pt_BR]=Acessório de visualização da área de trabalho +Comment[sk]=Zobrazenie plochy nábytok +Comment[sl]=Delovna površina prikaza namizja +Comment[sr]=Намештај приказа површи +Comment[sr@ijekavian]=Намештај приказа површи +Comment[sr@ijekavianlatin]=Nameštaj prikaza površi +Comment[sr@latin]=Nameštaj prikaza površi +Comment[sv]=Visa skrivbordsmöbler +Comment[tr]=Masaüstü mobilya görünümü +Comment[uk]=Компоненти для перегляду стільниці +Comment[x-test]=xxDesktop view furniturexx +Comment[zh_TW]=桌面檢視設備 +Encoding=UTF-8 +Keywords= +Name=Desktop +Name[af]=Werkskerm +Name[ar]=سطح المكتب +Name[ast]=Escritoriu +Name[be]=Працоўны стол +Name[be@latin]=Rabočy stoł +Name[bg]=Работен плот +Name[bn]=ডেস্কটপ +Name[bn_IN]=ডেস্কটপ +Name[br]=Gorretaol +Name[ca]=Escriptori +Name[ca@valencia]=Escriptori +Name[cs]=Pracovní plocha +Name[csb]=Pùlt +Name[cy]=Penbwrdd +Name[da]=Skrivebord +Name[de]=Arbeitsfläche +Name[el]=Επιφάνεια εργασίας +Name[en_GB]=Desktop +Name[eo]=Labortablo +Name[es]=Escritorio +Name[et]=Töölaud +Name[eu]=Mahaigaina +Name[fa]=رومیزی +Name[fi]=Työpöytä +Name[fr]=Bureau +Name[fy]=Buroblêd +Name[ga]=Deasc +Name[gl]=Escritorio +Name[gu]=ડેસ્કટોપ +Name[he]=שולחן עבודה +Name[hi]=डेस्कटॉप +Name[hne]=डेस्कटाप +Name[hsb]=Dźěłowy powjerch +Name[hu]=Munkaasztal +Name[ia]=Scriptorio +Name[id]=Desktop +Name[it]=Desktop +Name[ka]=სამუშაო დაფა +Name[kk]=Жұмыс үстелі +Name[kn]=ಗಣಕತೆರೆ +Name[ko]=데스크톱 +Name[ku]=Sermasê +Name[lt]=Darbastalis +Name[mai]=डेस्कटाप +Name[mk]=Работна површина +Name[ml]=പണിയിടം +Name[mr]=डेस्कटॉप +Name[ms]=Ruang Kerja +Name[nb]=Skrivebord +Name[nds]=Schriefdisch +Name[ne]=डेस्कटप +Name[nl]=Bureaublad +Name[nn]=Skrivebord +Name[oc]=Burèu +Name[or]=ଡ଼େସ୍କଟପ +Name[pa]=ਡੈਸਕਟਾਪ +Name[pl]=Pulpit +Name[pt]=Ambiente de Trabalho +Name[pt_BR]=Área de trabalho +Name[ro]=Birou +Name[se]=Čállinbeavdi +Name[si]=වැඩතලය +Name[sk]=Plocha +Name[sl]=Namizje +Name[sr]=Површ +Name[sr@ijekavian]=Површ +Name[sr@ijekavianlatin]=Površ +Name[sr@latin]=Površ +Name[sv]=Skrivbord +Name[ta]=பணிமேசை +Name[te]=రంగస్ఠలం +Name[tg]=Мизи корӣ +Name[th]=พื้นที่ทำงาน +Name[tr]=Masaüstü +Name[uk]=Стільниця +Name[uz]=Ish stoli +Name[uz@cyrillic]=Иш столи +Name[vi]=Màn hình nền +Name[wa]=Sicribanne +Name[xh]=Desktop +Name[x-test]=xxDesktopxx +Name[zh_CN]=桌面 +Name[zh_TW]=桌面 +Type=Service + +X-KDE-ServiceTypes=Plasma/Shell +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-PluginInfo-Name=org.kde.desktop +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-Plasma-MainScript=layout.js diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000000..60a02b6f63 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(kcontrol) +add_subdirectory(kfontview) +add_subdirectory(plasma-desktop) diff --git a/doc/kcontrol/CMakeLists.txt b/doc/kcontrol/CMakeLists.txt new file mode 100644 index 0000000000..20c3b5bba9 --- /dev/null +++ b/doc/kcontrol/CMakeLists.txt @@ -0,0 +1,24 @@ +add_subdirectory(clock) +add_subdirectory(colors) +add_subdirectory(desktopthemedetails) +add_subdirectory(joystick) +add_subdirectory(kcmaccess) +add_subdirectory(kcmstyle) +add_subdirectory(solid-actions) +add_subdirectory(solid-hardware) +add_subdirectory(splashscreen) +add_subdirectory(powerdevil) + +if ( Q_WS_X11 ) +add_subdirectory(autostart) +add_subdirectory(bell) +add_subdirectory(cursortheme) +add_subdirectory(fonts) +add_subdirectory(fontinst) +add_subdirectory(keys) +add_subdirectory(keyboard) +add_subdirectory(mouse) +add_subdirectory(paths) +add_subdirectory(kcmsmserver) +add_subdirectory(khotkeys) +endif () diff --git a/doc/kcontrol/autostart/CMakeLists.txt b/doc/kcontrol/autostart/CMakeLists.txt new file mode 100644 index 0000000000..68faa5f00a --- /dev/null +++ b/doc/kcontrol/autostart/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/autostart) diff --git a/doc/kcontrol/autostart/index.docbook b/doc/kcontrol/autostart/index.docbook new file mode 100644 index 0000000000..c16f8a3d7c --- /dev/null +++ b/doc/kcontrol/autostart/index.docbook @@ -0,0 +1,183 @@ + + + +]> +
    + +Autostart + +&Anne-Marie.Mahfouf; + + + +2013-12-05 +&kde; 4.12 + + +KDE +System Settings +autostart +desktop file +script file + + + + +Autostart Manager + +This module is a configuration tool for managing what programs start up with your personal &kde;. It allows you to add programs or scripts so they automatically start during the startup of your &kde; session and to manage them. + +Please note that in this module all changes are immediately applied. + +The program scans ~/.config/autostart/, your KDE Autostart folder ($KDEHOME/Autostart as default), $KDEHOME/env and $KDEHOME/shutdown folders to check what programs and scripts are already there and displays them. It allows you to manage them easily. + + +Note that you can change the location of your Autostart +folder in Account Details Paths +in the Common Appearance and Behaviour category of the &systemsettings; and set a different folder +than $KDEHOME/Autostart. + + +Files display +The main part of the module displays the programs that are loaded when &kde; starts and scripts that are run when &kde; starts, shutdowns or before &kde; starts. + + +Name + + +You cannot amend this column. It states the name of the program or script you want to start with &kde;. The name is extracted from the Desktop file from the Name key for a program and is the filename for a script. + + + + +Command + + +This column lists the commands that are run to start the program. Commands can be modified through the Properties button or by double clicking the program/script row. The command is extracted from the Desktop file from the Exec key. + + +For a script the command is the path to the script. + + + + +Status + + +This setting is only available for programs through Desktop files. You can keep a program in the Autostart folder but disable it from being run at &kde; start. Setting the program to Disabled will not run it on start. + + +Setting a program to Disabled sets the Desktop file Hidden property to true in the Autostart folder. + + + + +Run On + + +Programs (Desktop files) can only be run on startup. Scripts can be run on Startup, Shutdown or Pre-KDE Startup. This column allows you to change when your script is run. Startup is when &kde; starts, shutdown is when you log out of &kde; and pre-KDE startup is before &kde; starts. + + +Scripts and desktop files set to run on Startup are copied or symlinked in $KDEHOME/Autostart and will be run during &kde; startup. + + +Scripts set on to be ran on Shutdown are copied or symlinked in the $KDEHOME/shutdown directory and will be automatically run during &kde; shutdown after the user has logged out. + + +Scripts set to run at Pre-&kde; Startup are copied or symlinked in $KDEHOME/env and are sourced during &kde; startup (the startkde script will look for scripts here). + + +Only scripts with the sh extension can be read by &kde; for Pre-&kde; startup and Shutdown modes. + + + + + + + +Actions + +On the right you have some buttons to change the way Autostart is configure. You can add programs or scripts, remove them or change their properties. + + + +Add Program + + +Clicking this button displays the standard &kde; Choose Application dialog and allows you to choose which program you want to start. After choosing the program, clicking OK brings you the properties for this program. + + +This will copy the program Desktop file in your Autostart folder. + + + + +Add Script + + +This button opens a dialog which asks you for the location of the script you want to add. If you keep Create as symlink checked (default) then the script will be added as a symlink. If you uncheck this option then the script will be copied in the corresponding local folder. + + + +Remove + + +Remove will immediately remove the Desktop file for the program or the script or symbolic link in the Autostart folder. + + + +Properties + + +This button (only enabled for programs &ie; Desktop files) allows you to change the properties of the program or script. You have general properties, permissions properties, a preview when applicable and properties related to the application for programs. + + + +Advanced + + +This button only applies to programs (&ie; Desktop files). A dialog asks you if you only want the program to autostart only in &kde; (and not in other desktop environments you might run). By default, the program will autostart in all desktop environments you might run. Checking Autostart only in KDE will autostart the program only if you start the &kde; desktop environment. + + +This sets the value KDE to the OnlyShowIn key of the program Desktop file. + + + + + + +
    + + + diff --git a/doc/kcontrol/bell/CMakeLists.txt b/doc/kcontrol/bell/CMakeLists.txt new file mode 100644 index 0000000000..a6defe0757 --- /dev/null +++ b/doc/kcontrol/bell/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/bell) diff --git a/doc/kcontrol/bell/index.docbook b/doc/kcontrol/bell/index.docbook new file mode 100644 index 0000000000..dc561b65b2 --- /dev/null +++ b/doc/kcontrol/bell/index.docbook @@ -0,0 +1,64 @@ + + + +]> + +
    +System Bell + + +PatDowler +MatthiasHoelzer +MikeMcBride + + + +2011-11-28 +&kde; 4.8 + + +The system bell or beep is a feature of the X server, which +attempts to make good use of the available hardware. + +&kde; normally does not use the system bell; instead using its +own system notifications, which could include log entries, message +popups, or its own beep. You can configure these in the +Manage Notifications &systemsettings; module. + +It is not always possible for the X server to actually make +a beep sound with exactly the parameters selected due to hardware +limitations. For example, on most PCs, volume control is not very good +so the X server seems to fake low volume with a reduced duration of +the sound. Thus, if the settings do not seem to do anything, this is +because the X server and/or the hardware do not support anything +better. + + Select Use system bell instead of system notification +then you are able to set the following parameters for the bell: + + + + +Volume (percentage of maximum volume) +Here you can customize the volume of the system bell. + + + +Pitch (in Hz) +Here you can customize the pitch of the system bell. + + + +Duration (in milliseconds) +Here you can customize the duration of the system bell. + + + + +For further customization of the bell, see the Accessibility &systemsettings; module. + +You can use the Test button to hear how +the current settings will sound. + +
    diff --git a/doc/kcontrol/clock/CMakeLists.txt b/doc/kcontrol/clock/CMakeLists.txt new file mode 100644 index 0000000000..80d65e48ad --- /dev/null +++ b/doc/kcontrol/clock/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/clock) diff --git a/doc/kcontrol/clock/index.docbook b/doc/kcontrol/clock/index.docbook new file mode 100644 index 0000000000..69afa0a8da --- /dev/null +++ b/doc/kcontrol/clock/index.docbook @@ -0,0 +1,90 @@ + + + +]> + +
    + +Date & Time + +&Mike.McBride; &Mike.McBride.mail; + + + +2013-12-05 +4.12 + + +KDE +Systemsettings +clock +date +time +set +configure + + + + + +Date & Time + +You can use this module to alter the system date and time, using a +convenient graphical interface. + +As these settings do not only affect you as a user, +but rather the whole system, you must have system administrator (root) access to change the system date and +time. If you do not have this access level, this module will only show +you the current settings, but your changes will not be saved. + + +Date and Time + +If you check Set date and time automatically you are able +to select a Time server from the drop down box and all other +settings in this dialog are disabled. + + +The applications rdate or ntpdate are used in +this module to fetch date and time from a time server. This happens when you log in to &kde; +while being online or when you later connect to the Internet and access to time +servers is possible. +Both applications are simple NTP clients that set a system's clock +to match the time obtained by communicating with one or more NTP servers. +This is not sufficient, however, for maintaining an accurate clock in the long run. +This is useful for occasionally setting the time on machines that do not have full-time +network access, such as laptops. + + +If you do not use a time server, you are able to set the date manually +using the lower half of the tab. Simply +choose your month and year using the controls at the top of the calendar +and the day of the month by clicking on the day in the +calendar. +Use the controls at the bottom of the calendar +to select the current date, enter the date in the edit box or select the +week of the year from the drop down box. + +You set the time using the spin boxes at the bottom of the analog clock. +You can also directly enter your value. + + + + +Time Zone +To set a new time zone, simply select your area from the list below. +Use the filter box to find the desired Area or Region. + + + + +
    diff --git a/doc/kcontrol/colors/CMakeLists.txt b/doc/kcontrol/colors/CMakeLists.txt new file mode 100644 index 0000000000..df4ece2e0d --- /dev/null +++ b/doc/kcontrol/colors/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/colors) diff --git a/doc/kcontrol/colors/index.docbook b/doc/kcontrol/colors/index.docbook new file mode 100644 index 0000000000..b1e99097ec --- /dev/null +++ b/doc/kcontrol/colors/index.docbook @@ -0,0 +1,399 @@ + + + + +]> + +
    + +Colors + +&Matthew.Woehlke; &Matthew.Woehlke.mail; + + + +2013-11-05 +4.12 + + +KDE +KControl +color +kcm + + + + +Colors + + + Introduction + + The Color Selection module is comprised of several sections: + + + + The Scheme tab, + used to manage schemes. + + + The Options tab, + used to change the options of the current scheme. + + + The Colors tab, + used to change the colors of the current scheme. + + + The state effects tabs + (Inactive, Disabled), used to + change the state effects of the current scheme. + + + + Note: This documentation will sometimes refer to the + "current" scheme, or the "active" scheme. + The "current" scheme is the set of colors and color scheme options + that was most recently applied, &ie; what you would get if you choose + Cancel. The "active" scheme is the set of + colors as has been most recently edited by you, &ie; what you would get if + you choose Apply. + + + + Scheme Management + + The Scheme tab lets you manage the color schemes + on your machine. Only one scheme is active at once, but you may save schemes, + allowing you to quickly change the scheme later. When you have created a + scheme you like, you can save it with Save Scheme.... + You can remove schemes using Remove Scheme. + Note that system schemes cannot be removed; trying to do so will display an + error. + + &kde; ships with several schemes. If you have an Internet connection, + you can also browse and retrieve user-created schemes using Get + New Schemes.... + + + Importing Schemes + + You can also import schemes that you have downloaded or otherwise + obtained, as well as import &kde; 3 schemes. &kde; 4 color schemes are + named like "*.colors", while &kde; 3 schemes are named like + "*.kcsrc". + + Because &kde; 4 has many more color roles than &kde;, importing a + &kde; 3 scheme is different from importing a &kde; 4 scheme. When a + &kde; 4 scheme is imported, it becomes part of your saved scheme + collection. When a &kde; 3 scheme is imported, the colors that exist in + &kde; 3 are overlaid onto the active scheme, and the result is not + automatically saved into your collection. After importing a &kde; 3 scheme, + you will likely need to adjust the colors that are new in &kde; 4, as well + as the state effects. For best results, before importing a &kde; 3 scheme, + select a similar &kde; 4 scheme (you do not need to apply the scheme before + importing). + + + + + Color Scheme Options + + The Options tab allows you to change some + properties that deal with how the color scheme is used, as well as some + options that change the color scheme that are different from actually + assigning colors. + + + Apply inactive window color effects + — If checked, state effects (see below) will be applied to inactive + windows. This can help visually identify active versus inactive windows, + and may have aesthetic value, depending on your taste. However, some + users feel that it causes distracting "flickering" since + windows must be repainted when they become inactive. Unlike desktop + effects, color state effects do not require compositing support and will + work on all systems, however they will only work on &kde; 4 applications. + + Inactive selection changes color + — If checked, the current selection in elements which do not have + input focus will be drawn using a different color. This can assist visual + identification of the element with input focus in some applications, + especially those which simultaneously display several lists. + + Shade sorted column in lists + — If checked, multi-column lists will use a slightly different + color to paint the column whose information is being used to sort the + items in the list. + + Apply colors to non-KDE4 applications + — If checked, &kde; will attempt to export its color scheme so that + non-&kde; 4 applications will use the same colors. Most applications will + honor the exported scheme to varying degrees, though some may have bugs + or otherwise not honor the exported scheme. + + Contrast + — This slider controls the contrast of shaded elements, such as + frame borders and the "3D" effects used by most styles. A lower + value gives less contrast and therefore softer edges, while a higher + value makes such edges "stand out" more. + + + + + + + Colors + + The Colors tab allows you to change the colors in + the active color scheme. + + Creating or changing a scheme is a simple matter of clicking on the + swatch in the color list and selecting a new color. You can check your + changes at any time by pressing Apply. It is suggested + that you save your scheme (via the Scheme tab) when you + are done. + + The Common Colors set, which is displayed + initially, is not actually a "set" in the sense used by &kde; (see + next section), but presents a number of color roles in a way that makes it + easier to edit the scheme as a whole. When creating a new color scheme, you + will usually change these colors first, and use the other sets to tweak + specific colors if needed. + + Note that Common Colors makes available roles from + all sets. For example, "View Background" here is shorthand for the + Normal Background role from the View set. Also, setting colors that do not + refer to a specific set will change that color in all + sets. (As an exception, "Inactive Text" will change the color for + all sets except for Selection; there is a separate + "Selection Inactive Text" for Inactive Text in the Selection set.) + Some roles may not be visible under Common Colors at + all, and can only be changed (if needed) by selecting the appropriate + set. + + + Color Sets + + &kde; 4 breaks the color scheme into several sets based on the type + of user interface element, as follows: + + View — + information presentation elements, such as lists, trees, text input boxes, etc. + + Window — + window elements that are not buttons or views. + + Button — + buttons and similar elements. + + Selection — + selected text and items. + + Tooltip — + tool tips, "What's This" tips, and similar elements. + + Window Manager — + window title bars and related elements. + + + + Each set contains a number of color roles. Except for the Window + Manager colors, each set has the same roles. All colors are associated with + one of the above sets. + + + + Color Roles + + Each color set is made up of a number of roles which are available in + all other sets. (The Window Manager set is an exception; it has its own + unique roles that do not exist in any other set, and does not use the same + roles as the other sets.) In addition to the obvious Normal Text and Normal + Background, these roles are as follows: + + + Alternate Background — + used when there is a need to subtly change the background to aid in + item association. This might be used ⪚ as the background of a + heading, but is mostly used for alternating rows in lists, especially + multi-column lists, to aid in visually tracking rows. + + Link Text — + used for hyperlinks or to otherwise indicate "something which may + be visited", or to show relationships. + + Visited Text — + used for "something (⪚ a hyperlink) that has been + visited", or to indicate something that is "old". + + Active Text — + used to indicate an active element or attract attention, ⪚ alerts, + notifications; also for hovered hyperlinks. + + Inactive Text — + used for text which should be unobtrusive, ⪚ comments, + "subtitles", unimportant information, etc. + + Negative Text — + used for errors, failure notices, notifications that an action may be + dangerous (⪚ unsafe web page or security context), etc. + + Neutral Text — + used to draw attention when another role is not appropriate; ⪚ + warnings, to indicate secure/encrypted content, etc. + + Positive Text — + used for success notices, to indicate trusted content, etc. + + + + As well as the text roles, there are a few additional + "decoration" roles that are used for drawing lines or shading + UI elements (while the above may, in appropriate circumstances, also be + used for this purpose, the following are specifically + not meant for drawing text). These are: + + + Focus Decoration — + used to indicate the item which has active input focus. + + Hover Decoration — + used for mouse-over effects, ⪚ the "illumination" effects for + buttons. + + + + In addition, except for Inactive Text, there is a corresponding + background role for each of the text roles. Currently (except for Normal + and Alternate Background), these colors are not chosen by the user, but are + automatically determined based on Normal Background and the corresponding + Text color. These colors may be previewed by selecting one of the sets + other than "Common Colors". + + The choice of color role is left to the developer; the above are + guidelines intended to represent typical usage. + + + + Window Manager Colors + + As previously stated, the Window Manager set has its own roles, + independent of those in other sets. These are (currently) only accessible + via Common Colors, and are as follows: + + + Active Titlebar — + used to draw the title bar background, borders, and/or decorations for + the active window (that is, the one with input focus). Not all window + decorations will use this in the same way, and some may even use the + Normal Background from the Window set to draw the title bar. + + Active Titlebar Text — + used to draw the title bar text when Active Titlebar is used to draw + the title bar background. May also be used for other foreground + elements which use Active Titlebar as the background. + + + + The Inactive Titlebar [Text] roles are the same as the above, but for + inactive windows, rather than active windows. + + + + + + Color State Effects + + Color state effects are applied to interface elements in the inactive + (windows that do not have focus; only if Apply inactive window + color effects is enabled) or disabled states. By changing the + effects, the appearance of elements in these states can be changed. Usually, + inactive elements will have reduced contrast (text fades slightly into the + background) and may have slightly reduced intensity, while disabled elements + will have strongly reduced contrast and are often notably darker or lighter. + + + Three types of effect may be applied to each state (with the effects + of the two states being independent). These are Intensity, Color and + Contrast. The first two (Intensity, Color) control the overall color, while + the last (Contrast) deals with the foreground colors relative to the + background. + + + Intensity + + Intensity allows the overall color to be lightened or darkened. + Setting the slider to the middle produces no change. The available effects + are: + + + Shade — + makes everything lighter or darker in a controlled manner. Each + "tick" on the slider increases or decreases the overall + intensity (&ie; perceived brightness) by an absolute amount. + + Darken — + changes the intensity to a percentage of the initial value. A slider + setting halfway between middle and maximum results in a color half as + intense as the original. The minimum gives a color twice as intense as + the original. + + Lighten — + conceptually the opposite of darken; lighten can be thought of as + working with "distance from white", where darken works with + "distance from black". The minimum is a color twice as + "far" from white as the original, while halfway between + middle and maximum gives an intensity halfway between the original + color and white. + + + + + + Color + + Color also changes the overall color, but is not limited to + intensity. The available effects are: + + + Desaturate — + changes the relative chroma. The middle setting produces no change; + maximum gives a gray whose perceptual intensity equals that of the + original color. Lower settings increase the chroma, giving a color that + is less gray / more "vibrant" than the original. + + Fade — + smoothly blends the original color into a reference color. The minimum + setting on the slider produces no change; maximum gives the reference + color. + + Tint — + similar to Fade, except that the color (hue and chroma) changes more + quickly while the intensity changes more slowly as the slider value is + increased. + + + + + + Contrast + + The contrast effects are similar to the color effects, except they + apply to the text, using the background color as the reference color, and + desaturate is not available. Fade produces text that "fades out" + more quickly, but keeps its color longer, while Tint produces text that + changes color to match the background more quickly while keeping a greater + intensity contrast for longer (where "longer" means higher + settings on the slider). For Contrast effects, the minimum setting on the + slider produces no change, while maximum causes the text to completely + disappear into the background. + + + + + + +
    + diff --git a/doc/kcontrol/cursortheme/CMakeLists.txt b/doc/kcontrol/cursortheme/CMakeLists.txt new file mode 100644 index 0000000000..3d3639a5c8 --- /dev/null +++ b/doc/kcontrol/cursortheme/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/cursortheme) diff --git a/doc/kcontrol/cursortheme/index.docbook b/doc/kcontrol/cursortheme/index.docbook new file mode 100644 index 0000000000..85c605757d --- /dev/null +++ b/doc/kcontrol/cursortheme/index.docbook @@ -0,0 +1,59 @@ + + + +]> + +
    + +Cursor Theme + +&Mike.McBride; &Mike.McBride.mail; +&Brad.Hards; &Brad.Hards.mail; + + + +2013-06-02 +&kde; 4.11 + + +This is the documentation for the &kde; &systemsettings; module to +customize the mouse cursor appearance. + + + + +KDE +Systemsettings +mouse +cursor + + + + +This module allows you to select from a number of installed cursor themes. +A preview of the cursor display is shown above the list box. + + +The features provided by this module may not be available on some systems. +Your system may need to be updated to support cursor themes. + + + +If you have additional cursor themes available, you can install and remove +them using the buttons right to the list box. Note that you cannot remove the default themes. + + +Use the Get New Themes button to launch the Get Hot New Stuff dialog and +download additional themes from the Internet. + +Some themes allow to +choose the cursor size using the combobox below to the label Size. + + + +The size can be set to the resolution dependent or one of the sizes available for your system. + + +
    diff --git a/doc/kcontrol/desktopthemedetails/CMakeLists.txt b/doc/kcontrol/desktopthemedetails/CMakeLists.txt new file mode 100644 index 0000000000..b74bf99563 --- /dev/null +++ b/doc/kcontrol/desktopthemedetails/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/desktopthemedetails) diff --git a/doc/kcontrol/desktopthemedetails/clicking-apply.png b/doc/kcontrol/desktopthemedetails/clicking-apply.png new file mode 100644 index 0000000000..f57723b4db Binary files /dev/null and b/doc/kcontrol/desktopthemedetails/clicking-apply.png differ diff --git a/doc/kcontrol/desktopthemedetails/customizing.png b/doc/kcontrol/desktopthemedetails/customizing.png new file mode 100644 index 0000000000..a16743fd96 Binary files /dev/null and b/doc/kcontrol/desktopthemedetails/customizing.png differ diff --git a/doc/kcontrol/desktopthemedetails/index.docbook b/doc/kcontrol/desktopthemedetails/index.docbook new file mode 100644 index 0000000000..19882edb2d --- /dev/null +++ b/doc/kcontrol/desktopthemedetails/index.docbook @@ -0,0 +1,182 @@ + + + +]> +
    + +Desktop Theme + + +Andrew +Lake + + + +2013-06-02 +&kde; 4.11 + + +KDE +System Settings +desktop +theme +plasma + + + + +Theme tab + +A list of available themes is displayed on this tab. Select a theme by clicking on an item in the list. + +Use the Get New Themes button to launch the Get Hot New Stuff dialog and +download additional themes from the Internet. + + + + +Details tab + +A &kde; plasma desktop theme is made up of several components or items, +consisting mostly in svg backgrounds and colors schemes. This module allows you +to customize your desktop Plasma theme and to replace theme items +with items from other themes or from files. + + +Desktop theme items consist in panel theme item, kickoff theme item, +analog clock, ... + + +If you want to change your current theme for a full new theme (for example from +Air to Glassified) then you can do so in the Theme tab. + + + +Quick start +First download one or several themes you want to use to customize your +desktop items by clicking the Get New Themes... +button in the Theme tab. + +All the available themes will be displayed on top, the current one being highlighted. + +In the theme list, select a base theme that will be used for most widgets and start +customizing each item you want to change with a new theme, for example you can +have the panel in Glassified theme, the widget +backgrounds in Air and so on. + +Here Air is the theme for most widgets and we changed the panel background +to Glassified, the Analog clock to Glassified and the Notes to Oxygen. + + + +Customizing your plasma theme + + + + + + Customizing your plasma theme + + + + + + +Clicking the Apply will create a new theme named +(Customized) and will apply it to your desktop. + + + + +Creating a custom theme +When the module is opened, it will show items from the current desktop +theme. If you would like to start customizing with a different theme, select it +from the box above the list of theme items. This theme from the box is the +starting theme, the base for your new customized desktop theme. + + +In the list of items, for each item you would like to replace, select +replacement item from the drop list for that item. You may select the +corresponding item from another theme, or you may select a file. &plasma; +desktop theme items are svg files, except for the color scheme which is a &kde; +color scheme file. + + + +Click Apply when all the changes have been made. This +will create a new theme named (Customized) which you can +select from the Appearance Settings dialog on the desktop. + + + + +Your custom theme + + + + + + Customized + + + + + + +The theme items listed are the most visible components of the plasma +desktop theme. Other theme components are not listed. Your newly created +theme will use these unlisted components from the theme that you selected at +the start of creating the customized theme. + + + + +Naming a custom theme + +To provide a name for your customized theme, click on the +More checkbox and +enter the name of the theme. You may also provide your name (author), a theme +version and a description of your theme. Click Apply +when done. + + + + +Removing a theme + +Select the theme you would like to remove from the box above the list of +theme items. Click on the More check box. Click the +Remove Theme button to +remove the selected theme. + + + + +Exporting a theme to a file + +You can share your newly customized theme with the community by exporting the +theme to a file. Select your customized theme from the box above the list +of theme items. Click on the More check box. Click the +Export Theme to +File.... Enter the name of the zip file to which the theme will be +saved. + + + + +
    + + diff --git a/doc/kcontrol/fontinst/CMakeLists.txt b/doc/kcontrol/fontinst/CMakeLists.txt new file mode 100644 index 0000000000..d21455a73d --- /dev/null +++ b/doc/kcontrol/fontinst/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/fontinst) diff --git a/doc/kcontrol/fontinst/index.docbook b/doc/kcontrol/fontinst/index.docbook new file mode 100644 index 0000000000..66a48c526d --- /dev/null +++ b/doc/kcontrol/fontinst/index.docbook @@ -0,0 +1,109 @@ + + + +]> + +
    +Font Management + + +&Craig.Drummond; &Craig.Drummond.Mail; + + + + +2013-06-19 +&kde; 4.11 + + +KDE +Systemsettings +fonts + + + + +Font Management + +This module is responsible for installing, un-installing, previewing and managing your fonts. + + +Font Groups +There are 4 special pre-defined font groups: + + + All Fonts This will display all fonts, both personal and system-wide. + Personal Fonts The fonts shown will be your personal fonts, and will not be available to other users. + System Fonts The fonts shown will be those available to all users. Installing a font system-wide, or removing a system-wide font, will require administrator privileges. + Unclassified This will list all fonts that have not been placed into any user-defined groups. This group will only appear if you have some user-defined groups. + +To add a font to a group, drag it from the list of fonts onto a group. To remove a font from a group, drag the font onto the All Fonts group. +Below this list you find buttons to create a new group, remove a group and enable or disable the fonts in the current group. +In the context menu of this list you have additional menuitems to print font samples and export a font to a zip archive. + + +Enabling and Disabling +Users with many fonts may find it useful to only have certain fonts enabled (or active) at certain times. To facilitate this, this module will allow you to disable individual fonts, or whole groups of fonts. Disabling a font does not remove the font from the system, it simply hides it so that it no longer appears within applications. Re-enabling a font will then allow it to be used. + + + + + +Font List + +The main display is a list of the installed fonts, grouped via the fonts' family name - the number in square brackets represents the number of installed styles for that family. e.g. The Times font may be listed as: + + + Times [4] + + Regular + Italic + Bold + Bold Italic + + + + + +To install a font, press the Add... button, and select the desired fonts from within the file dialog. The selected font group will control where the fonts will be installed. +To un-install fonts, select the appropriate fonts from the list, and press the Delete... button. + +Click with the &RMB; to open a context menu with some additional actions like Enable, Disable, Print, Open in Font Viewer and Reload. + + +Font Filtering +A text field on top of the font preview allows you to filter the list of fonts. You can filter fonts based upon different categories: + + Family. + Style. + Foundry. + FontConfig match. This allows you to enter a family name, and see the family that fontconfig would actually use. + Font file type. + Font file name. + Font file location. + Writing system. + + + + +Get New Fonts +New fonts may be installed from local files, or downloaded from kde-look.org. The Get New Fonts entry in the tool button (located above the group list), allows you to install fonts from kde-look.org. The fonts downloaded in this manner will be installed into your Personal Fonts group. To install system-wide, you will need to move them to the System Fonts group - this may be achieved by dragging the fonts over the System Fonts group entry and will require administrator privileges. + + + + + +Duplicate Fonts +If you have lots of fonts installed on your system it is possible that you may have duplicates. +Click on the tools icon above the group list and select the Scan For Duplicate Fonts... tool, a simple tool that will scan your system looking for fonts that have multiple files associated with them. For example, if you have times.ttf and times.TTF installed in /usr/local/share/fonts the underlying font mechanism (called FontConfig) will only see one of these. So, if you un-installed the font, it would re-appear, as only one of the files would have been removed. Running this tool will produce a dialog listing each font that has multiple files, and the corresponding list of files. To select a file for deletion, click on the column containing the trash can icon. + + + +Preview +This displays a preview text in different font sizes. +Using the context menu enables you to zoom in and out, select a preview type (Standard Preview or All Characters) and change the preview text. + + +
    diff --git a/doc/kcontrol/fonts/CMakeLists.txt b/doc/kcontrol/fonts/CMakeLists.txt new file mode 100644 index 0000000000..d09f4035a2 --- /dev/null +++ b/doc/kcontrol/fonts/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/fonts) diff --git a/doc/kcontrol/fonts/adjust-all.png b/doc/kcontrol/fonts/adjust-all.png new file mode 100644 index 0000000000..506a6179c8 Binary files /dev/null and b/doc/kcontrol/fonts/adjust-all.png differ diff --git a/doc/kcontrol/fonts/anti-aliasing.png b/doc/kcontrol/fonts/anti-aliasing.png new file mode 100644 index 0000000000..d88cacaed1 Binary files /dev/null and b/doc/kcontrol/fonts/anti-aliasing.png differ diff --git a/doc/kcontrol/fonts/index.docbook b/doc/kcontrol/fonts/index.docbook new file mode 100644 index 0000000000..b57c5f0199 --- /dev/null +++ b/doc/kcontrol/fonts/index.docbook @@ -0,0 +1,169 @@ + + + +]> + +
    + +Fonts + +&Mike.McBride; &Mike.McBride.mail; +&Anne-Marie.Mahfouf; &Anne-Marie.Mahfouf.mail; + + + +2013-12-05 +&kde; 4.12 + + +KDE +KControl +fonts + + + + + +Fonts + +This module is designed to allow you to easily select different +fonts for different parts of the &kde; Desktop. + + + +Here's a screenshot of the fonts settings module + + + + + + The fonts settings module + + + + + +The panel consists of different font groups to give you a lot of +flexibility in configuring your fonts: + + +General: Used everywhere when the other font +groups do not apply + +Fixed width: Anywhere a +non-proportional font is specified + +Small: When small fonts are used + +Toolbar: Font used in &kde; application +toolbars + +Menu: Font used in &kde; application +menus + +Window title: Font used in the window +title + +Taskbar: Font used in the taskbar +panel applet + +Desktop: Font used on the desktop +to label icons + + + +Each font has a corresponding Choose... +button. By clicking on this button, a dialog box appears. You can +use this dialog box to choose a new font, a font style and size. +Then press Ok. + +An example of the font you have chosen will be displayed in the space +between the font group name and the Choose... +button. + +When you are done, simply click OK and +all the necessary components of &kde; will be restarted so your changes +can take affect immediately. + +The Adjust All Fonts... button allows you to +quickly set properties for all the fonts selected +above. A font selection dialog similar to the standard one will +appear, but you will notice checkboxes that allow you to change the +Font, Font style or +Size independently of each other. You can +choose any one, two, or three of these options, and they will be +applied to all the font groups. + + + +Adjusting all fonts + + + + + + The Adjust All Fonts... dialog + + + + + +For example, if you have selected several different font faces +above, and realize they are all a size too big (this often happens +when you change screen resolution, for instance), you can apply a new +font size to all the fonts, without affecting your customized font +faces and styles. + + +Anti-aliasing text + +Default anti-aliasing settings are those used system wide by your distribution and labelled System Settings. You can disable all anti-aliasing by choosing Disabled in the drop down box. + +To use and set up anti-aliasing, simply choose Enabled from the box on the right of Use anti-aliasing:. The Configure... button will then get enabled and clicking on it will bring you a dialog to configure anti-aliasing. + + + +Anti-aliasing properties + + + + + + The anti-aliasing properties dialog + + + + + +Placing a mark in the Exclude range: checkbox will allow you to specify which range of fonts will not be anti-aliased. This range is specified +with the two combo boxes on the same line. + +You can also choose the method that &kde; uses to create an anti-alias +look to your fonts, and how strongly it should be applied. If you are not +familiar with the individual methods, you should leave this option +alone. + + + + +The ability to use anti-aliased fonts and icons requires that you have +support in both X and the &Qt; toolkit, that you have suitable fonts +installed, and that you are using the built-in font serving capabilities +of the X server. If you still are having problems, please contact the +appropriate &kde; mailing list, or check the +FAQ. + + + + +Fonts DPI + +Force fonts DPI: proposes you an alternate DPI other than your system one which is used as default when this setting is on Disabled. You can check what DPI your X server is set to by running xdpyinfo | grep resolution in a terminal window and then change the DPI using the drop down box. This will be applied to newly started applications only. + + + + +
    diff --git a/doc/kcontrol/fonts/main.png b/doc/kcontrol/fonts/main.png new file mode 100644 index 0000000000..2c0346c4a4 Binary files /dev/null and b/doc/kcontrol/fonts/main.png differ diff --git a/doc/kcontrol/joystick/CMakeLists.txt b/doc/kcontrol/joystick/CMakeLists.txt new file mode 100644 index 0000000000..5c2d007e3d --- /dev/null +++ b/doc/kcontrol/joystick/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/joystick) diff --git a/doc/kcontrol/joystick/index.docbook b/doc/kcontrol/joystick/index.docbook new file mode 100644 index 0000000000..1b89d72f09 --- /dev/null +++ b/doc/kcontrol/joystick/index.docbook @@ -0,0 +1,137 @@ + + + +]> +
    + +Joystick + + +Michael +Anderson + +
    nosrednaekim@gmail.com
    +
    +
    + +
    + +2013-12-05 +4.12 + + +KDE +System Settings +joystick +calibrate + +
    + + +Joystick configuration module + +This module provides an interface to calibrate your joystick device as well as +see which functions on your joystick are mapped to which logical button or +axis. + + + +Here's a screenshot of the joystick module + + + + + + The joystick module + + + + + + +Device + + +Here you can see the logical location of the device. If you have more +than one joystick device, you can also select which one to configure. + + + + +Position + + +The plotted location of the X and Y axes of the joystick. Enable Show trace +to plot the path of the joystick from one point to another. + + +If the joystick cannot reach every point on this graph, or if it does not center correctly, you need to calibrate your device. + + + + +Buttons + + +A place to see the pressed state of buttons on your joystick and if the buttons +are mapped correctly. When a joystick button is pressed, PRESSED will appear +next to the logical number (as the computer sees it) of the button which was +pressed. + + + + +Axes + + +This displays the numerical values of the axis positions. 0 is centered and the +maximum and minimum values depend on the joystick and the particular axis. + + + + +Calibrate + + +Clicking on Calibrate will open a dialog to calibrate every axis on your joystick. + + + + +Calibrating your joystick + + + + + + Calibrating your joystick + + + + + + +Each axis will have to be moved to the minimum position, then the center, and finally the +maximum position. A meter in the bottom left corner of the dialog will help +determine which way increases and which way decreases the values. + + + + + +
    + + diff --git a/doc/kcontrol/joystick/joystick-calibration.png b/doc/kcontrol/joystick/joystick-calibration.png new file mode 100644 index 0000000000..5ff3bda13c Binary files /dev/null and b/doc/kcontrol/joystick/joystick-calibration.png differ diff --git a/doc/kcontrol/joystick/joystick-main.png b/doc/kcontrol/joystick/joystick-main.png new file mode 100644 index 0000000000..f5a8071f42 Binary files /dev/null and b/doc/kcontrol/joystick/joystick-main.png differ diff --git a/doc/kcontrol/kcmaccess/CMakeLists.txt b/doc/kcontrol/kcmaccess/CMakeLists.txt new file mode 100644 index 0000000000..22beb92d21 --- /dev/null +++ b/doc/kcontrol/kcmaccess/CMakeLists.txt @@ -0,0 +1,4 @@ +########### install files ############### +# +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/kcmaccess) diff --git a/doc/kcontrol/kcmaccess/index.docbook b/doc/kcontrol/kcmaccess/index.docbook new file mode 100644 index 0000000000..27d5188099 --- /dev/null +++ b/doc/kcontrol/kcmaccess/index.docbook @@ -0,0 +1,264 @@ + + + +]> + +
    +Accessibility + + +&Mike.McBride; &Mike.McBride.mail; + + + +2013-12-05 +&kde; 4.12 + + +KDE +Systemsettings +accessibility +keys +bell + + + + +Accessibility + + +Introduction + +This module is designed to help users who have difficulty hearing +audible cues, or who have difficulty using a keyboard. + + +The module is divided into four tabs: Bell, Modifier keys, Keyboard Filters and Activation Gestures. + + + +<guilabel>Bell</guilabel> + + +This panel is divided into an Audible Bell section +and a Visible Bell section. + + + +The top check box labeled Use System Bell, determines +whether the normal System bell rings. If this option is disabled, the +System bell will be silenced. + + + +The next check box down can be used to play a different sound whenever +the system bell is triggered. To activate, place a mark in the check +box labeled Use customized bell, and enter the +complete pathname to the sound file in the text box labeled +Sound to play. If you want, you can select the +Browse button to navigate through your filesystem +to find the exact file. + + + +For those users who have difficulty hearing the System bell, or those +users who have a silent computer, &kde; offers the visible bell. This +provides a visual signal (inverting the screen or flashing a color +across it) when the system bell would normally sound. + + + +To use the visible bell, first place a mark in the check box labeled +Use visible bell. + + + +You can then select between Invert screen, or +Flash screen. If you select Invert +screen, all colors on the screen will be reversed. If you +choose Flash screen, you can choose the color by +clicking the button to the right of the Flash +screen selection. + + + +The slider bar can be used to adjust the duration of the visible +bell. The default value is 500ms, or half a second. + + + + + +<guilabel>Modifier keys</guilabel> + +There are the two sections Sticky Keys +and Locking Keys to this panel. + + + +Use Sticky Keys + + +If this option is enabled, you can press and release the &Shift;, +&Alt; or &Ctrl; keys, and then press another key to get a key combo +(example: &Ctrl; &Alt; +Del could be done with &Ctrl; then &Alt; +then Del). + + + +Also in this section is a check box labeled Lock Sticky +Keys. If this check box is enabled, the &Alt;, &Ctrl; +and &Shift; keys stay selected until you +de-selected them. + + + +As an example: + + + +With Lock Sticky Keys disabled: + +If you press the &Shift; key then press the +F key, the computer interprets this as &Shift;F. Now if you +type a P, the computer interprets this as the letter p +(no shift). + + + + + +With Lock Sticky Keys enabled: + + +If you press the &Shift; key twice then press the F key, +the computer interprets this as &Shift;F. Now if you +type a P, the computer interprets this as the letter P +(&Shift;P). To +de-select the &Shift; key, press it again. + + + + + + + + + + + + + +<guilabel>Keyboard Filters</guilabel> + +There are two sections to this panel. + + + +Use slow keys + + +If this option is enabled, you must hold the key down for a +specified length of time (adjustable with the slider) before the +keystroke will be accepted. This helps prevent accidental key strokes. + + + + + +Use bounce keys + + +If this option is enabled, you must wait for a specified length of time +(configurable with the slider) before the next key press can be +accepted. This prevents accidental multiple key strokes. + + + + + + + + + + +<guilabel>Activation Gestures</guilabel> + +There are two sections to this panel. + + + +Activation Gestures with these options: + + + +Use gestures for activating sticky keys and slow keys +Here you can activate keyboard gestures that turn on the following features: +Mouse Keys: Press &Shift;+NumLock +Sticky keys: Press &Shift; key 5 consecutive times +Slow keys: Hold down &Shift; for 8 seconds +Turn sticky keys and slow keys off after a certain period of inactivity + + + + + + +Notification with these options: + + + +Use the system bell whenever a gesture is used to turn an accessibility +feature on or off + +Show a confirmation dialog whenever a keyboard accessibility feature is +turned on or off +If this option is checked, &kde; will show a confirmation dialog whenever a +keyboard accessibility feature is turned on or off. +Ensure you know what you are doing if you uncheck it, as the keyboard +accessibility settings will then always be applied without confirmation. + +Use &kde;'s system notification mechanism whenever a keyboard accessibility +feature is turned on or off + + + + + +Click the Configure Notifications button to open a dialog +which allows you to edit the notifications for status changes of all keys. + + + + + + +
    diff --git a/doc/kcontrol/kcmsmserver/CMakeLists.txt b/doc/kcontrol/kcmsmserver/CMakeLists.txt new file mode 100644 index 0000000000..68189ccdbc --- /dev/null +++ b/doc/kcontrol/kcmsmserver/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +#KDE_LANG = en +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/kcmsmserver) diff --git a/doc/kcontrol/kcmsmserver/index.docbook b/doc/kcontrol/kcmsmserver/index.docbook new file mode 100644 index 0000000000..fe580c36af --- /dev/null +++ b/doc/kcontrol/kcmsmserver/index.docbook @@ -0,0 +1,122 @@ + + + +]> + +
    + +Session Management + +&Jost.Schenck; &Jost.Schenck.mail; + + + +2013-12-05 +&kde; 4.12 + + +KDE +KControl +session +System Settings + + + + +Session Management + + +Use + +In this &systemsettings; module you can configure &kde;'s session +manager and logout settings. + +Session management refers to &kde;'s ability to save the state +of applications and windows when you log out of &kde; and restore them +when you log back in. + + +General + + +Confirm logout + +If this option is checked, when logging out, &kde; will display a +dialog asking for confirmation. In this dialog you can also choose +whether you want to restore your current session when you login the next +time. + + + +Offer shutdown options + +If this option is checked, &kde; will offer a choice of actions during +logout. These include simply ending the current session (the +action performed if shutdown options are not enabled) turning off the +computer, or restarting the computer. + + + + + + +Default Leave Option +You can configure what should happen by default when you log out of +&kde;. These options are not possible on all operating systems, and some of +them may not appear unless you are using &kdm; as your login manager. + +The options available are self explanatory, if you are in doubt, +leave the default settings. They are: + + +End current session (this is the default) +Turn off computer +Restart computer + + + + +On Login +You may choose one of three options on what should happen when +you log into &kde;: + + + +Restore previous session +If this option is checked, &kde; will save your current +session's state when you logout. &kde; will restore your session on the +next login, so you can continue to work with a desktop just like you +left it. + + + +Restore manually saved session +Instead of restoring &kde; to the state it was when +you logged out last, it will be restored to a specific state that you +have saved manually. +If this option is checked, the start menu offers an additional item +LeaveSave Session. + + + +Start with an empty session + +If you choose this option, &kde; will never restore sessions that it has saved. + + + +Finally, you can enter a colon (:) or comma (,) +separated list of applications that should not be saved in sessions, +and therefore will not be started when restoring a session. For example +xterm:konsole or xterm,konsole. + + + + + + + +
    + diff --git a/doc/kcontrol/kcmstyle/CMakeLists.txt b/doc/kcontrol/kcmstyle/CMakeLists.txt new file mode 100644 index 0000000000..47ab4e8b0c --- /dev/null +++ b/doc/kcontrol/kcmstyle/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/kcmstyle) diff --git a/doc/kcontrol/kcmstyle/index.docbook b/doc/kcontrol/kcmstyle/index.docbook new file mode 100644 index 0000000000..a0b456b5ac --- /dev/null +++ b/doc/kcontrol/kcmstyle/index.docbook @@ -0,0 +1,238 @@ + + + +]> + +
    + + +Style + +&Mike.McBride; &Mike.McBride.mail; + + + +2013-06-02 +&kde; 4.11 + + +KDE +System Settings +style + + + +Style + + +Introduction + +This module is used to configure how the individual widgets are +drawn by &kde;. + +A Widget is a commonly-used +programmer's term for referring to User Interface elements such as +buttons, menus, and scroll bars. You can think of them as the +fundamental pieces that are assembled to make your +application. + +You can configure how the widgets are drawn with this module, +but to change the color of the widgets, you should refer to the +section entitled Colors. + +This panel is divided into two tabs: Applications +and Fine Tuning. + + +<guilabel>Applications</guilabel> tab + +The top drop down box, labeled Widget Style +contains a list of the pre-defined styles. +Each style has a name, and a brief description. + +To change styles, simply click on the style name, and a preview +of the style will be displayed in the preview box below the style list. + + If a style is configurable, the Configure... button +at the right side of the drop down box is enabled and can be used to open +a dialog to select further settings. + + + + + + +<guilabel>Fine Tuning</guilabel> tab + + + + +Graphical effects + +&kde; applications will run internal animations with the selected display resolution and CPU usage. + + + + +Show icons on buttons + +If this option is selected, action buttons (like OK and +Apply) will have a small icon located within them to act +as a visual reference. If this option is not selected, then only text +will appear on the button. + + + + +Show icons in menus + +If this option is selected, &kde; applications will show small icons alongside +most menu items. If this option is not selected, then only text +will appear in the menus. Changes to the visibility of menu icons will only affect newly started +applications. + + + + +Main toolbar text, Secondary toolbar text + +These drop down boxes lets you determine where on the button in both toolbars the +text name of the button will appear as the default. +If No Text is selected, then there is no text on the toolbar buttons. +If Text Only is selected, then the button's icon is replaced with a text name of +the button. If Text Beside Icons is selected, then the name of the button +will be placed to the right of the icon. +If Text Below Icons is selected, the default will be to have the text +of the button below the icon. + +This option only specifies the default location. +Each application can override the setting used in this panel. + + + + +Menubar style + +In application: The classic menubar at the top of the application window. +Title bar button: The window has an additional menu button top left. +All changes will take effect only for newly started applications. +Top screen menubar: This will turn on a menubar at the top of the screen that +is hidden by default and revealed on mouse over or a menu button on the window decoration. +This menubar will reflect the menu options of the active application. +Only export: for use by plasma runners + applets. +To use this feature you need a KRunner plugin like Appmenu action launcher. +Select an application, press &Alt;F2 +and type the menu action you want to perform. + + + + + + + + + + + +
    diff --git a/doc/kcontrol/keyboard/CMakeLists.txt b/doc/kcontrol/keyboard/CMakeLists.txt new file mode 100644 index 0000000000..deb161aec2 --- /dev/null +++ b/doc/kcontrol/keyboard/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/keyboard) diff --git a/doc/kcontrol/keyboard/index.docbook b/doc/kcontrol/keyboard/index.docbook new file mode 100644 index 0000000000..d737994415 --- /dev/null +++ b/doc/kcontrol/keyboard/index.docbook @@ -0,0 +1,218 @@ + + + +]> + +
    +Keyboard + + + +&Mike.McBride; &Mike.McBride.mail; +&Anne-Marie.Mahfouf; &Anne-Marie.Mahfouf.mail; + + + +2013-12-05 +&kde; 4.12 + + +KDE +System Settings +keyboard + + + +This module allows you to choose how your keyboard works. + +There are three tabs in this module. + + +The Hardware tab +The actual effect of setting these options depends upon the +features provided by your keyboard hardware and the X server on which +&kde; is running. As an example, you may find that changing the key +click volume has no effect because that feature is not available on your +system. + + + +Keyboard model: + + +Here you can set your keyboard model. This setting is independent of your +keyboard layout and refers to the "hardware" model, i.e. the way your +keyboard is manufactured. Modern keyboards that come with your computer usually +have two extra keys and are referred to as "104-key" models, which is +probably what you want if you do not know what kind of keyboard you have. + + + + + +NumLock on KDE Startup + +You can choose to either always Turn on or +Turn off the NumLock when &kde; starts, or you can +choose to have &kde; leave NumLock at whatever it was set to before &kde; +started up. + + + + +Keyboard Repeat + +When this option is turned on, pressing and holding down a key +emits the same character repeatedly until the key is released. Pressing +and holding the key will have the same effect as pressing it multiple +times in succession. + Almost all users will want to have this option enabled, because it +makes navigating through documents with the arrow keys significantly +easier. + + + +Delay + +This option allows you to set the delay after which a pressed +key will start generating keycodes. + + + +Rate + +This option allows you to set the rate at which keycodes are +generated while a key is pressed. + + + +Key click volume + +If supported, this option allows you to hear audible clicks from +your computer's speakers when you press the keys on your keyboard. In +essence, this simulates the click of a mechanical +type-writer. You can change the loudness of the key click feedback by +dragging the slider button. Setting the volume to 0% by moving the slider +to the left turns off the key click. +Type some characters into the Test area text box to +verify the volume you selected. +Many computers won't support this function. +Very few people would choose to enable this option, since it +generally annoys everyone else in the room. However, if your heart +yearns for the pre-soft-key era, this may help you to re-experience the +warm sentimentality of days-gone-by. + + + + + + + + + + + +The Layouts tab +This tab lets you choose what keyboard layouts you want to use, what +indicator you want in the system tray, how you want to switch and which +shortcuts you will use. + + + +Layout Indicator + +You can choose to Show layout indicator +which will show the language iso name in the system tray. You can choose to +show the indicator even if you have a single layout enabled +and you can show the language flag instead of only the ISO code by checking +Show flag. + + + + +Switching Policy + +If you select Application or +Window switching policy, changing the keyboard layout will +only affect the current application or window. Global will +change the keyboard layout globally and Desktop will change +it only for the current virtual desktop. + + + + + +Shortcuts for Switching Layout + +Main shortcuts is a shortcut for switching +layouts which is handled by X.org. 3rd level shortcuts is a +shortcut for switching to a third level of the active layout (if it has one) +which is handled by X.org. It allows modifier-only shortcuts. +Alternative shortcut is a shortcut for switching layouts +which is handled by &kde;. It does not support modifier-only shortcuts and also +may not work in some situations (⪚ if popup is active or from screensaver). + + + + + +Configure layouts + +If you check Configure layouts you will be able to +add and remove keyboard layouts in order to switch between them when you need. + +Clicking on Add will open a dialog where you +will be able to choose the new keyboard layout you want to add as well as its +variant, label and shortcut. + + +The list of layouts right to the Layout label in the +Add Layout dialog can be narrowed down using +Limit selection by language drop-down list. You +can click on Preview button to preview chosen layout +in a separate window. + +You can order the layouts with the up and down buttons, the top one being +the default one. + + + + +Spare layouts + +This option can be enabled only if you have more than two layouts. +Spare layouts allow to toggle between small number of layouts easily +while keeping more layouts handy close by. For example you might use 3 +languages: English, Ukrainian and German but first two are used often +and third one just occasionally. In this case you can configure first +two as main layouts (setting Main layout count: 2) +and German one as spare one - when you toggle with +keyboard and left mouse button the switch will happen between main +layouts only but you can always choose 3rd layout with context menu. +When spare layout is chosen it replaces the last main layout (this is +done for technical reasons, usually global shortcuts work based on the +1st active layout so it's more reliable to preserve the 1st layout when +allowing to activate spare layouts). + +As X.org only allows to have 4 layouts to switch spare layouts feature +also allows to overcome this limitation: user can have up to 4 main +layouts (managed by X.org) but 4 more spare layouts allowed (managed by +layout switcher). Thus user can conveniently work with up to total of 8 +layouts (although the 4 spare layouts limit can be raised in the future). + + + + + + + + +The Advanced tab +In this tab, if you check Configure keyboard options +you will then be able to set a few options regarding some keys settings. You can +expand these options by clicking on the > symbol on the left of the option +then you choose your setting in the expanded list. + +
    diff --git a/doc/kcontrol/keys/CMakeLists.txt b/doc/kcontrol/keys/CMakeLists.txt new file mode 100644 index 0000000000..2efb379dce --- /dev/null +++ b/doc/kcontrol/keys/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/keys) diff --git a/doc/kcontrol/keys/index.docbook b/doc/kcontrol/keys/index.docbook new file mode 100644 index 0000000000..f634c939b8 --- /dev/null +++ b/doc/kcontrol/keys/index.docbook @@ -0,0 +1,202 @@ + + + +]> + +
    +Shortcuts + + +&Mike.McBride; &Mike.McBride.mail; +&Jost.Schenck; &Jost.Schenck.mail; + + + +2013-12-05 +&kde; 4.12 + + +KDE +KControl +key bindings +bindings +shortcuts + + + + +Shortcuts + + +Introduction + +While most of the functionality offered by &kde; can be accessed +using a simple point and click interface, many people +prefer using the keyboard for some tasks. Pressing something like + &Ctrl;F is often +just faster than moving your hands off the keyboard to the mouse, +opening the Edit menu and selecting +Find. + + As different people have different preferences about keyboard +shortcuts, &kde; offers full customization of key +bindings. A key binding or shortcut is a combination of an +action with a key or a combination of keys. + + + + +Standard Shortcuts and Global Shortcuts + +Standard shortcuts and Global shortcuts work just the same. +Actually, in a certain way standard or application shortcuts are +global as well. The only +difference is: + + +Standard keyboard shortcuts refer to actions +that are often available in applications, such as Save, Print, Copy +&etc; +Global keyboard shortcuts are shortcuts for +actions that make sense even when no application is opened. These +shortcuts usually refer to actions like switching desktops, manipulating +windows &etc; + + +Please note, that the application shortcuts configured here are +only the standard actions often found in +applications. Most applications will define their own actions as well, +for which you have to customize key bindings using the application's +key bindings dialog. + + +Standard Keyboard Shortcuts page +At the top of this dialog you see an input box, where you can +search interactively for shortcut names (⪚ Copy) or combination of keys +(⪚ Ctrl+C) by typing them literally here. +Below the search box you can see a list of key bindings, &ie; associations between actions +(⪚ Copy) shown in the Actions column and keys or combination of keys +(⪚ &Ctrl;C ) shown in the +Shortcut or Alternate column. + + + + + +Global Keyboard Shortcuts page +At the top of this page is a drop down box which allows you +to select a KDE component like KWin, Plasma Desktop &etc; +The defined shortcuts for the selected component are displayed in the +Action and Global column in the list view. +Click the File drop down box at the right to import/export a +Scheme, set all shortcuts to none and remove a component. +This page has the same search box as the Standard Keyboard +Shortcuts tab. + + + + +Configuring Key Bindings + +Configuring key bindings is pretty easy. In the middle of +these &systemsettings; modules you will find a list of available +actions. If there's a key binding configured for that action you will +find it in the columns right to it. Just select the action you want to +configure. + +After you have selected an action in the list you will notice that +this item is highlighted and you see two additional check boxes below +this item in the list view. There you can configure a +combination of keys or maybe no key binding at all for the selected +action. + + + +Default: the selected action +will be associated with &kde;'s default value. This is a good choice +for most actions, as &kde; comes with reasonable key bindings we have +thought about. + +Custom: if this option is +enabled, you can create a key combination for the selected +action. Click on the button labeled either None +or labeled with the previously selected custom shortcut. Now the button label +changes to Input. Then press any modifier key +(&ie; &Shift;, &Ctrl;, or &Alt;) and then normal key or a function key (⪚ F11) +you want to assign to this key combination. +Clicking on the Alternate column of a row in the list +allows to edit the second shortcut for the action. + + + + + + + + +
    diff --git a/doc/kcontrol/khotkeys/CMakeLists.txt b/doc/kcontrol/khotkeys/CMakeLists.txt new file mode 100644 index 0000000000..3873b3657e --- /dev/null +++ b/doc/kcontrol/khotkeys/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/khotkeys) diff --git a/doc/kcontrol/khotkeys/groups-comment.png b/doc/kcontrol/khotkeys/groups-comment.png new file mode 100644 index 0000000000..8ef80c3ad0 Binary files /dev/null and b/doc/kcontrol/khotkeys/groups-comment.png differ diff --git a/doc/kcontrol/khotkeys/index.docbook b/doc/kcontrol/khotkeys/index.docbook new file mode 100644 index 0000000000..17df861fef --- /dev/null +++ b/doc/kcontrol/khotkeys/index.docbook @@ -0,0 +1,751 @@ + + + +]> +
    +Custom Shortcuts + + + +Subhashish Pradhan +&TC.Hollingsworth; &TC.Hollingsworth.mail; + + + + +2011 +Subhashish Pradhan + + +2012 +&TC.Hollingsworth; + +&FDLNotice; + +2013-12-05 +&kde; 4.12 + + +KDE +Systemsettings +shortcuts +hotkeys +Custom Shortcuts +khotkeys + + + + +Introduction + +The Custom Shortcuts &systemsettings; module +allows you to configure custom keyboard shortcuts and mouse gestures. You can +use them to control applications, run specific commands, and more. + + + + + +Managing Shortcuts and Groups + + +Adding Groups + +The module allows to you to sort related shortcuts into Groups. For +instance, if you have some shortcuts that relate to your music player, you +could create a group for them called Music Player. + + +To add a new group, click on the Edit button below +the left pane and select the New Group option. + + + + +Adding Shortcuts + +To add a new shortcut, click on the Edit button +below the left pane and select the New option. + +The first menu that appears allows you to select the type of trigger. The +following options are available: + + + + +Global Shortcut +These are standard keyboard shortcuts that will be recognized +anywhere, as long as a &kde; &plasma; Workspace is running. + + + +Window Action +Window Actions are triggers that occur when something happens +to a particular window, like when it appears, gains focus, or is closed. + + + + +Mouse Gesture Action +A mouse gesture action is triggered when a particular movement +of the mouse (or touchpad or touchscreen) is performed. + + + + +Once you've selected the type of trigger, another submenu appears that allows +you to select an action type. The following types are available: + + + + +Command/&URL; +This action will launch a command or open a &URL; when the +shortcut is triggered. + + + +&DBus; Command +This action will call a &DBus; method in a running application or +system daemon. For more information on &DBus;, see the +&DBus; +Introduction on &kde; TechBase. + + + +Send Keyboard Input +This action will send keyboard input to the currently +running application, just as if you had typed it in yourself. + + + + +Once you've selected the action type, you can then edit the shortcut to +your liking. See for more information. + + + + +Deleting Shortcuts and Groups + +To delete a shortcut or group, select it, then click on the +Edit button below the left pane, and select the +Delete option. + + + + +Exporting Groups + +You can export a group, so you can save the shortcuts contained within and +use them on another computer or maintain a backup. + +To export a group, select the group to be exported, then click on the +Edit button below the left pane and select the +Export Group... option, which opens a new window that +lets you define some options about the exported group. The following options +are available: + + + + +Export Actions +This allows you to select the state the shortcuts will be in +when they are imported later. Select Actual State to +maintain their current state, Enabled to ensure they are +all enabled, or Disabled to ensure they are all disabled. + + + + +Id +Here you may enter text to identify the group. If the group is +included by default, this text may be filled in by default. + + + +Allow Merging +This defines what happens if the group already exists on the +system the exported group is imported to. When enabled, any new actions will +be added to the group on the target system, while actions that have the same +name but a different configuration will updated with the configuration from the +imported file. When disabled, the module will refuse to import the file. + + + + +Filename +Here you may enter the filename you wish to export the +shortcut to. You may also select the Browse button to +the right of the text box to open the File dialog and select a file from there. + + +Exported files use the extension .khotkeys by +default. + + + + + + +Exporting a Group + + +The Export Group dialog. +Exporting a group of shortcuts. + + + + + + +Importing Groups + +To import a group, click the Edit button below the +left pane and select Import.... A file selection dialog +opens that permits you to locate a file previously created using the Export +function. + + + + + + + +Modifying Groups + +In the left section of the window, the default shortcuts are categorized +into groups. These groups can be expanded by clicking the arrow beside them to +reveal the shortcuts. + +When you click on a group, you are presented with two tabs to configure +the group itself. The Comment allows you to store notes +about the group, and are not actually used by the system for anything. The +Conditions tab allows you to restrict which windows a group +of shortcuts works with. + + +The Comment Tab + + +The Comment tab in a group. +Modifying a group's comment. + + + +Conditions are displayed in a tree, the top level of which is +And. All conditions beneath And must +be satisfied for shortcuts in the group to be triggered. + +You may add additional groups of conditions by clicking the +New drop down box to the right of the tree of conditions. Types +of groups include the aforementioned And, +Or, in which only one member of the group must be +satisfied, or Not, in which the inverse of all +members will trigger the shortcuts in the group. + +To add a window definition to the list, press the New +button. You may select Active Window... if you want +the shortcut to be triggered only if the specified window currently has focus, +or you may select Existing Window... if you want the +shortcut to be triggered as long as that window is open , regardless of +whether or not you are using it. Selecting either of these options opens a +window where you can edit the window definitions. + +Click the Edit... button to edit an existing set +of window definitions. A window opens with the window definition editor. For +more information, see . + +To remove a window definition from the conditions list, click the +Delete button. + + + + + +Modifying Shortcuts + +In the left section of the window, the default shortcuts are categorized +into groups. These groups can be expanded by clicking the arrow beside them to +reveal the shortcuts. + +The Preset Actions group, when expanded, reveals a +shortcut configuration named PrintScreen which, when +selected, reveals section on the right side, with three tabs: + + +The Comment Tab + +The Comment tab allows you to describe how to use the +shortcut, what it does, or anything else you might want to include. + + + + +The Trigger Tab +The Trigger tab contains the trigger configuration, +which depends on the type of trigger specified: + + + + +Global (Keyboard) Shortcut + + +To modify a keyboard shortcut, click on the button that contains a wrench, +and then enter the desired keyboard shortcut. To erase the shortcut, click on +the button with the icon to the right of the +change shortcut button. + + +Modifying a Keyboard Trigger + + +The Trigger tab for a keyboard shortcut. +Modifying a trigger for a keyboard shortcut. + + + + + + + +Window Action + + +Window actions contain several options: + + + + +Trigger When + + +This configures the particular window action that must occur for the +shortcut to be triggered. The following options are available: + + + +Window appears - +Triggered when a window is opened. + +Window disappears - +Triggered when a window is closed. + +Window gets focus - +Triggered when you switch to a window. + +Window loses focus - +Triggered when you switch away from a window. + + + + + + + +Window +This is where you define the actual window or windows the trigger applies +to. For more information, see + + + + + + + + +Mouse Gesture + + +A mouse gesture can be changed by clicking the Edit +button below the area that displays the mouse gesture, which will open a window. +Hold down the &LMB; and draw the desired mouse gesture in the area provided. +The gesture will be saved when you release the &LMB;. + + +Modifying a Mouse Gesture Trigger + + +The Trigger tab for a mouse gesture shortcut. +Modifying a trigger for a mouse gesture. + + + + + + + + + + + +The Action Tab + +The Action tab is where you configure the action that +will be performed when the shortcut is triggered. There are several types of +actions that have different configuration options: + + + + +Command/&URL; + + +When using a Command or &URL; is a trigger, a text box is provided where +you may enter the command to run or &URL; to open when the shortcut is triggered. +You may also click the Browse button to the right of the +text box to open a file selection dialog in which you can select a file on your +local or a remote system. + + +Modifying a Command Action + + +The Action tab for a command. +Modifying a command action. + + + + + + + +&DBus; Command + + +The following options are provided that allow you to specify a &DBus; +method to be performed: + + + + +Remote application +The service name of the remote application the method is to +be performed on, for instance org.kde.kate if you wanted +to perform it on the &kate; Advanced Text Editor. + + + +Remote object +The path to the remote object the method is to be performed on, +for instance /Document/1, if you wanted to perform it on +the first document opened in &kate;. + + + +Function +The name of the &DBus; method to be called, for instance +print if you wanted to print the document. + + + + +For more information on &DBus;, see the +&DBus; +Introduction on &kde; TechBase. + + + + + +Send Keyboard Input + + +At the top of the Action tab, there is a large text +entry where you may enter the keystrokes you wish to be sent when the shortcut +is triggered. + +Most keys contain a single character, and to enter them here you just +enter that character. For instance, to type an A, just enter +A. Some keys have longer names, and you can also use +those names. For instance, to press the &Alt; key, simply enter +Alt. + + +Individual keystrokes should be separated by a colon +(:). For instance, to type foo, enter +F:O:O. + +Keys that need to pressed at the same time should be separated by a plus +sign. For instance, to press +&Ctrl;C, enter +Ctrl+C. + + +Remember, you must enter keystrokes here exactly as you would on a keyboard. +To capitalize letters, you must enter the &Shift; key. For instance, to type +Hello, enter Shift+H:E:L:L:O. + +This also applies to special characters. For instance, to type the +at-sign on the U.S. English keyboard layout, enter Shift+2. + + + + +The action that is performed is dependent on the currently selected +keyboard layout. If you change keyboard layouts and trigger a shortcut, it +may have undesired consequences. + + +Beneath the keystroke entry text box, you may select which window the +keystrokes will be directed to. You may choose from the following options: + + + +Active window - +The window that is currently open. + +Specific window - +The window that you describe using the form below. For more information on +describing windows, see . + +Action window - +When using the Window Actions trigger type, enter the keystrokes in the window +that triggered the shortcut. + + + + +Modifying a Keyboard Input Action + + +The Action tab for keyboard input. +Modifying the keyboard input action for a shortcut. + + + + + + + + + + + + + + +Defining Windows + +Several areas of the module allow you to define a list of windows. They +all use the same interface, which contains the following options: + + + + +Comment +This is merely an informational text box you can use to explain +what the trigger applies to or save other useful information. It is not +actually used by the system for anything. + + + +Window List +Beneath the Comment box on the left is the +list of all window definitions currently present in the trigger. Simply click +on one to perform an operation on it. + + + +Edit... + + +Press this button to modify the currently selected window definition. A +new window is opened that allows you to edit it. This window contains the +following options: + + + + +Window Data + + +This is where you describe the window the trigger should apply to. + +At the top, there is a Comment field, which is +informational just like the one in the main Trigger tab. + +Three window characteristics are available: + + + +Window title - +The title that appears at the top of the window. + +Window class - +Usually the name of the application. + +Window role - +Usually the name of the &Qt; class that makes up the window. + + + +Each window characteristic provides a drop-down box, and a text box below +it. Enter the value you wish to test windows for in the text box. From the +drop-down box, select which kind of test you wish to apply. You can use +Is to require an exact match, Contains +to require that the text entered be somewhere inside the full value, or +Matches Regular Expression to use a regular expression to +define the match. You may also perform the inverse of all these operations. +Select Is Not Important if you do not wish for that +particular characteristic to be examined. + +The easiest way to fill in this information is to open the desired window, +then click the Autodetect button at the bottom of this +section, and finally click on the desired trigger window. All three window +characteristics will be populated with the information from that window, and +you can then adjust the settings as necessary. + + + + + +Window Types + + +This lets you restrict the match to a particular type of window. The +following options are available: + + + +Normal - +A regular application window. + +Desktop - +The main desktop is actually a special window all its own. + +Dialog - +A small window that is part of a normal application, such as a message box or +a configuration screen. + +Dock - +A small window that can be attached or detached from a main application window. + + + + + + + + + + + + + +New... +Create a new window definition. This opens the edit dialog +described above. + + + +Duplicate... +This creates a new window definition with the exact same +specifications as the currently selected window definition. It will open the +edit dialog described above so you may make further changes. + + + +Delete +Removes the currently selected window definition. + + + + + + + + +Settings + +When you first enter the module, or when you click the +Settings button below the left pane, there will be +several configuration options in the right pane: + + + + +Start the Input Actions daemon on login +This configures whether or not to activate the background +application that monitors for keyboard shortcuts and triggers configured +actions. It is enabled by default. + + + +Gestures + +Select this check box to enable mouse gestures. + +There are two options specific to mouse gestures: + + + + +Timeout +This specifies the maximum time interval during which the mouse +gesture will be monitored and recognized by the system, in milliseconds. + + + + +Mouse Button +This specifies the mouse button to be used for mouse gestures. +Usually, 1 is the &LMB;, 2 is the +&RMB;, and 3 is the middle or wheel button. If your +mouse has more buttons, you can also use those instead. + + +Button 1 is unavailable, so mouse gestures do not interfere with the +normal operation of your system. + + + + + + + + + + + +Settings + + +The Settings screen. +Editing Custom Shortcuts settings. + + + + + + + +Credits and License + +Special thanks to Google Code-In 2011 participant Subhashish Pradhan for +writing much of this article. + + + +&underFDL; +&underGPL; + + + +
    + + diff --git a/doc/kcontrol/khotkeys/manage-export.png b/doc/kcontrol/khotkeys/manage-export.png new file mode 100644 index 0000000000..20a6fd89aa Binary files /dev/null and b/doc/kcontrol/khotkeys/manage-export.png differ diff --git a/doc/kcontrol/khotkeys/oxygen-22x22-edit-clear-locationbar-rtl.png b/doc/kcontrol/khotkeys/oxygen-22x22-edit-clear-locationbar-rtl.png new file mode 100644 index 0000000000..2bc48d8607 Binary files /dev/null and b/doc/kcontrol/khotkeys/oxygen-22x22-edit-clear-locationbar-rtl.png differ diff --git a/doc/kcontrol/khotkeys/settings.png b/doc/kcontrol/khotkeys/settings.png new file mode 100644 index 0000000000..95c297d205 Binary files /dev/null and b/doc/kcontrol/khotkeys/settings.png differ diff --git a/doc/kcontrol/khotkeys/shortcuts-action-command.png b/doc/kcontrol/khotkeys/shortcuts-action-command.png new file mode 100644 index 0000000000..2ed9b2bbbd Binary files /dev/null and b/doc/kcontrol/khotkeys/shortcuts-action-command.png differ diff --git a/doc/kcontrol/khotkeys/shortcuts-action-keyboard.png b/doc/kcontrol/khotkeys/shortcuts-action-keyboard.png new file mode 100644 index 0000000000..b2a7fca222 Binary files /dev/null and b/doc/kcontrol/khotkeys/shortcuts-action-keyboard.png differ diff --git a/doc/kcontrol/khotkeys/shortcuts-trigger-keyboard.png b/doc/kcontrol/khotkeys/shortcuts-trigger-keyboard.png new file mode 100644 index 0000000000..564665c9ce Binary files /dev/null and b/doc/kcontrol/khotkeys/shortcuts-trigger-keyboard.png differ diff --git a/doc/kcontrol/khotkeys/shortcuts-trigger-mouse.png b/doc/kcontrol/khotkeys/shortcuts-trigger-mouse.png new file mode 100644 index 0000000000..0eedf6f537 Binary files /dev/null and b/doc/kcontrol/khotkeys/shortcuts-trigger-mouse.png differ diff --git a/doc/kcontrol/mouse/CMakeLists.txt b/doc/kcontrol/mouse/CMakeLists.txt new file mode 100644 index 0000000000..582e99f55c --- /dev/null +++ b/doc/kcontrol/mouse/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/mouse) diff --git a/doc/kcontrol/mouse/index.docbook b/doc/kcontrol/mouse/index.docbook new file mode 100644 index 0000000000..ba8b0d8fce --- /dev/null +++ b/doc/kcontrol/mouse/index.docbook @@ -0,0 +1,472 @@ + + + +]> + +
    + +Mouse + +&Mike.McBride; &Mike.McBride.mail; +&Brad.Hards; &Brad.Hards.mail; + + + +2013-12-05 +&kde; 4.12 + + +This is the documentation for the &kde; &systemsettings; module that configures mice and other pointing devices. + + + + +KDE +Systemsettings +mouse + + + + +Mouse + +This module allows you to configure your pointing device. Your +pointing device may be a mouse, a track ball, a touch-pad, or another piece +of hardware that performs a similar function. + +This module is divided into several tabs: +General, +Advanced and +Mouse Navigation. +There may also be one or more additional tabs if you have a Logitech mouse +or trackball, providing access to special features. + + + +General + + + +Button Order + +If you are left-handed, you may +prefer to swap the functions of the left and +right buttons on your pointing device by choosing the +Left handed option. If your pointing device has more than two +buttons, only those that function as the left and +right buttons are affected. For example, if you have +a three-button mouse, the middle button is +unaffected. + + + + +Reverse scroll direction + +With this check box selected, the scroll wheel (if any) will work in the opposite direction (so that if rolling the top of the scroll wheel towards you previously causes a scroll down, then it will now cause a scroll up). This may be useful to handle a unusual setup of the X server. + + + + + +Double-click to open files and folders (select icons on first click) + + If this option is not checked, icons/files will be opened with a +single click of the left mouse-button. This +default behavior is consistent with what you would expect when you click +links in most web browsers. If checked however, icons/files will be +opened with a double click, while a single click will only select the +icon or file. This is the behavior you may know from other desktops or +operating systems. + +This option can be changed in the configuration dialogs of &dolphin; and &konqueror; +on the Navigation tab as well. + + + + + +Single-click to open files and folders + +This is the default setting for &kde;. Clicking once on an icon +will open it. To select you can drag around the icon(s) or &Ctrl;Right click, +or simply click and hold to drag it. You can also enable automatic selection of icons using the Automatically select icons check box, discussed below. + + + + +Change pointer shape over icons + +When this option is checked, the shape of the mouse pointer +changes whenever it is over an icon. + +This option should be checked in most situations. It gives +more visual feedback and says, in essence, if you click here, something +will happen. + + + + + +Automatically select icons + + +As noted above, if you have Single-click to open files and folders selected, you can select icons by dragging around them, or by using &Ctrl;Right click. If you routinely need to select icons, you may want to enable this check box, which will allow icons to be selected automatically by pausing over the icon. The Delay: slider determines how long is required before the automatic selection takes effect. + + + + + + + + +Advanced + + + + +Pointer acceleration + +This option allows you to change the relationship +between the distance that the mouse pointer moves on the +screen and the relative movement of the physical device +itself (which may be a mouse, track-ball, or some other +pointing device.) + +A high value for the acceleration multiplier will lead to large +movements of the mouse pointer on the screen, even when +you only make a small movement with the physical device. + +A multiplier between 1x and +3x will works well for many systems. With a multiplier +over 3x the mouse pointer may become difficult to +control. + + + + +Pointer threshold + +The threshold is the smallest distance that the mouse pointer must +move on the screen before acceleration has any effect. If the movement +is within the threshold, the mouse pointer moves as if the acceleration +were set to 1x. + +Thus, when you make small movements with the physical device (⪚ +mouse), you still have fine control of the mouse pointer on the screen, +whereas larger movements of the physical device will move the mouse +pointer rapidly to different areas on the screen. + +You can set the threshold by entering a value into the edit box or +by clicking the up/down arrows to the right of the box. + +In general, the higher you set the Pointer +acceleration value, the higher you will want to set the +Pointer threshold value. For example, a +Pointer threshold of 4 pixels may +be appropriate for a Pointer Acceleration of +2x, but 10 pixels might be +better for 3x. + + + + +Double click interval + +This is the maximum amount of time between clicks for &kde; to +register a double click. If you click twice, and the time between those +two clicks is less than this number, &kde; recognizes that as a double +click. If the time between these two clicks is greater than this +number, &kde; recognizes those as two separate +single clicks. + + + + +Drag start time and Drag start +distance + + +If you + +click with the mouse +drag within the time specified in Drag start +time, and +move a distance equal to or greater than the number (of +pixels) specified in Drag start distance + + + +&kde; will drag the selected item. + + + + +Mouse wheel scrolls by + +If you have a wheel mouse, use the spin box to determine how many +lines of text one step of the mouse wheel will +scroll. + + + + + + + + +Mouse Navigation + + +This tab allows you to configure the keyboard number pad keys as a mouse-type device. This may be useful when you are working on a device without another pointing device, or where you have no other use for the number pad. + + + + + +Move pointer with keyboard (using the num pad) + + +To enable keyboard mouse mode, you need to select the check box labeled Move pointer with keyboard (using the num pad). When you do this, the other settings will become enabled, and you can customize the keyboard pointer behavior further, if required. + + +The various keys on the number pad move in the direction you would expect. Note that you can move diagonally as well as up, down, left and right. The 5 key emulates a click to a pointer button, typically &LMB;. You change which button is emulated by using the / key (which makes it &LMB;), * key (which makes it &MMB;) and - (which makes it &RMB;). Using the + emulates a double click to the selected pointer button. You can use the 0 key to emulate holding down the selected pointer button (for easy dragging), and then use the . to emulate releasing the selected pointer button. + + + + + +Acceleration delay + + +This is the time (in milliseconds) between the initial key press and the first repeated motion event for mouse key acceleration. + + + + + +Repeat interval + + +This is the time in milliseconds between repeated motion events for mouse key acceleration. + + + + + +Acceleration time + + +This is the time in milliseconds before the pointer reaches a maximum speed for mouse key acceleration. + + + + + +Maximum speed + + +This is the maximum speed in pixels per second the pointer can reach for mouse key acceleration. + + + + + +Acceleration profile + + +This is the slope of the acceleration curve for mouse key acceleration. + + + + + + + + + +Logitech Support + + +Some Logitech USB mice support special features, such as switching to a higher resolution, or +providing cordless status. If &kde; was built with libusb support, then you will get an additional +tab for each supported mouse that is plugged in. + + + +The supported devices are: + + +Wheel Mouse Optical +MouseMan Traveler +MouseMan Dual Optical +MX310 Optical Mouse +MX510 Optical Mouse +MX300 Optical Mouse +MX500 Optical Mouse +iFeel Mouse +Mouse Receiver +Dual Receiver +Cordless Freedom Optical +Cordless Elite Duo +MX700 Optical Mouse +Cordless Optical Trackman +Cordless MX Duo Receiver +MX100 Laser Mouse +Receiver for Cordless Presenter + + + +Not all devices support all capabilities (typically cordless devices do not +provide resolution switching, and of course only cordless devices support +cordless status reporting), so some parts of the tab will not be enabled for +some mouse types. + + + +If the mouse supports resolution switching, the Sensor Resolution +radio button group will be enabled, and you can switch from 400 counts per +inch to 800 counts per inch and back. If you use +800 counts per inch, the same physical movement of the mouse +will cause a greater (roughly double) amount of motion of the cursor. This tends +to be popular amongst gamers. + + + +If the mouse supports cordless reporting, the Battery Level +and RF Channel widgets will be enabled. You can only change +the RF Channel if your mouse has two channel support. + + + + + + +
    diff --git a/doc/kcontrol/paths/CMakeLists.txt b/doc/kcontrol/paths/CMakeLists.txt new file mode 100644 index 0000000000..526a61bc25 --- /dev/null +++ b/doc/kcontrol/paths/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/paths) diff --git a/doc/kcontrol/paths/index.docbook b/doc/kcontrol/paths/index.docbook new file mode 100644 index 0000000000..eecb4c76ab --- /dev/null +++ b/doc/kcontrol/paths/index.docbook @@ -0,0 +1,91 @@ + + + +]> + +
    +Paths + + + + +Michael +Anderson + +
    nosrednaekim@gmail.com
    +
    +
    +Anne-Marie +Mahfouf + +
    annma@kde.org
    +
    + +
    + +2009-11-17 +&kde; 4.4 + + +KDE +KControl +desktop +paths + +
    + +This is a module to configure essential paths for the &kde; desktop. + + + +Configure some &kde; paths + + + + + + Configure some &kde; paths + + + + + + + +Desktop path: +The Desktop folder contains all the files which you see on your desktop. The default path is usually ~/Desktop but you can change here the location of this folder. + + + +Autostart path: + +The Autostart folder contains applications or links to applications or scripts you want to start whenever &kde; starts. You can change the location of this folder if you want to. + + + +Documents path: +This folder will be used by default to load or save documents from or to. + + +Download path: +This folder will be used by default to save your downloaded items. + + +Movies path: +This folder will be used by default to load or save movies from or to. + + +Pictures path: +This folder will be used by default to load or save pictures from or to. + + +Music path: +This folder will be used by default to load or save music from or to. + + +If you change a path and click Apply a dialog appears where you can choose to move the content to the +new folder or use the new folder without moving the files. + +
    diff --git a/doc/kcontrol/paths/paths.png b/doc/kcontrol/paths/paths.png new file mode 100644 index 0000000000..eff8756d5c Binary files /dev/null and b/doc/kcontrol/paths/paths.png differ diff --git a/doc/kcontrol/powerdevil/CMakeLists.txt b/doc/kcontrol/powerdevil/CMakeLists.txt new file mode 100644 index 0000000000..99d7ac7733 --- /dev/null +++ b/doc/kcontrol/powerdevil/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/powerdevil) diff --git a/doc/kcontrol/powerdevil/activity.png b/doc/kcontrol/powerdevil/activity.png new file mode 100644 index 0000000000..2522903f22 Binary files /dev/null and b/doc/kcontrol/powerdevil/activity.png differ diff --git a/doc/kcontrol/powerdevil/advanced.png b/doc/kcontrol/powerdevil/advanced.png new file mode 100644 index 0000000000..5b928274a7 Binary files /dev/null and b/doc/kcontrol/powerdevil/advanced.png differ diff --git a/doc/kcontrol/powerdevil/energy.png b/doc/kcontrol/powerdevil/energy.png new file mode 100644 index 0000000000..1998a57f36 Binary files /dev/null and b/doc/kcontrol/powerdevil/energy.png differ diff --git a/doc/kcontrol/powerdevil/index.docbook b/doc/kcontrol/powerdevil/index.docbook new file mode 100644 index 0000000000..a99dc9bf0c --- /dev/null +++ b/doc/kcontrol/powerdevil/index.docbook @@ -0,0 +1,317 @@ + + + +]> +
    + +Power Management + +&Dario.Freddi;&Dario.Freddi.mail; + + + +2013-08-18 +&kde; 4.11 + + +KDE +System Settings +powerdevil +power management +laptop power + + + + +PowerDevil, a Power Management Daemon + +This module is a configuration tool for managing Power Management in &kde;. It is a configurator for the PowerDevil daemon, that you can start or stop from the Service Manager module. + +Through this module, you can define your computer's behavior in various situations, allowing you to save as much energy as possible. + + +Powerdevil works with Solid only and it's integrated in your desktop. You won't even notice it is running, apart from notifications. Moreover, Powerdevil checks your system capabilities first, so you'll see only options available for your system in this module. + + +Note that PowerDevil does not poll your system. This means that it will not waste energy while trying to save it, as some other power managers do. + +Energy Saving Settings + +This page shows the actual energy saving settings. If a battery is detected you have three tabs with different settings for On AC Power, On Battery and On Low Battery profiles. + + + +Energy Saving Settings + + + + + + Energy Saving Settings + + + Energy Saving Settings + + + + + +There are a lot of options you can configure for each profile: + + + +Screen Brightness + +Sets the screen's brightness using a slider. + + + +Dim Screen + +If checked, the display will be progressively dimmed, until reaching +brightness 0, in the amount of time you specify in the spin box. + + + + +Screen Energy Saving + +Here you can define power saving for your monitor. +The check box defines if DPMS support should be enabled or disabled. +If you choose to activate it, set the timeouts in the spin boxes below. + + + + +Suspend Session + +Suspends the session after the selected time to the status +Sleep, Hibernate, +Shutdown or Lock screen. + + + +Button events handling + +Select actions to perform whenever the laptop lid is closed or a button is pressed. + + +Run Script + +Activate this option to run a custom script when a profile is loaded, unloaded +or after a given amount of time. + + + + + + + + +Activity Settings +Here you can configure the Power Management settings per activity. The settings for each activity can be configured on its own tab. + + +Activity Settings + + + + + + Activity Settings + + + Activity Settings + + + + + + +Don't use special settings + + +Use the settings defined on Energy Saving page. + + + + +Act like + + +Here you can choose a predefined profile from the drop-down box: PC running on AC power, PC running on battery power or PC running on low battery. + + + + +Define a special behavior + + +If this option is chosen, a special behavior for activity can be chosen. If this is a presentation activity, you can check the Never shutdown the screen and Never suspend or shutdown the computer boxes. It is also possible to define the time interval for the computer to switch to a suspension mode for the power saving activities. + + + + +Use separate settings (advanced users only) + + +This option allows users to choose the detail power saving settings for a separate activity. The settings are similar to the settings from the Energy Saving page. + + + + + + + +Advanced Settings + + +Advanced Settings + + + + + + Advanced Settings + + + Advanced Settings + + + + + +Battery levels + +These items are only displayed if a battery is detected. + + +Battery is at low level at + + +Here you can set the percentage after which the battery will be considered low. + + + +Battery is at critical level at + + +Here you can set the percentage after which the battery will be considered at critical level. + + + +When battery is at critical level + + +Defines the action that will be done when battery reaches the critical level. + + + + + + +Events + + +Lock screen on resume + + +When this box is checked, every time the PC gets suspended, the screen will be locked when resuming. You will be asked for a password when resuming from sleep state. + + +Never prevent an action on lid close + + +When this option is selected, applications will not be allowed to inhibit sleep when the lid is closed. + + + +Configure Notifications + + +If you want a deeper control over your notifications, you can refine them by clicking this button. + + + + + + + + + + + + +PowerDevil Runner + +PowerDevil can be manually controlled through &krunner; (&Alt;F2). This makes changing profile, brightness, governor and more much easier and faster. The runner has various keywords you can enter to access its functions + + +Runner Keywords + +Here comes a list of keywords you can enter in &krunner; to perform PowerDevil related actions. + + +Power Profile + + +You can manually choose a profile using this keyword. All available profiles will be shown in form of &krunner; matches. + + + + +Screen Brightness + + +You can control screen brightness through this keyword. In this form, some &krunner; matches that let you do some common actions will be shown. If you enter a value after the keyword (⪚: Screen Brightness 50), the brightness will be set to that value. + + + +Suspend + + +You can suspend your pc through this keyword. Available suspend methods will be shown as &krunner; matches. + + + + + + +
    + + + diff --git a/doc/kcontrol/solid-actions/CMakeLists.txt b/doc/kcontrol/solid-actions/CMakeLists.txt new file mode 100644 index 0000000000..44950ff5cf --- /dev/null +++ b/doc/kcontrol/solid-actions/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/solid-actions) diff --git a/doc/kcontrol/solid-actions/add-action.png b/doc/kcontrol/solid-actions/add-action.png new file mode 100644 index 0000000000..67b4ea98b0 Binary files /dev/null and b/doc/kcontrol/solid-actions/add-action.png differ diff --git a/doc/kcontrol/solid-actions/edit-action.png b/doc/kcontrol/solid-actions/edit-action.png new file mode 100644 index 0000000000..aa2731b2c7 Binary files /dev/null and b/doc/kcontrol/solid-actions/edit-action.png differ diff --git a/doc/kcontrol/solid-actions/index.docbook b/doc/kcontrol/solid-actions/index.docbook new file mode 100644 index 0000000000..54da3eed85 --- /dev/null +++ b/doc/kcontrol/solid-actions/index.docbook @@ -0,0 +1,222 @@ + + + +]> +
    + +Device Actions + +BenCooksley +&Anne-Marie.Mahfouf; + + + +2013-12-05 +&kde; 4.12 + + +KDE +System Settings +Solid +actions +devices +hardware + + + + +Actions for new devices + +Introduction + +This module allows you to set up the actions you want to associate when +you plug new devices. + + +Screenshot of the Device Actions Manager + + + + + + Screenshot of the Device Actions Manager + + + + + +You will see listed all of the possible actions that are available when +devices are plugged in. +Please note that they will only be available under certain conditions. + + +Add + This will ask you to supply a name for your new action, then will open that +action for editing. + If you do not supply all the required details in that dialog, then your +action will be deleted. + + +Edit + This will allow you to change the Name, Icon, command to execute + and the conditions the action must match to be shown. + + +Discard / Cannot be deleted / Remove + This will do different actions, depending on if it was supplied with your +system or created by you. + You can only discard changes to system wide actions, if there are no changes +then these actions cannot be deleted. + User created actions are completely removed, including all changes. + + + + + + +Add Action + + +The Add button brings you a dialog to enter the +new action name. + + + +Screenshot of the Add Action dialog + + + + + + Screenshot of the Add Action dialog + + + + + +The action name should correctly describe the action. After entering a name and +clicking OK, the Edit Action dialog will show to allow +you to set up the action properties. + + + + +Edit Action + +This dialog allows you to change the settings of the action you +selected. + + +Screenshot of the Edit Action dialog + + + + + + Screenshot of the Edit Action dialog + + + + + +This includes the icon, the action name, the action command and the various +parameters. +You can change the icon by clicking on it. +The parameters are displayed in a +tree, the top item having to be true in order for the action to be shown. + + +Edit parameter + + +Parameter type + +This controls what can be contained inside it, or +what matching is performed to check if it is true. + + +Property Match + +Compares the specified property of the Device (using +Device type and Value name) and using the evaluator, compares it against the +text provided. + + + +Content Conjunction + +All sub-properties of this must be true for +it to be true itself. + + + +Content Disjunction + +Any sub-property of this may be true for it +to be true itself. + + + +Device interface match + +Checks to see if the Device can be +classed as the selected Device type. + + + + + + + + +Device type + +These are the possible device types supported by &kde; on +your system. +They are used in both Property and Device interface matches. + + + + +Value name + +This is a list of all possible property names for the +selected device type. +It also provides the selection of the value evaluator (which is either +Equals or Contains), and allows +entry of the text to be compared with. + + + +Reset Parameter + +Reverts any unsaved changes you have made to this parameter. + + + +Save Parameter changes + +Saves the changes you have made to the parameter. + + + + + + + +
    + + diff --git a/doc/kcontrol/solid-actions/main.png b/doc/kcontrol/solid-actions/main.png new file mode 100644 index 0000000000..19c91c6abb Binary files /dev/null and b/doc/kcontrol/solid-actions/main.png differ diff --git a/doc/kcontrol/solid-hardware/CMakeLists.txt b/doc/kcontrol/solid-hardware/CMakeLists.txt new file mode 100644 index 0000000000..cf7a9a2323 --- /dev/null +++ b/doc/kcontrol/solid-hardware/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/solid-hardware) diff --git a/doc/kcontrol/solid-hardware/index.docbook b/doc/kcontrol/solid-hardware/index.docbook new file mode 100644 index 0000000000..58ed17d11c --- /dev/null +++ b/doc/kcontrol/solid-hardware/index.docbook @@ -0,0 +1,62 @@ + + + +]> +
    +Information Sources + + +BenCooksley + + + +2012-11-17 +&kde; 4.10 + + +KDE +Systemsettings +hardware +backend + + + + +This &systemsettings; module allows you to change the order of the backends used by Solid to +retrieve information about your hardware, and manage its network connection, +bluetooth and infrared devices if present. None of these backends can be used directly by the user. + +To use ⪚ a different network management backend, select it, and move +it up to the first position using the arrow buttons next to the list. Then the +Apply button will be available. + + + +Network Management Backend + +This is used by &kde; applications to determine the status of your computers +connection to the Internet and other networks. It also allows them to retrieve +information such as the current configuration of your network cards, and to change +their configuration if permitted. + + + +Remote Control Management Backend + +Used by &kde; applications to handle input from devices such as infrared remote controls. + + + +Modem Management Backend + +A &DBus; interface to communicate with mobile broadband (GSM, CDMA, UMTS, &etc;) cards. + + + + + + + +
    diff --git a/doc/kcontrol/splashscreen/CMakeLists.txt b/doc/kcontrol/splashscreen/CMakeLists.txt new file mode 100644 index 0000000000..81b5f77a7e --- /dev/null +++ b/doc/kcontrol/splashscreen/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kcontrol/splashscreen) diff --git a/doc/kcontrol/splashscreen/get-new-theme.png b/doc/kcontrol/splashscreen/get-new-theme.png new file mode 100644 index 0000000000..dc631ed73b Binary files /dev/null and b/doc/kcontrol/splashscreen/get-new-theme.png differ diff --git a/doc/kcontrol/splashscreen/index.docbook b/doc/kcontrol/splashscreen/index.docbook new file mode 100644 index 0000000000..0136bd969d --- /dev/null +++ b/doc/kcontrol/splashscreen/index.docbook @@ -0,0 +1,117 @@ + + + +]> +
    +Splash Screen + + + +Anne-Marie +Mahfouf + +
    &Anne-Marie.Mahfouf.mail;
    +
    +
    + +
    + +2010-09-25 +&kde; 4.5 + + +KDE +System Settings +splash screen +theme + +
    + + +This module provides an interface to install and view splash screen themes. + + + +Here's a screenshot of the splash screen manager + + + + + + The splash screen manager module + + + + + +On the left you can see the list of already installed splash screens. The current splash screen name is highlighted. +On the right is displayed a preview of the current splash screen with some information about it below the preview. + + +Get New Themes... + + +You need to be connected to the Internet to use this action. A dialog will display a list of themes from the http://www.kde-look.org website. Clicking the Install button associated with a theme will install this theme locally. + + + +Installing a new theme + + + + + + Installing a new theme + + + + + +This new theme is immediately available after you close the System Settings Add-On Installer dialog and is highlighted. The preview displays the picture. You need to click the Apply biutton to make this theme your current splash-screen theme. + + + + +Install Theme File... + + +Instead of installing a theme directly as above you might have a tarball of a theme locally that you want to install. Clicking on this button will open a file dialog and you choose the tarball of the theme you want to install. The installer then unpacks the theme locally and makes it immediately available in the themes list. + + + + +Remove Theme + + +Removing a theme means removing the theme files from your disk. A message box warns you before doing anything and indicates to you what it will remove. Clicking on Delete in this message box will remove the splash screen theme from your disk. Clicking Cancel will abort the remove action. If you choose Delete, the theme is then no longer available in the list. + + + + +Test Theme + + +This shows you the theme and the associated icons as they will be displayed as splash screen when &kde; starts. Clicking on the splash screen hides it. + + + + + +
    + + diff --git a/doc/kcontrol/splashscreen/splash-main.png b/doc/kcontrol/splashscreen/splash-main.png new file mode 100644 index 0000000000..375455e9c1 Binary files /dev/null and b/doc/kcontrol/splashscreen/splash-main.png differ diff --git a/doc/kfontview/CMakeLists.txt b/doc/kfontview/CMakeLists.txt new file mode 100644 index 0000000000..0921d20edb --- /dev/null +++ b/doc/kfontview/CMakeLists.txt @@ -0,0 +1 @@ +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en) diff --git a/doc/kfontview/index.docbook b/doc/kfontview/index.docbook new file mode 100644 index 0000000000..dc2f12e471 --- /dev/null +++ b/doc/kfontview/index.docbook @@ -0,0 +1,124 @@ + +KFontview"> + + + + +]> + +
    + + +Font Viewer + +&Burkhard.Lueck; &Burkhard.Lueck.mail; + + + + +2013-06-19 +1.1 (&kde; 4.11) + + +KDE +fontview +fonts + + + + +&kfontview; is an application to view and install different types of fonts: + +TrueType (.ttf) +OpenType (otf, .ttf) +Postscript type 1 (file extension on &Windows; & OS/2 .pfb/.pfm; on &UNIX;/&Linux; .pfa/.afm) +BDF (Bitmap Distribution Format, file extension .bdf) +PCF (Portable Compiled Format, file extension .pcf) + + +You can use it as standalone program to preview and install a font +downloaded from the Internet. In the preview you can check that the font +has all characters you need. + + +Additionally &kfontview; can be launched from +the &systemsettings; module +Font Installer to use the extended preview capabilities +of &kfontview;. +To use &kfontview; in &konqueror; select Services in +the sidebar and go to the folders Fonts/Personal + or Fonts/System. +Then open the context menu with a &RMB; click on a font and start &kfontview; +by selecting it from the context menu. + + + + +&kfontview; application + + + + + + &kfontview; application + + + + + + +&kfontview;'s Standard Preview displays your font in several +different sizes using a pangram (a sentence using every letter of the alphabet at least once). +So you can get a good idea of what it will look like. +From the drop down box in the toolbar you can choose additional preview types: + +All Characters displays all characters +provided by the font in one size. +Unicode Block: The Unicode standard arranges groups of characters +together in blocks +Unicode Script: In Unicode, a script is a collection of letters +and other written signs used to represent textual information in one or more +writing systems +Change Text: Using this button opens the Preview +String dialog where you can insert a user defined text. +This feature enables you to check that a font supports the characters you need. + + +You can use the zoom buttons or the mousewheel to change the font size in all preview types. +In Unicode Block and Unicode Script +preview mode additional tooltip info's are displayed hovering a character with the +mouse pointer: +The character's Unicode Category and the value in +UCS-4, UTF-16, UTF-8 and +as XML Decimal Entity. + + +To install a font you downloaded from the Internet, click the +Install button in the lower-right corner. + + +You can install the font for personal us only available to you or system-wide available +for all users, the latter will require administrator privileges. + +Fonts are often provided in an archive format like zip. +Open these archives with &ark; which provides a simple previewer for fonts. +To make use of the extended capabilities of &kfontview; extract the fonts and open them in +&kfontview;. + +Applications need to be restarted to use of the newly installed fonts. +
    + + diff --git a/doc/kfontview/kfontview.png b/doc/kfontview/kfontview.png new file mode 100644 index 0000000000..fa205993ba Binary files /dev/null and b/doc/kfontview/kfontview.png differ diff --git a/doc/plasma-desktop/CMakeLists.txt b/doc/plasma-desktop/CMakeLists.txt new file mode 100644 index 0000000000..c5c24b907a --- /dev/null +++ b/doc/plasma-desktop/CMakeLists.txt @@ -0,0 +1,5 @@ +########### install files ############### +# +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en) + diff --git a/doc/plasma-desktop/add-widgets.png b/doc/plasma-desktop/add-widgets.png new file mode 100644 index 0000000000..7c062760eb Binary files /dev/null and b/doc/plasma-desktop/add-widgets.png differ diff --git a/doc/plasma-desktop/desktop-settings.png b/doc/plasma-desktop/desktop-settings.png new file mode 100644 index 0000000000..03732de504 Binary files /dev/null and b/doc/plasma-desktop/desktop-settings.png differ diff --git a/doc/plasma-desktop/device_notifier_widget.png b/doc/plasma-desktop/device_notifier_widget.png new file mode 100644 index 0000000000..34d916da5e Binary files /dev/null and b/doc/plasma-desktop/device_notifier_widget.png differ diff --git a/doc/plasma-desktop/device_notifier_widget_actions.png b/doc/plasma-desktop/device_notifier_widget_actions.png new file mode 100644 index 0000000000..d00a7a2ebc Binary files /dev/null and b/doc/plasma-desktop/device_notifier_widget_actions.png differ diff --git a/doc/plasma-desktop/index.docbook b/doc/plasma-desktop/index.docbook new file mode 100644 index 0000000000..06eb4ac7d7 --- /dev/null +++ b/doc/plasma-desktop/index.docbook @@ -0,0 +1,642 @@ + + + + + +]> + + + +The &plasma; Handbook + + + + +Sebastian +Kügler + +sebas@kde.org + +Claus Christensen + + + + +2008–2010 +Sebastian Kügler + + +&FDLNotice; + +2013-06-19 +&kde; 4.11 + + + +&plasma;, the most visible pillar of &kde;, is the core interface to the desktop. + + + + +KDE +Plasma +Plasmoid +Widget +Containments +Desktop +Runner +Kicker + + + + + +Introduction + + +&plasma; is one of the key technologies of &kde; Software Compilation 4 (also known as the +“Pillars of &kde;”), and one of the most visible to users. As &plasma; +treats the user interface differently than a traditional desktop, there may be +confusion on what &plasma; is, what it does, and how to perform common tasks. + + + +This document attempts to address these problems by providing answers to the +most common questions. + + + + +Using &plasma; + + +&plasma; Components + + +&plasma; Widgets and Containments + + +The essence of &plasma; revolves around two basic concepts: + + + + +&plasma; Widgets + + +Applets, or small applications that live on the desktop. + + + + +Containments + + +Applets that act as the container for the &plasma; widgets + + + + + + +On a default desktop, there are two main elements: the Panel and the Desktop +itself. Both are containments in the &plasma; sense. + + + + + +Default &plasma; Desktop + +The &plasma; Desktop can be configured in countless ways. The screenshot seen below shows a fairly standard desktop. Some distributions apply extensive customizations, so your desktop may look different. + +Similarly, the graphical appearance of the interface elements can be styled. These screenshots uses the &kde; default style, Oxygen. + + + +A fairly standard &plasma; Desktop + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +The program starter. Usually this will be &kickoff;. +A couple of icons giving easy access to often used applications +The Pager widget +The Task Manager, which shows the titles of windows belonging to the applications currently running. No application had opened a window, when the screenshot was taken +The System Tray +The Digital Clock widget +The Panel Toolbox +The Desktop Toolbox (previously known as a Cashew) +A Folder View widget, showing the content of the Desktop folder + + + + + + +The Panel + + +The default panel holds a few &plasma; widgets: starting from the left, there is the +&kickoff; application launcher. You can use it to start applications, open recently +opened files and the usual logout/shutdown options. There is also a structure +that allows you to browse through your applications. The layout has been +optimized for the use case that is most common: starting an application. The +default tab is the Favorites tab that holds the most-used +entries. In the beginning, you'll probably find yourself using the +Applications tab more often. +Once you have found out what your most frequently started applications are, +right click on the items and select the +Add to Favorites to add them to your +Favorites (or directly into the panel or on the desktop. +Note that you need to unlock &plasma; by means of right +clicking on the desktop for any kind of modification). If you prefer the +traditional menu-style application launcher, change it by +right clicking on the menu button and selecting +Switch to Classic Menu Style. + + + +The taskbar is another widget on the panel. It shows an area for all open windows on +all desktops by default. You can make it show all open windows on the current +desktop only by checking Only show tasks from the +current desktop when you right click on the task manager, between +two windows. The size of the text on the taskbar items can be set +in &systemsettings; under the +Application AppearanceFonts +Taskbar. Right-clicking on the taskbar +brings the Settings dialog where you can choose several options to customize +your taskbar. + +Here is a screenshot of the taskbar settings dialog + + + + + + Taskbar settings dialog + + + + + + +Another default panel item is the System Tray, which +is used by traditional applications and widgets as a dock. Right clicking on the +System Tray allows you to open the settings dialog, +where you can set entries to display and their visibility. + + +System Tray settings dialog + + + + + + System Tray settings dialog + + + + +An icon located usually in the system tray is the Device Notifier. +Plug in a USB disk and a dialog will open that allows you to +open the device in &dolphin; or another associated application. + + + + + + + + + + + + + + +The Device Notifier is used for handling pluggable devices such as USB pendrives (also called flash drives or thumb drives), digital cameras, external USB hard drives, &etc; It also comes into play when a medium such as a &CD; or DVD is loaded into an optical drive. + +When you plug in an external device, or load a new medium into a drive, the Notifier window pops up (you can also open it explicitly by clicking on its Panel icon.) It stays open while the cursor is over it, otherwise it hides itself after a few seconds. + +The Notifier window shows a list of all the devices it currently knows about. Moving the mouse cursor over a device will highlight how many possible actions are associated with that device. + + + + + + + + + + + + + + +Clicking anywhere in the shaded box around the device name (but not on the eject/unmount icon if present) expands the entry to show the list of possible actions for that device. The set of actions depends on the device; it is configurable from the Device Notifier's context menu or from the &systemsettings; modules Device Actions and Removable Devices. Simply select one with the mouse or keyboard to launch that action. + +There is also a small icon to the right of each entry showing whether that device is currently accessible (or mounted) or not. Only when a device is not mounted is it safe to physically disconnect it from the computer. Clicking on the icon causes the device to be unmounted and/or the medium to be ejected if it is currently mounted, and will mount it if it is not. Note that unmounting/ejecting might fail if the device still has open files on it, ⪚ if a large file copy hasn't finished. In most cases you can just wait a while and try again. When an unmounting has succeeded, a success icon will be shown on the Device Notifier's icon. + + +The System Tray normally holds some more entries like +&klipper;, &kmix; &etc; + +Some of the default entries in the System Tray are hidden +to save space in the bar. To display these entries click on the small white triangle + +, then use +the &LMB; to launch a widget or the &RMB; to open its settings dialog. + + +The right-most &plasma; widget in the default panel holds the Digital +Clock. +This clock can display the time in different timezones as well as have its size +changed. The clock will adjust its font size to the area it is given by the +surrounding containment (that is the panel in this case). If you choose to +display the date, this date will be rendered using the Small +font option from the &systemsettings; Font +dialog. The time will take the rest of the space. So in the end, you'll choose +yourself the amount of information displayed, and if that fits. If you want to +display more information, make the panel larger or put the clock on the desktop +where it can grow freely. + + + +An optional item on your panel is the Pager. +It allows you to switch between your virtual desktops. +If you change the layout of the Pager +through the Number of rows option, +which will also affect the layout and animations that are shown in &kwin;’s +Desktop Grid effect. +For larger pagers, selecting Display windows icons +typically makes sense. + + + + + + + + + + +The Panel Toolboxes + +If your desktop is unlocked (you can do that by +right clicking on the desktop, or when no +application has the focus, with &Alt;D, L), a small &plasma; +logo will appear in the bottom right corner in the panel (it is commonly named the +“cashew”). Click on this cashew, and the panel controller opens. + + +Panel Settings + + + + + + Panel Settings + + + + +The panel controller allows you to reposition, resize and realign the panel. The +&plasma; widgets living in this panel will adjust their size automatically. +&plasma; widgets have basic knowledge about sizing, provided by the containment. They +are programmed to take advantage of that size, and inform the applet about how +much space they possibly need. In the end, the containment gives a possible size +to the applets, the applets obey. + + + + +Adding Applets + + +Unlock the desktop and you'll be able to add and remove &plasma; widgets from panel +or desktop. You add &plasma; widgets by simply dragging them where you want them. +Right click on an widget to remove it. + + +Add Widgets + + + + + + Add Widgets + + + + +The Get New Widgets button allows you to add widgets +you've previously downloaded and download new &plasma; widgets. +Currently it supports native &plasmagik; packages +and some &Mac; OS X dashboard widgets. Widgets you install this way can then be +accessed just like regular, preinstalled widgets. + + + + +The Desktop + + +The desktop is in fact another containment. One that does not put size +constraints on the applets. Applets can be moved and sized freely. On the +unlocked desktop, &plasma; widgets will show a frame when you move the mouse over +them. This applet handle allows you to move, resize, relocate and realign the +panel. It also allows you to drag &plasma; widgets on the desktop. The buttons in the +corner are used to resize, rotate configure and remove the applet. When +rotated, a &plasma; widget will act magnetic towards 12 o'clock, so it is easy to get +them back into sensible position. By default, most applets keep their aspect +ratio when they are being resized. If you want to freely resize an applet, +hold the &Ctrl; key pressed while resizing. + + + +Right clicking on the desktop also offers you to configure aspects such as the +wallpaper and the layout used, and the mouse actions. It offers to download new +wallpapers through &knewstuff;. + + + +Desktop Settings + + + + + + Desktop Settings + + + + +To change the &plasma; theme or download a new one through &knewstuff; +open the Workspace Appearance Desktop +Theme page in the &systemsettings;. + + +With open applications, it quickly gets hard to see the &plasma; widgets on your +desktop. The Dashboard gets those &plasma; widgets +in front of you, much like the Show desktop functionality +you are used to from traditional desktops. + + + + + +&krunner; + + +&krunner; is a versatile mini command-line. You can use it to start applications, +open web pages, access bookmarks, search through your desktop data, calculate +short equations, and many more.Pressing &Alt;F2 opens the &krunner; dialog. +You just start typing and &krunner; will start searching matches as soon as +you've entered more than two characters. You can open the settings dialog to +learn about &krunner;’s functionality, provided by plugins. You can +navigate through the matches using the tab and arrow keys. + + + +If you want to know what is going on on your system, there is the Show +System Activity button, giving you quick access to a list of windows +and processes, with options to monitor their output and kill processes. + + + + + +Activities + + +The desktop toolbox, accessed via the upper right corner has a button for +displaying your activities, of which &plasma; allows you to have more than one. +Basically, that is multiple desktop containments hosting multiple sets of &plasma; widgets. +Display the Activities bar, select one of the predefined activities +or choose Create Activity +to create a new containment, select your new containment and customize suiting your taste. +&plasma;’s activities and &kwin;’s desktop grid are similar in that respect, but there +is a fundamental difference. While virtual desktop are used to group and organize windows, +&plasma;’s activities are used to group and organize &plasma; widgets. This way, you can +switch between activities and have relevant &plasma; widgets supporting the task you are +currently trying to accomplish. You can create a Free time activity, with +comic strips, a puzzle and other &plasma; widgets, and a Work activity, +with relevant RSS feeds, calculator and calendar, for example. + + + +To delete an activity, press the Stop Activity button on +Activities bar (press &Alt;D then &Alt;A to open this bar) then click +the red 'X' (or press &Alt;D +then &Alt;R) and confirm the +deletion. + + + + + + +Shortcuts + + +Most of &plasma;’s functionality is also accessible through keyboard +shortcuts. The various combinations should be pressed in sequence, that +is for example &Alt;D, A +means: Press &Alt; and D, release and press A. + +Currently, the following default shortcuts can be used: + + + + +&Alt;D, A +Add Widgets + + + +&Alt;D, R +Remove Widget + + + +&Alt;D, L +Lock/Unlock Widgets + + + + +&Alt;D, N +Next Widget + + + +&Alt;D, P +Previous Widget + + + +&Alt;D, S +Widget settings + + + +&Alt;D, &Alt;A +Activities + + + +&Alt;D, &Alt;R +Remove this Activity + + + +&Alt;D, &Alt;S +Activities Settings + + + +Meta +Next Activity + + + +Meta&Shift; +Previous Activity + + + +&Ctrl;F12 +Show Dashboard + + + +&Alt;D, T +Run the Associated Application + + + + + +To change shortcuts, click with the &LMB; on the desktop Toolbox +(the cashew usually at the top right of the desktop) and select Shortcut Settings. + + + + + + +Credits and License + + +&plasma; + + +Program copyright 2008 &Aaron.J.Seigo; &Aaron.J.Seigo.mail; + + + +Documentation Copyright © 2008–2010 Sebastian Kügler sebas@kde.org + + + + +&underFDL; + + + +&documentation.index; + + + diff --git a/doc/plasma-desktop/panel-settings.png b/doc/plasma-desktop/panel-settings.png new file mode 100644 index 0000000000..49fe1dc48c Binary files /dev/null and b/doc/plasma-desktop/panel-settings.png differ diff --git a/doc/plasma-desktop/plasma-desktop-annotated.png b/doc/plasma-desktop/plasma-desktop-annotated.png new file mode 100644 index 0000000000..8ab73438f9 Binary files /dev/null and b/doc/plasma-desktop/plasma-desktop-annotated.png differ diff --git a/doc/plasma-desktop/system-tray-settings.png b/doc/plasma-desktop/system-tray-settings.png new file mode 100644 index 0000000000..e3ff1c3a59 Binary files /dev/null and b/doc/plasma-desktop/system-tray-settings.png differ diff --git a/doc/plasma-desktop/system-tray-up-arrow.png b/doc/plasma-desktop/system-tray-up-arrow.png new file mode 100644 index 0000000000..6de1abb999 Binary files /dev/null and b/doc/plasma-desktop/system-tray-up-arrow.png differ diff --git a/doc/plasma-desktop/taskbar-settings.png b/doc/plasma-desktop/taskbar-settings.png new file mode 100644 index 0000000000..47c61e9793 Binary files /dev/null and b/doc/plasma-desktop/taskbar-settings.png differ diff --git a/kcms/CMakeLists.txt b/kcms/CMakeLists.txt new file mode 100644 index 0000000000..204e72c080 --- /dev/null +++ b/kcms/CMakeLists.txt @@ -0,0 +1,49 @@ +project(kcms) + +remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) + +find_package(Freetype) +set_package_properties(Freetype PROPERTIES DESCRIPTION "A font rendering engine" + URL "http://www.freetype.org" + TYPE OPTIONAL + PURPOSE "Needed to build kfontinst, a simple font installer." + ) + + +set(libkxftconfig_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/fonts/kxftconfig.cpp ) + +if(X11_Xkb_FOUND) + add_subdirectory( keyboard ) +endif() + +if(NOT WIN32) +add_subdirectory( bell ) + +if (LIBUSB_FOUND) + add_subdirectory( input ) +endif() + +add_subdirectory( access ) +add_subdirectory( dateandtime ) +add_subdirectory( autostart ) +endif() + +add_subdirectory( launch ) +add_subdirectory( colors ) +add_subdirectory( krdb ) +add_subdirectory( style ) +add_subdirectory( desktoptheme ) +add_subdirectory( standard_actions ) +add_subdirectory( keys ) + +add_subdirectory( hardware ) +add_subdirectory( desktoppaths ) + +if( FREETYPE_FOUND ) + if( WIN32 OR FONTCONFIG_FOUND ) + add_subdirectory( fonts ) + endif() +endif() +if(FONTCONFIG_FOUND AND FREETYPE_FOUND AND NOT WIN32) + add_subdirectory( kfontinst ) +endif() diff --git a/kcms/PURPOSE b/kcms/PURPOSE new file mode 100644 index 0000000000..ef03e579ef --- /dev/null +++ b/kcms/PURPOSE @@ -0,0 +1,10 @@ +System Settings is: +-Desktop Configuration +-User System Configuration + +System Settings is not: +-Application Configuration +-System Administration/Configuration +-Tools/Utilities + +On other environments the already provided equivalent should be used. In KDE the other environment equivalents should not be needed. diff --git a/kcms/access/CMakeLists.txt b/kcms/access/CMakeLists.txt new file mode 100644 index 0000000000..a7903eeab3 --- /dev/null +++ b/kcms/access/CMakeLists.txt @@ -0,0 +1,58 @@ + + + +########### next target ############### +find_package(Phonon4Qt5 REQUIRED NO_MODULE) +include_directories(${PHONON_INCLUDE_DIR}) + +set(kcm_access_PART_SRCS kcmaccess.cpp ) + + +add_library(kcm_access MODULE ${kcm_access_PART_SRCS}) + + +target_link_libraries(kcm_access + Qt5::X11Extras + KF5::KCMUtils + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::NotifyConfig + KF5::WindowSystem + KF5::KDE4Support + ${X11_LIBRARIES} +) + +install(TARGETS kcm_access DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### next target ############### + +set(kaccess_KDEINIT_SRCS kaccess.cpp main.cpp ) + + +kf5_add_kdeinit_executable( kaccess ${kaccess_KDEINIT_SRCS}) + +target_link_libraries(kdeinit_kaccess + Qt5::Widgets + Qt5::X11Extras + KF5::ConfigCore + KF5::Completion + KF5::I18n + KF5::IconThemes + KF5::Notifications + KF5::WidgetsAddons + KF5::WindowSystem + Phonon::phonon4qt5 + KF5::KDE4Support + ${X11_LIBRARIES} +) + +install(TARGETS kdeinit_kaccess ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(TARGETS kaccess ${INSTALL_TARGETS_DEFAULT_ARGS} ) + +########### install files ############### + +install( FILES kcmaccess.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kaccess.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kaccess.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kaccess/ ) diff --git a/kcms/access/Messages.sh b/kcms/access/Messages.sh new file mode 100644 index 0000000000..3834cc09a8 --- /dev/null +++ b/kcms/access/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +### TODO: why do we need 2 POT files for a single directory? +$XGETTEXT kaccess.cpp main.cpp rc.cpp -o $podir/kaccess.pot +$XGETTEXT kcmaccess.cpp rc.cpp -o $podir/kcmaccess.pot diff --git a/kcms/access/kaccess.cpp b/kcms/access/kaccess.cpp new file mode 100644 index 0000000000..87cd688c11 --- /dev/null +++ b/kcms/access/kaccess.cpp @@ -0,0 +1,921 @@ +#include + +#include "kaccess.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include + +#include +#include + +struct ModifierKey { + const unsigned int mask; + const KeySym keysym; + const char *name; + const char *lockedText; + const char *latchedText; + const char *unlatchedText; +}; + +static ModifierKey modifierKeys[] = { + { ShiftMask, 0, "Shift", + I18N_NOOP("The Shift key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Shift key is now active."), + I18N_NOOP("The Shift key is now inactive.") }, + { ControlMask, 0, "Control", + I18N_NOOP("The Control key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Control key is now active."), + I18N_NOOP("The Control key is now inactive.") }, + { 0, XK_Alt_L, "Alt", + I18N_NOOP("The Alt key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Alt key is now active."), + I18N_NOOP("The Alt key is now inactive.") }, + { 0, 0, "Win", + I18N_NOOP("The Win key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Win key is now active."), + I18N_NOOP("The Win key is now inactive.") }, + { 0, XK_Meta_L, "Meta", + I18N_NOOP("The Meta key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Meta key is now active."), + I18N_NOOP("The Meta key is now inactive.") }, + { 0, XK_Super_L, "Super", + I18N_NOOP("The Super key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Super key is now active."), + I18N_NOOP("The Super key is now inactive.") }, + { 0, XK_Hyper_L, "Hyper", + I18N_NOOP("The Hyper key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Hyper key is now active."), + I18N_NOOP("The Hyper key is now inactive.") }, + { 0, 0, "Alt Graph", + I18N_NOOP("The Alt Graph key has been locked and is now active for all of the following keypresses."), + I18N_NOOP("The Alt Graph key is now active."), + I18N_NOOP("The Alt Graph key is now inactive.") }, + { 0, XK_Num_Lock, "Num Lock", + I18N_NOOP("The Num Lock key has been activated."), + "", + I18N_NOOP("The Num Lock key is now inactive.") }, + { LockMask, 0, "Caps Lock", + I18N_NOOP("The Caps Lock key has been activated."), + "", + I18N_NOOP("The Caps Lock key is now inactive.") }, + { 0, XK_Scroll_Lock, "Scroll Lock", + I18N_NOOP("The Scroll Lock key has been activated."), + "", + I18N_NOOP("The Scroll Lock key is now inactive.") }, + { 0, 0, "", "", "", "" } +}; + + +/********************************************************************/ + + +KAccessApp::KAccessApp(bool allowStyles, bool GUIenabled) + : KUniqueApplication(allowStyles, GUIenabled), + overlay(0), _player(0) +{ + _activeWindow = KWindowSystem::activeWindow(); + connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId))); + + features = 0; + requestedFeatures = 0; + dialog = 0; + + initMasks(); + XkbStateRec state_return; + XkbGetState (QX11Info::display(), XkbUseCoreKbd, &state_return); + unsigned char latched = XkbStateMods (&state_return); + unsigned char locked = XkbModLocks (&state_return); + state = ((int)locked)<<8 | latched; +} + +int KAccessApp::newInstance() +{ + KGlobal::config()->reparseConfiguration(); + readSettings(); + return 0; +} + +void KAccessApp::readSettings() +{ + KSharedConfig::Ptr _config = KGlobal::config(); + KConfigGroup cg(_config, "Bell"); + + // bell --------------------------------------------------------------- + _systemBell = cg.readEntry("SystemBell", true); + _artsBell = cg.readEntry("ArtsBell", false); + _currentPlayerSource = cg.readPathEntry("ArtsBellFile", QString()); + _visibleBell = cg.readEntry("VisibleBell", false); + _visibleBellInvert = cg.readEntry("VisibleBellInvert", false); + _visibleBellColor = cg.readEntry("VisibleBellColor", QColor(Qt::red)); + _visibleBellPause = cg.readEntry("VisibleBellPause", 500); + + // select bell events if we need them + int state = (_artsBell || _visibleBell) ? XkbBellNotifyMask : 0; + XkbSelectEvents(QX11Info::display(), XkbUseCoreKbd, XkbBellNotifyMask, state); + + // deactivate system bell if not needed + if (!_systemBell) + XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd, XkbAudibleBellMask, 0); + else + XkbChangeEnabledControls(QX11Info::display(), XkbUseCoreKbd, XkbAudibleBellMask, XkbAudibleBellMask); + + // keyboard ------------------------------------------------------------- + KConfigGroup keyboardGroup(_config,"Keyboard"); + + // get keyboard state + XkbDescPtr xkb = XkbGetMap(QX11Info::display(), 0, XkbUseCoreKbd); + if (!xkb) + return; + if (XkbGetControls(QX11Info::display(), XkbAllControlsMask, xkb) != Success) + return; + + // sticky keys + if (keyboardGroup.readEntry("StickyKeys", false)) + { + if (keyboardGroup.readEntry("StickyKeysLatch", true)) + xkb->ctrls->ax_options |= XkbAX_LatchToLockMask; + else + xkb->ctrls->ax_options &= ~XkbAX_LatchToLockMask; + if (keyboardGroup.readEntry("StickyKeysAutoOff", false)) + xkb->ctrls->ax_options |= XkbAX_TwoKeysMask; + else + xkb->ctrls->ax_options &= ~XkbAX_TwoKeysMask; + if (keyboardGroup.readEntry("StickyKeysBeep", false)) + xkb->ctrls->ax_options |= XkbAX_StickyKeysFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_StickyKeysFBMask; + xkb->ctrls->enabled_ctrls |= XkbStickyKeysMask; + } + else + xkb->ctrls->enabled_ctrls &= ~XkbStickyKeysMask; + + // toggle keys + if (keyboardGroup.readEntry("ToggleKeysBeep", false)) + xkb->ctrls->ax_options |= XkbAX_IndicatorFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_IndicatorFBMask; + + // slow keys + if (keyboardGroup.readEntry("SlowKeys", false)) { + if (keyboardGroup.readEntry("SlowKeysPressBeep", false)) + xkb->ctrls->ax_options |= XkbAX_SKPressFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_SKPressFBMask; + if (keyboardGroup.readEntry("SlowKeysAcceptBeep", false)) + xkb->ctrls->ax_options |= XkbAX_SKAcceptFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_SKAcceptFBMask; + if (keyboardGroup.readEntry("SlowKeysRejectBeep", false)) + xkb->ctrls->ax_options |= XkbAX_SKRejectFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_SKRejectFBMask; + xkb->ctrls->enabled_ctrls |= XkbSlowKeysMask; + } + else + xkb->ctrls->enabled_ctrls &= ~XkbSlowKeysMask; + xkb->ctrls->slow_keys_delay = keyboardGroup.readEntry("SlowKeysDelay", 500); + + // bounce keys + if (keyboardGroup.readEntry("BounceKeys", false)) { + if (keyboardGroup.readEntry("BounceKeysRejectBeep", false)) + xkb->ctrls->ax_options |= XkbAX_BKRejectFBMask; + else + xkb->ctrls->ax_options &= ~XkbAX_BKRejectFBMask; + xkb->ctrls->enabled_ctrls |= XkbBounceKeysMask; + } + else + xkb->ctrls->enabled_ctrls &= ~XkbBounceKeysMask; + xkb->ctrls->debounce_delay = keyboardGroup.readEntry("BounceKeysDelay", 500); + + // gestures for enabling the other features + _gestures = keyboardGroup.readEntry("Gestures", false); + if (_gestures) + xkb->ctrls->enabled_ctrls |= XkbAccessXKeysMask; + else + xkb->ctrls->enabled_ctrls &= ~XkbAccessXKeysMask; + + // timeout + if (keyboardGroup.readEntry("AccessXTimeout", false)) + { + xkb->ctrls->ax_timeout = keyboardGroup.readEntry("AccessXTimeoutDelay", 30)*60; + xkb->ctrls->axt_opts_mask = 0; + xkb->ctrls->axt_opts_values = 0; + xkb->ctrls->axt_ctrls_mask = XkbStickyKeysMask | XkbSlowKeysMask; + xkb->ctrls->axt_ctrls_values = 0; + xkb->ctrls->enabled_ctrls |= XkbAccessXTimeoutMask; + } + else + xkb->ctrls->enabled_ctrls &= ~XkbAccessXTimeoutMask; + + // gestures for enabling the other features + if (keyboardGroup.readEntry("AccessXBeep", true)) + xkb->ctrls->ax_options |= XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask; + else + xkb->ctrls->ax_options &= ~(XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask); + + _gestureConfirmation = keyboardGroup.readEntry("GestureConfirmation", false); + + _kNotifyModifiers = keyboardGroup.readEntry("kNotifyModifiers", false); + _kNotifyAccessX = keyboardGroup.readEntry("kNotifyAccessX", false); + + // mouse-by-keyboard ---------------------------------------------- + + KConfigGroup mouseGroup(_config,"Mouse"); + + if (mouseGroup.readEntry("MouseKeys", false)) + { + xkb->ctrls->mk_delay = mouseGroup.readEntry("MKDelay", 160); + + // Default for initial velocity: 200 pixels/sec + int interval = mouseGroup.readEntry("MKInterval", 5); + xkb->ctrls->mk_interval = interval; + + // Default time to reach maximum speed: 5000 msec + xkb->ctrls->mk_time_to_max = mouseGroup.readEntry("MKTimeToMax", + (5000+interval/2)/interval); + + // Default maximum speed: 1000 pixels/sec + // (The old default maximum speed from KDE <= 3.4 + // (100000 pixels/sec) was way too fast) + xkb->ctrls->mk_max_speed = mouseGroup.readEntry("MKMaxSpeed", interval); + + xkb->ctrls->mk_curve = mouseGroup.readEntry("MKCurve", 0); + xkb->ctrls->mk_dflt_btn = mouseGroup.readEntry("MKDefaultButton", 0); + + xkb->ctrls->enabled_ctrls |= XkbMouseKeysMask; + } + else + xkb->ctrls->enabled_ctrls &= ~XkbMouseKeysMask; + + features = xkb->ctrls->enabled_ctrls & (XkbSlowKeysMask | XkbBounceKeysMask | XkbStickyKeysMask | XkbMouseKeysMask); + if (dialog == 0) + requestedFeatures = features; + // set state + XkbSetControls(QX11Info::display(), XkbControlsEnabledMask | XkbMouseKeysAccelMask | XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbAccessXKeysMask | XkbAccessXTimeoutMask, xkb); + + // select AccessX events + XkbSelectEvents(QX11Info::display(), XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask); + + // do we need to stay running to perform notifications? + if (!_artsBell && !_visibleBell && !(_gestures && _gestureConfirmation) + && !_kNotifyModifiers && !_kNotifyAccessX) { + + // We will exit, but the features need to stay configured + uint ctrls = XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbMouseKeysMask | XkbAudibleBellMask | XkbControlsNotifyMask; + uint values = xkb->ctrls->enabled_ctrls & ctrls; + XkbSetAutoResetControls(QX11Info::display(), ctrls, &ctrls, &values); + exit(0); + } else { + // reset them after program exit + uint ctrls = XkbStickyKeysMask | XkbSlowKeysMask | XkbBounceKeysMask | XkbMouseKeysMask | XkbAudibleBellMask | XkbControlsNotifyMask; + uint values = XkbAudibleBellMask; + XkbSetAutoResetControls(QX11Info::display(), ctrls, &ctrls, &values); + } + + delete overlay; + overlay = 0; +} + +static int maskToBit (int mask) { + for (int i = 0; i < 8; i++) + if (mask & (1 << i)) + return i; + return -1; +} + +void KAccessApp::initMasks() { + for (int i = 0; i < 8; i++) + keys [i] = -1; + state = 0; + + for (int i = 0; strcmp (modifierKeys[i].name, "") != 0; i++) { + int mask = modifierKeys[i].mask; + if (mask == 0) { + if (modifierKeys[i].keysym != 0) { + mask = XkbKeysymToModifiers (QX11Info::display(), modifierKeys[i].keysym); + } else { + if (!strcmp(modifierKeys[i].name, "Win")) { + mask = KKeyServer::modXMeta(); + } else { + mask = XkbKeysymToModifiers (QX11Info::display(), XK_Mode_switch) + | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Shift) + | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Latch) + | XkbKeysymToModifiers (QX11Info::display(), XK_ISO_Level3_Lock); + } + } + } + + + int bit = maskToBit (mask); + if (bit != -1 && keys[bit] == -1) + keys[bit] = i; + } +} + +#warning x11EventFilter needs porting to QAbstractNativeEventFilter +#if 0 +bool KAccessApp::x11EventFilter(XEvent *event) +{ + // handle XKB events + if (event->type == xkb_opcode) + { + XkbAnyEvent *ev = (XkbAnyEvent*) event; + + switch (ev->xkb_type) { + case XkbStateNotify: + xkbStateNotify(); + break; + case XkbBellNotify: + xkbBellNotify((XkbBellNotifyEvent*)event); + break; + case XkbControlsNotify: + xkbControlsNotify((XkbControlsNotifyEvent*)event); + break; + } + return true; + } + + // process other events as usual + return KApplication::x11EventFilter(event); +} +#endif + + +void VisualBell::paintEvent(QPaintEvent *event) +{ + QWidget::paintEvent(event); + QTimer::singleShot(_pause, this, SLOT(hide())); +} + + +void KAccessApp::activeWindowChanged(WId wid) +{ + _activeWindow = wid; +} + + +void KAccessApp::xkbStateNotify () { + XkbStateRec state_return; + XkbGetState (QX11Info::display(), XkbUseCoreKbd, &state_return); + unsigned char latched = XkbStateMods (&state_return); + unsigned char locked = XkbModLocks (&state_return); + int mods = ((int)locked)<<8 | latched; + + if (state != mods) { + if (_kNotifyModifiers) + for (int i = 0; i < 8; i++) { + if (keys[i] != -1) { + if ( !strcmp(modifierKeys[keys[i]].latchedText, "") + && ( (((mods >> i) & 0x101) != 0) != (((state >> i) & 0x101) != 0) )) + { + if ((mods >> i) & 1) { + KNotification::event ("lockkey-locked", i18n(modifierKeys[keys[i]].lockedText)); + } + else { + KNotification::event ("lockkey-unlocked", i18n(modifierKeys[keys[i]].unlatchedText)); + } + } + else if (strcmp(modifierKeys[keys[i]].latchedText, "") + && ( ((mods >> i) & 0x101) != ((state >> i) & 0x101) )) + { + if ((mods >> i) & 0x100) { + KNotification::event ("modifierkey-locked", i18n(modifierKeys[keys[i]].lockedText)); + } + else if ((mods >> i) & 1) { + KNotification::event ( "modifierkey-latched", i18n(modifierKeys[keys[i]].latchedText)); + } + else { + KNotification::event ("modifierkey-unlatched", i18n(modifierKeys[keys[i]].unlatchedText)); + } + } + } + } + state = mods; + } +} + +void KAccessApp::xkbBellNotify(XkbBellNotifyEvent *event) +{ + // bail out if we should not really ring + if (event->event_only) + return; + + // flash the visible bell + if (_visibleBell) + { + // create overlay widget + if (!overlay) + overlay = new VisualBell(_visibleBellPause); + + WId id = _activeWindow; + + NETRect frame, window; + NETWinInfo net(QX11Info::connection(), id, desktop()->winId(), 0); + + net.kdeGeometry(frame, window); + + overlay->setGeometry(window.pos.x, window.pos.y, window.size.width, window.size.height); + + if (_visibleBellInvert) + { + QPixmap screen = QPixmap::grabWindow(id, 0, 0, window.size.width, window.size.height); +#ifdef __GNUC__ +#warning is this the best way to invert a pixmap? +#endif +// QPixmap invert(window.size.width, window.size.height); + QImage i = screen.toImage(); + i.invertPixels(); + QPalette pal = overlay->palette(); + pal.setBrush(overlay->backgroundRole(), QBrush(QPixmap::fromImage(i))); + overlay->setPalette(pal); +/* + QPainter p(&invert); + p.setRasterOp(QPainter::NotCopyROP); + p.drawPixmap(0, 0, screen); + overlay->setBackgroundPixmap(invert); +*/ + } + else + { + QPalette pal = overlay->palette(); + pal.setColor(overlay->backgroundRole(), _visibleBellColor); + overlay->setPalette(pal); + } + + // flash the overlay widget + overlay->raise(); + overlay->show(); + flush(); + } + + // ask Phonon to ring a nice bell + if (_artsBell) { + if (!_player) { // as creating the player is expensive, delay the creation + _player = Phonon::createPlayer(Phonon::AccessibilityCategory); + _player->setParent(this); + _player->setCurrentSource(_currentPlayerSource); + } + _player->play(); + } +} + +QString mouseKeysShortcut (Display *display) { + // Calculate the keycode + KeySym sym = XK_MouseKeys_Enable; + KeyCode code = XKeysymToKeycode(display, sym); + if (code == 0) { + sym = XK_Pointer_EnableKeys; + code = XKeysymToKeycode(display, sym); + if (code == 0) + return ""; // No shortcut available? + } + + // Calculate the modifiers by searching the keysym in the X keyboard mapping + XkbDescPtr xkbdesc = XkbGetMap(display, XkbKeyTypesMask | XkbKeySymsMask, XkbUseCoreKbd); + + if (!xkbdesc) + return ""; // Failed to obtain the mapping from server + + bool found = false; + unsigned char modifiers = 0; + int groups = XkbKeyNumGroups(xkbdesc, code); + for (int grp = 0; grp < groups && !found; grp++) + { + int levels = XkbKeyGroupWidth(xkbdesc, code, grp); + for (int level = 0; level < levels && !found; level++) + { + if (sym == XkbKeySymEntry(xkbdesc, code, level, grp)) + { + // keysym found => determine modifiers + int typeIdx = xkbdesc->map->key_sym_map[code].kt_index[grp]; + XkbKeyTypePtr type = &(xkbdesc->map->types[typeIdx]); + for (int i = 0; i < type->map_count && !found; i++) + { + if (type->map[i].active && (type->map[i].level == level)) + { + modifiers = type->map[i].mods.mask; + found = true; + } + } + } + } + } + XkbFreeClientMap (xkbdesc, 0, true); + + if (!found) + return ""; // Somehow the keycode -> keysym mapping is flawed + + XEvent ev; + ev.type = KeyPress; + ev.xkey.display = display; + ev.xkey.keycode = code; + ev.xkey.state = 0; + int key; + KKeyServer::xEventToQt(&ev, &key); + QString keyname = QKeySequence(key).toString(); + + unsigned int AltMask = KKeyServer::modXAlt(); + unsigned int WinMask = KKeyServer::modXMeta(); + unsigned int NumMask = KKeyServer::modXNumLock(); + unsigned int ScrollMask= KKeyServer::modXScrollLock(); + + unsigned int MetaMask = XkbKeysymToModifiers (display, XK_Meta_L); + unsigned int SuperMask = XkbKeysymToModifiers (display, XK_Super_L); + unsigned int HyperMask = XkbKeysymToModifiers (display, XK_Hyper_L); + unsigned int AltGrMask = XkbKeysymToModifiers (display, XK_Mode_switch) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Shift) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Latch) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Lock); + + unsigned int mods = ShiftMask | ControlMask | AltMask | WinMask + | LockMask | NumMask | ScrollMask; + + AltGrMask &= ~mods; + MetaMask &= ~(mods | AltGrMask); + SuperMask &= ~(mods | AltGrMask | MetaMask); + HyperMask &= ~(mods | AltGrMask | MetaMask | SuperMask); + + if ((modifiers & AltGrMask) != 0) + keyname = i18n("AltGraph") + '+' + keyname; + if ((modifiers & HyperMask) != 0) + keyname = i18n("Hyper") + '+' + keyname; + if ((modifiers & SuperMask) != 0) + keyname = i18n("Super") + '+' + keyname; + if ((modifiers & WinMask) != 0) + keyname = i18n("Meta") + '+' + keyname; + if ((modifiers & WinMask) != 0) + keyname = QKeySequence(Qt::META).toString() + '+' + keyname; + if ((modifiers & AltMask) != 0) + keyname = QKeySequence(Qt::ALT).toString() + '+' + keyname; + if ((modifiers & ControlMask) != 0) + keyname = QKeySequence(Qt::CTRL).toString() + '+' + keyname; + if ((modifiers & ShiftMask) != 0) + keyname = QKeySequence(Qt::SHIFT).toString() + '+' + keyname; + + return keyname; +} + +void KAccessApp::createDialogContents() { + if (dialog == 0) { + dialog = new KDialog( 0 ); + dialog->setCaption( i18n("Warning") ); + dialog->setButtons( KDialog::Yes | KDialog::No ); + dialog->setButtonGuiItem( KDialog::Yes, KStandardGuiItem::yes() ); + dialog->setButtonGuiItem( KDialog::No, KStandardGuiItem::no() ); + dialog->setDefaultButton( KDialog::No ); + dialog->setEscapeButton( KDialog::Close ); + dialog->setObjectName( "AccessXWarning" ); + dialog->setModal( true ); + + KVBox *topcontents = new KVBox (dialog); + topcontents->setSpacing(KDialog::spacingHint()*2); +#ifdef __GNUC__ +#warning "kde4 fixme" +#endif + //topcontents->setMargin(KDialog::marginHint()); + + QWidget *contents = new QWidget(topcontents); + QHBoxLayout * lay = new QHBoxLayout(contents); + lay->setSpacing(KDialog::spacingHint()); + + QLabel *label1 = new QLabel( contents); + QPixmap pixmap = KIconLoader::global()->loadIcon("dialog-warning", KIconLoader::NoGroup, KIconLoader::SizeMedium, KIconLoader::DefaultState, QStringList(), 0, true); + if (pixmap.isNull()) + pixmap = QMessageBox::standardIcon(QMessageBox::Warning); + label1->setPixmap(pixmap); + + lay->addWidget( label1, 0, Qt::AlignCenter ); + lay->addSpacing(KDialog::spacingHint()); + + QVBoxLayout * vlay = new QVBoxLayout(); + lay->addItem( vlay ); + + featuresLabel = new QLabel( "", contents ); + featuresLabel->setAlignment( Qt::AlignVCenter ); + featuresLabel->setWordWrap( true ); + vlay->addWidget( featuresLabel ); + vlay->addStretch(); + + QHBoxLayout * hlay = new QHBoxLayout(); + vlay->addItem(hlay); + + QLabel *showModeLabel = new QLabel( i18n("&When a gesture was used:"), contents ); + hlay->addWidget( showModeLabel ); + + showModeCombobox = new KComboBox (contents); + hlay->addWidget( showModeCombobox ); + showModeLabel->setBuddy(showModeCombobox); + showModeCombobox->insertItem ( 0, i18n("Change Settings Without Asking")); + showModeCombobox->insertItem ( 1, i18n("Show This Confirmation Dialog")); + showModeCombobox->insertItem ( 2, i18n("Deactivate All AccessX Features & Gestures")); + showModeCombobox->setCurrentIndex (1); + + dialog->setMainWidget(topcontents); + + connect (dialog, SIGNAL(yesClicked()), this, SLOT(yesClicked())); + connect (dialog, SIGNAL(noClicked()), this, SLOT(noClicked())); + connect (dialog, SIGNAL(closeClicked()), this, SLOT(dialogClosed())); + } +} + +void KAccessApp::xkbControlsNotify(XkbControlsNotifyEvent *event) +{ + unsigned int newFeatures = event->enabled_ctrls & (XkbSlowKeysMask | XkbBounceKeysMask | XkbStickyKeysMask | XkbMouseKeysMask); + + if (newFeatures != features) { + unsigned int enabled = newFeatures & ~features; + unsigned int disabled = features & ~newFeatures; + + if (!_gestureConfirmation) { + requestedFeatures = enabled | (requestedFeatures & ~disabled); + notifyChanges(); + features = newFeatures; + } + else { + // set the AccessX features back to what they were. We will + // apply the changes later if the user allows us to do that. + readSettings(); + + requestedFeatures = enabled | (requestedFeatures & ~disabled); + + enabled = requestedFeatures & ~features; + disabled = features & ~requestedFeatures; + + QStringList enabledFeatures; + QStringList disabledFeatures; + + if (enabled & XkbStickyKeysMask) + enabledFeatures << i18n("Sticky keys"); + else if (disabled & XkbStickyKeysMask) + disabledFeatures << i18n("Sticky keys"); + + if (enabled & XkbSlowKeysMask) + enabledFeatures << i18n("Slow keys"); + else if (disabled & XkbSlowKeysMask) + disabledFeatures << i18n("Slow keys"); + + if (enabled & XkbBounceKeysMask) + enabledFeatures << i18n("Bounce keys"); + else if (disabled & XkbBounceKeysMask) + disabledFeatures << i18n("Bounce keys"); + + if (enabled & XkbMouseKeysMask) + enabledFeatures << i18n("Mouse keys"); + else if (disabled & XkbMouseKeysMask) + disabledFeatures << i18n("Mouse keys"); + + QString question; + switch (enabledFeatures.count()) { + case 0: switch (disabledFeatures.count()) { + case 1: question = i18n("Do you really want to deactivate \"%1\"?", + disabledFeatures[0]); + break; + case 2: question = i18n("Do you really want to deactivate \"%1\" and \"%2\"?", + disabledFeatures[0], disabledFeatures[1]); + break; + case 3: question = i18n("Do you really want to deactivate \"%1\", \"%2\" and \"%3\"?", + disabledFeatures[0], disabledFeatures[1], + disabledFeatures[2]); + break; + case 4: question = i18n("Do you really want to deactivate \"%1\", \"%2\", \"%3\" and \"%4\"?", + disabledFeatures[0], disabledFeatures[1], + disabledFeatures[2], disabledFeatures[3]); + break; + } + break; + case 1: switch (disabledFeatures.count()) { + case 0: question = i18n("Do you really want to activate \"%1\"?", + enabledFeatures[0]); + break; + case 1: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\"?", + enabledFeatures[0], disabledFeatures[0]); + break; + case 2: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\" and \"%3\"?", + enabledFeatures[0], disabledFeatures[0], + disabledFeatures[1]); + break; + case 3: question = i18n("Do you really want to activate \"%1\" and to deactivate \"%2\", \"%3\" and \"%4\"?", + enabledFeatures[0], disabledFeatures[0], + disabledFeatures[1], disabledFeatures[2]); + break; + } + break; + case 2: switch (disabledFeatures.count()) { + case 0: question = i18n("Do you really want to activate \"%1\" and \"%2\"?", + enabledFeatures[0], enabledFeatures[1]); + break; + case 1: question = i18n("Do you really want to activate \"%1\" and \"%2\" and to deactivate \"%3\"?", + enabledFeatures[0], enabledFeatures[1], + disabledFeatures[0]); + break; + case 2: question = i18n("Do you really want to activate \"%1\", and \"%2\" and to deactivate \"%3\" and \"%4\"?", + enabledFeatures[0], enabledFeatures[1], + enabledFeatures[0], disabledFeatures[1]); + break; + } + break; + case 3: switch (disabledFeatures.count()) { + case 0: question = i18n("Do you really want to activate \"%1\", \"%2\" and \"%3\"?", + enabledFeatures[0], enabledFeatures[1], + enabledFeatures[2]); + break; + case 1: question = i18n("Do you really want to activate \"%1\", \"%2\" and \"%3\" and to deactivate \"%4\"?", + enabledFeatures[0], enabledFeatures[1], + enabledFeatures[2], disabledFeatures[0]); + break; + } + break; + case 4: question = i18n("Do you really want to activate \"%1\", \"%2\", \"%3\" and \"%4\"?", + enabledFeatures[0], enabledFeatures[1], + enabledFeatures[2], enabledFeatures[3]); + break; + } + QString explanation; + if (enabledFeatures.count()+disabledFeatures.count() == 1) { + explanation = i18n("An application has requested to change this setting."); + + if (_gestures) { + if ((enabled | disabled) == XkbSlowKeysMask) + explanation = i18n("You held down the Shift key for 8 seconds or an application has requested to change this setting."); + else if ((enabled | disabled) == XkbStickyKeysMask) + explanation = i18n("You pressed the Shift key 5 consecutive times or an application has requested to change this setting."); + else if ((enabled | disabled) == XkbMouseKeysMask) { + QString shortcut = mouseKeysShortcut(QX11Info::display()); + if (!shortcut.isEmpty() && !shortcut.isNull()) + explanation = i18n("You pressed %1 or an application has requested to change this setting.", shortcut); + } + } + } + else { + if (_gestures) + explanation = i18n("An application has requested to change these settings, or you used a combination of several keyboard gestures."); + else + explanation = i18n("An application has requested to change these settings."); + } + + createDialogContents(); + featuresLabel->setText ( question+"\n\n"+explanation + +" "+i18n("These AccessX settings are needed for some users with motion impairments and can be configured in the KDE System Settings. You can also turn them on and off with standardized keyboard gestures.\n\nIf you do not need them, you can select \"Deactivate all AccessX features and gestures\".") ); + + KWindowSystem::setState( dialog->winId(), NET::KeepAbove ); + kapp->updateUserTimestamp(); + dialog->show(); + } + } +} + +void KAccessApp::notifyChanges() { + if (!_kNotifyAccessX) + return; + + unsigned int enabled = requestedFeatures & ~features; + unsigned int disabled = features & ~requestedFeatures; + + if (enabled & XkbSlowKeysMask) + KNotification::event ("slowkeys", i18n("Slow keys has been enabled. From now on, you need to press each key for a certain length of time before it gets accepted.")); + else if (disabled & XkbSlowKeysMask) + KNotification::event ("slowkeys", i18n("Slow keys has been disabled.")); + + if (enabled & XkbBounceKeysMask) + KNotification::event ("bouncekeys", i18n("Bounce keys has been enabled. From now on, each key will be blocked for a certain length of time after it was used.")); + else if (disabled & XkbBounceKeysMask) + KNotification::event ("bouncekeys", i18n("Bounce keys has been disabled.")); + + if (enabled & XkbStickyKeysMask) + KNotification::event ("stickykeys", i18n("Sticky keys has been enabled. From now on, modifier keys will stay latched after you have released them.")); + else if (disabled & XkbStickyKeysMask) + KNotification::event ("stickykeys", i18n("Sticky keys has been disabled.")); + + if (enabled & XkbMouseKeysMask) + KNotification::event ("mousekeys", i18n("Mouse keys has been enabled. From now on, you can use the number pad of your keyboard in order to control the mouse.")); + else if (disabled & XkbMouseKeysMask) + KNotification::event ("mousekeys", i18n("Mouse keys has been disabled.")); +} + +void KAccessApp::applyChanges() { + notifyChanges(); + unsigned int enabled = requestedFeatures & ~features; + unsigned int disabled = features & ~requestedFeatures; + + KConfigGroup config(KGlobal::config(), "Keyboard"); + + if (enabled & XkbSlowKeysMask) + config.writeEntry("SlowKeys", true); + else if (disabled & XkbSlowKeysMask) + config.writeEntry("SlowKeys", false); + + if (enabled & XkbBounceKeysMask) + config.writeEntry("BounceKeys", true); + else if (disabled & XkbBounceKeysMask) + config.writeEntry("BounceKeys", false); + + if (enabled & XkbStickyKeysMask) + config.writeEntry("StickyKeys", true); + else if (disabled & XkbStickyKeysMask) + config.writeEntry("StickyKeys", false); + + KConfigGroup mousegrp(KGlobal::config(),"Mouse"); + + if (enabled & XkbMouseKeysMask) + mousegrp.writeEntry("MouseKeys", true); + else if (disabled & XkbMouseKeysMask) + mousegrp.writeEntry("MouseKeys", false); + mousegrp.sync(); + config.sync(); +} + +void KAccessApp::yesClicked() { + if (dialog != 0) + dialog->deleteLater(); + dialog = 0; + + KConfigGroup config(KGlobal::config(), "Keyboard"); + switch (showModeCombobox->currentIndex()) { + case 0: + config.writeEntry("Gestures", true); + config.writeEntry("GestureConfirmation", false); + break; + default: + config.writeEntry("Gestures", true); + config.writeEntry("GestureConfirmation", true); + break; + case 2: + requestedFeatures = 0; + config.writeEntry("Gestures", false); + config.writeEntry("GestureConfirmation", true); + } + config.sync(); + + if (features != requestedFeatures) { + notifyChanges(); + applyChanges(); + } + readSettings(); +} + +void KAccessApp::noClicked() { + if (dialog != 0) + dialog->deleteLater(); + dialog = 0; + requestedFeatures = features; + + KConfigGroup config(KGlobal::config(), "Keyboard"); + switch (showModeCombobox->currentIndex()) { + case 0: + config.writeEntry("Gestures", true); + config.writeEntry("GestureConfirmation", false); + break; + default: + config.writeEntry("Gestures", true); + config.writeEntry("GestureConfirmation", true); + break; + case 2: + requestedFeatures = 0; + config.writeEntry("Gestures", false); + config.writeEntry("GestureConfirmation", true); + } + config.sync(); + + if (features != requestedFeatures) + applyChanges(); + readSettings(); +} + +void KAccessApp::dialogClosed() { + if (dialog != 0) + dialog->deleteLater(); + dialog = 0; + + requestedFeatures = features; +} + +void KAccessApp::setXkbOpcode(int opcode) { + xkb_opcode = opcode; +} + +#include "kaccess.moc" diff --git a/kcms/access/kaccess.desktop b/kcms/access/kaccess.desktop new file mode 100644 index 0000000000..3e0262ea73 --- /dev/null +++ b/kcms/access/kaccess.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +Type=Service +Name=KDE Accessibility Tool +Name[af]=KDE toeganklikheid program +Name[ar]=أداة كدي للإتاحة +Name[ast]=Ferramienta d'accesibilidad de KDE +Name[be]=Інструмент даступнасці KDE +Name[be@latin]=Pryłada dastupnaści KDE +Name[bg]=Равностоен достъп +Name[bn]=কে.ডি.ই. সহায়ক প্রযুক্তি টুল +Name[br]=Ostilh haezadusted KDE +Name[bs]=KDE alat za pristupačnost +Name[ca]=Eina d'accessibilitat KDE +Name[ca@valencia]=Eina d'accessibilitat KDE +Name[cs]=Nástroj pro zpřístupnění prostředí KDE +Name[csb]=Pòmòce przëstãpù KDE +Name[cy]=Erfyn Hygyrchedd KDE +Name[da]=KDE Tilgængelighedsværktøj +Name[de]=KDE-Zugangshilfen +Name[el]=Εργαλείο προσιτότητας του KDE +Name[en_GB]=KDE Accessibility Tool +Name[eo]=KDE-ilo por alirebleco +Name[es]=Herramienta de accesibilidad de KDE +Name[et]=KDE hõlbustustööriist +Name[eu]=KDEren erabilerraztasun-tresna +Name[fa]=ابزار دستیابی‌پذیری KDE +Name[fi]=KDE:n esteettömyystyökalu +Name[fr]=Outil d'accessibilité de KDE +Name[fy]=KDE Tagonklikens +Name[ga]=Uirlis Inrochtaineachta KDE +Name[gl]=Utilidade de accesibilidade de KDE +Name[gu]=KDE ઉપયોગિતા સાધન +Name[he]=כלי הנגישות של KDE +Name[hi]=केडीई पहुँच औज़ार +Name[hne]=केडीई पहुंच औजार +Name[hr]=KDE pristupačnost +Name[hsb]=KDE pomocnik za přistupnosć +Name[hu]=KDE kezelési segédeszköz +Name[ia]=Instrumento de Accessibilitate de KDE +Name[id]=Alat Aksesibilitas KDE +Name[is]=KDE aðgengistól +Name[it]=Strumento per l'accessibilità di KDE +Name[ja]=KDE アクセシビリティ支援ツール +Name[ka]=KDE-ს სპეცეციალური შესაძლებლობების ხელსაწყოები +Name[kk]=KDE арнайы мүмкіндіктер құралы +Name[km]=ឧបករណ៍​មធ្យោបាយ​ងាយស្រួល​របស់ KDE +Name[kn]=ಕೆಡಿಇ ನಿಲುಕಣೆ (ಅಕ್ಸೆಸಿಬಲಿಟಿ) ಸಲಕರಣೆ +Name[ko]=KDE 접근성 도구 +Name[ku]=KDE Amûra Xwegihandinê +Name[lt]=KDE pritaikymo neįgaliesiems įrankis +Name[lv]=KDE pieejamības rīks +Name[mai]=केडीई पहुँच अओजार +Name[mk]=Алатка за пристапливост во KDE +Name[ml]=കെഡിഇ സാമീപ്യതാ ഉപകരണം +Name[mr]=केडीई सुलभता साधन +Name[ms]=Alat Keaksesan KDE +Name[nb]=Tilgjengelighetsverktøy for KDE +Name[nds]=KDE-Togangwarktüüch +Name[ne]=केडीई पहुँचयोग्य उपकरण +Name[nl]=KDE Toegankelijkheid +Name[nn]=Tilgjengeverktøy for KDE +Name[oc]=Esplech d'accessibilitat de KDE +Name[or]=KDE ଅଭିଗମ୍ୟତା ଉପକରଣ +Name[pa]=KDE ਸਹਾਇਕ ਟੂਲ +Name[pl]=Narzędzie ułatwień dostępu KDE +Name[pt]=Ferramenta de Acessibilidade do KDE +Name[pt_BR]=Ferramenta de Acessibilidade do KDE +Name[ro]=Utilitar de accesibilitate pentru KDE +Name[ru]=Специальные возможности +Name[se]=KDE reaidu álkkibut geavaheami várás +Name[si]=KDE ප්‍රවේශන මෙවලම +Name[sk]=Nástroj na sprístupnenie KDE +Name[sl]=Orodje za dostopnost KDE +Name[sr]=КДЕ‑ова алатка за приступачност +Name[sr@ijekavian]=КДЕ‑ова алатка за приступачност +Name[sr@ijekavianlatin]=KDE‑ova alatka za pristupačnost +Name[sr@latin]=KDE‑ova alatka za pristupačnost +Name[sv]=Handikappverktyg för KDE +Name[ta]=KDE அணுகும் கருவி +Name[te]=KDE అందుబాటు సాధనం +Name[tg]=Имкониятҳои KDE +Name[th]=เครื่องมือปรับการช่วยการใช้งานของ KDE +Name[tr]=KDE Erişilebilirlik Aracı +Name[ug]=ك د ئې(KDE) ياردەمچى ئىقتىدار قوراللىرى +Name[uk]=Утиліта керування доступності KDE +Name[uz]=KDE qulaylik vositasi +Name[uz@cyrillic]=KDE қулайлик воситаси +Name[vi]=Công cụ hỗ trợ truy cập của KDE +Name[wa]=Usteye d' accessibilité di KDE +Name[x-test]=xxKDE Accessibility Toolxx +Name[zh_CN]=KDE 辅助工具 +Name[zh_TW]=KDE 無障礙工具 +Exec=kaccess +X-DBUS-StartupType=None +X-KDE-Library=kcm_access +X-KDE-ParentApp=kcontrol +X-DocPath=kcontrol/kcmaccess/index.html diff --git a/kcms/access/kaccess.h b/kcms/access/kaccess.h new file mode 100644 index 0000000000..716bfcf338 --- /dev/null +++ b/kcms/access/kaccess.h @@ -0,0 +1,118 @@ +#ifndef __K_ACCESS_H__ +#define __K_ACCESS_H__ + + +#include +#include +//Added by qt3to4: +#include +#include + + +#include +#include + +#include + +#include +#define explicit int_explicit // avoid compiler name clash in XKBlib.h +#include +#undef explicit +#include + +class KDialog; +class QLabel; +class KComboBox; + +class KAccessApp : public KUniqueApplication +{ + Q_OBJECT + +public: + + explicit KAccessApp(bool allowStyles=true, bool GUIenabled=true); + +#if 0 + bool x11EventFilter(XEvent *event); +#endif + + int newInstance(); + + void setXkbOpcode(int opcode); + +protected: + + void readSettings(); + + void xkbStateNotify(); + void xkbBellNotify(XkbBellNotifyEvent *event); + void xkbControlsNotify(XkbControlsNotifyEvent *event); + + +private Q_SLOTS: + + void activeWindowChanged(WId wid); + void notifyChanges(); + void applyChanges(); + void yesClicked(); + void noClicked(); + void dialogClosed(); + + +private: + void createDialogContents(); + void initMasks(); + + int xkb_opcode; + unsigned int features; + unsigned int requestedFeatures; + + bool _systemBell, _artsBell, _visibleBell, _visibleBellInvert; + QColor _visibleBellColor; + int _visibleBellPause; + + bool _gestures, _gestureConfirmation; + bool _kNotifyModifiers, _kNotifyAccessX; + + QWidget *overlay; + + Phonon::MediaObject *_player; + QString _currentPlayerSource; + + WId _activeWindow; + + KDialog *dialog; + QLabel *featuresLabel; + KComboBox *showModeCombobox; + + int keys[8]; + int state; +}; + + +class VisualBell : public QWidget +{ + Q_OBJECT + +public: + + VisualBell(int pause) + : QWidget(( QWidget* )0, Qt::X11BypassWindowManagerHint), _pause(pause) + {} + + +protected: + + void paintEvent(QPaintEvent *); + + +private: + + int _pause; + +}; + + + + +#endif diff --git a/kcms/access/kaccess.notifyrc b/kcms/access/kaccess.notifyrc new file mode 100644 index 0000000000..9a228f7bfa --- /dev/null +++ b/kcms/access/kaccess.notifyrc @@ -0,0 +1,1532 @@ +[Global] +IconName=preferences-desktop-accessibility +Comment=Accessibility +Comment[af]=Toeganklikheid +Comment[ar]=الإتاحة +Comment[as]=অভিগম্যতা +Comment[ast]=Accesibilidá +Comment[be]=Даступнасць +Comment[be@latin]=Dastupnaść +Comment[bg]=Равностоен достъп +Comment[bn]=সহায়ক প্রযুক্তি +Comment[bn_IN]=বিশেষ ব্যবহারের সহায়তা +Comment[br]=Haezadusted +Comment[bs]=Pristupačnost +Comment[ca]=Accessibilitat +Comment[ca@valencia]=Accessibilitat +Comment[cs]=Zpřístupnění +Comment[csb]=Pòmòce przëstãpù +Comment[cy]=Hygyrchedd +Comment[da]=Tilgængelighed +Comment[de]=Zugangshilfen +Comment[el]=Προσβασιμότητα +Comment[en_GB]=Accessibility +Comment[eo]=Alirebleco +Comment[es]=Accesibilidad +Comment[et]=Hõlbustus +Comment[eu]=Erabilerraztasuna +Comment[fa]=دستیابی‌پذیری +Comment[fi]=Esteettömyys +Comment[fr]=Accessibilité +Comment[fy]=Tagonklikens +Comment[ga]=Inrochtaineacht +Comment[gl]=Accesibilidade +Comment[gu]=ઉપયોગિતા +Comment[he]=נגישות +Comment[hi]=पहुँच +Comment[hne]=पहुंच +Comment[hr]=Pristupačnost +Comment[hsb]=Přistupnosć +Comment[hu]=Kezelési segítség +Comment[ia]=Accessibilitate +Comment[id]=Aksesibilitas +Comment[is]=Auðveldað aðgengi +Comment[it]=Accessibilità +Comment[ja]=アクセシビリティ +Comment[kk]=Арнайы мүмкіндіктер +Comment[km]=មធ្យោបាយ​ងាយស្រួល +Comment[kn]=ನಿಲುಕಣೆ (ಆಕ್ಸೆಸಿಬಿಲಿಟಿ) +Comment[ko]=내게 필요한 설정 +Comment[ku]=Gihîştin +Comment[lt]=Pritaikymas neįgaliesiems +Comment[lv]=Pieejamība +Comment[mai]=अभिगम्यता +Comment[mk]=Пристапливост +Comment[ml]=സാമീപ്യത +Comment[mr]=सुलभता +Comment[ms]=Kebolehcapaian +Comment[nb]=Tilgjengelighet +Comment[nds]=Toganghülp +Comment[ne]=पहुँचता +Comment[nl]=Toegankelijkheid +Comment[nn]=Tilgjenge +Comment[oc]=Accessibilitat +Comment[or]=ଅଭିଗମ୍ୟତା +Comment[pa]=ਸਹੂਲਤਾਂ +Comment[pl]=Ułatwienia dostępu +Comment[pt]=Acessibilidade +Comment[pt_BR]=Acessibilidade +Comment[ro]=Accesibilitate +Comment[ru]=Специальные возможности +Comment[se]=Álkkibut geavaheapmi +Comment[si]=ප්‍රවේශණය +Comment[sk]=Prístupnosť +Comment[sl]=Dostopnost +Comment[sr]=Приступачност +Comment[sr@ijekavian]=Приступачност +Comment[sr@ijekavianlatin]=Pristupačnost +Comment[sr@latin]=Pristupačnost +Comment[sv]=Handikappstöd +Comment[ta]=அணுகுத்தன்மை +Comment[te]=అందుబాటు +Comment[tg]=Имкониятҳо +Comment[th]=ช่วยการใช้งานให้ง่ายขึ้น +Comment[tr]=Erişilebilirlik +Comment[ug]=ياردەم ئىقتىدارى +Comment[uk]=Доступність +Comment[uz]=Qulayliklar +Comment[uz@cyrillic]=Қулайликлар +Comment[vi]=Hỗ trợ truy cập +Comment[wa]=Accessibilité +Comment[x-test]=xxAccessibilityxx +Comment[zh_CN]=辅助 +Comment[zh_TW]=無障礙輔助 + +[Event/modifierkey-latched] +Name=A modifier key has become active +Name[af]='n Verandering sleutel was geaktiveer +Name[ar]=تم تفعيل مفتاح مغيِّر +Name[ast]=Activosé una tecla modificadora +Name[be@latin]=Klavišny madyfikatar prycisnuty +Name[bg]=Включен е клавиш-модификатор +Name[bn]=একটি মডিফায়ার কী সক্রিয় হয়েছে +Name[bs]=Modifikatorski taster je aktiviran +Name[ca]=Alguna tecla modificadora ha esdevingut activa +Name[ca@valencia]=Alguna tecla modificadora ha esdevingut activa +Name[cs]=Modifikátor klávesnice je aktivní +Name[csb]=Klawisza zjinaczi òsta aktiwòwónô +Name[da]=En ændringstast er blevet aktiv +Name[de]=Eine Modifikator-Taste ist aktiviert worden +Name[el]=Ένα πλήκτρο τροποποιητή έγινε ενεργό +Name[en_GB]=A modifier key has become active +Name[eo]=Modifigita klavo aktiviĝis +Name[es]=Se ha activado una tecla modificadora +Name[et]=Muuteklahv muutus aktiivseks +Name[eu]=Tekla aldatzaile bat aktibatu da +Name[fi]=Muunnosnäppäin on aktivoitunut +Name[fr]=Une touche de modification est devenue active +Name[fy]=In modifkaasjetoets wurdt aktyf +Name[ga]=Tá eochair mhionathraithe gníomhach anois +Name[gl]=Activouse unha tecla modificadora +Name[gu]=બદલવાની કળ સક્રિય બની છે +Name[he]=מקש משנה מצב הפך לפעיל +Name[hi]=कोई मॉडिफ़ायर कुंजी सक्रिय हो गया है +Name[hne]=कोई माडिफायर कुंजी सक्रिय हो गे हे +Name[hr]=Tipka modifikatora je aktivirana +Name[hsb]=Modifikowaca tasta je so aktiwizowała. +Name[hu]=Egy módosító billentyű aktívvá vált +Name[ia]=Un clave de modificator ha devenite active +Name[id]=Sebuah tombol pengubah telah menjadi aktif +Name[is]=Breytilykill er nú virkur +Name[it]=Un tasto modificatore è stato attivato +Name[ja]=修飾キーがアクティブになりました +Name[kk]=Түрлендіргіш перне белсендірілді +Name[km]=គ្រាប់​ចុច​កែប្រែ​ក្លាយ​ជា​សកម្ម +Name[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿ ಸಕ್ರಿಯಗೊಂಡಿದೆ +Name[ko]=수정자 키가 활성화됨 +Name[ku]=Bişkojkeke Guherker hat çalakirin +Name[lt]=Modifikavimo klavišas tapo aktyvus +Name[lv]=Modifikatora taustiņš tika aktivizēts +Name[mk]=Некое копче за модификација стана активно +Name[ml]=ഒരു മോഡിഫയര്‍ കീ സജീവമായിരിക്കുന്നു +Name[mr]=परिवर्तक कि सक्रिय झाली आहे +Name[nb]=En valgtast er slått på +Name[nds]=En Sünnertast is nu aktiev +Name[ne]=परिमार्जक कुञ्जी सक्रिय भएको छ +Name[nl]=Een modificatietoets is geactiveerd +Name[nn]=Ein valtast er no aktiv +Name[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି ସକ୍ରିୟ ହୋଇଯାଇଛି +Name[pa]=ਮੋਡੀਫਾਇਰ ਸਵਿੱਚ ਐਕਟਿਵ ਕੀਤੀ ਗਈ +Name[pl]=Klawisz modyfikujący został włączony +Name[pt]=Ficou activa uma tecla modificadora +Name[pt_BR]=Uma tecla modificadora se tornou ativa +Name[ro]=Un modificator de taste a devenit activ +Name[ru]=Нажата клавиша-модификатор +Name[se]=Válljenboallu lea šaddan aktiiva +Name[si]=වෙනස්කිරීම් යතුර සක්‍රීයයි +Name[sk]=Modifikačný kláves je aktívny +Name[sl]=Spremenilna tipka se je omogočila +Name[sr]=Модификаторски тастер је активиран +Name[sr@ijekavian]=Модификаторски тастер је активиран +Name[sr@ijekavianlatin]=Modifikatorski taster je aktiviran +Name[sr@latin]=Modifikatorski taster je aktiviran +Name[sv]=En väljartangent har aktiverats +Name[ta]=A modifier key has become active +Name[te]=సవరింపు కీ క్రియాశీలం కాబోతోంది +Name[tg]=Нажата клавиша модификатора +Name[th]=ปุ่มพิมพ์ประกอบถูกเปิดการทำงาน +Name[tr]=Bir değiştirici tuş etkinleştirildi +Name[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا ئاكتىپلاندى +Name[uk]=Клавіша модифікатора стала активною +Name[wa]=Ene modifieuse tape a divnou active +Name[x-test]=xxA modifier key has become activexx +Name[zh_CN]=修饰键已经激活 +Name[zh_TW]=組合鍵已啟動 +Comment=A modifier key (e.g. Shift or Ctrl) has changed its state and is now active +Comment[af]='n Veranderingsleutel (bv. Shift of Control) se toestand het verander en is nou aktief +Comment[ar]=مفتاح مغيِّر (شفت او كنترول) غيّر حالته وأصبح مفعلاً +Comment[ast]=Una tecla modificadora (Mayús o Ctrl) camudó l'estáu y ta activa +Comment[be@latin]=Klavišny madyfikatar (naprykład, „Shift” ci „Ctrl”) źmianiŭ svajo stanovišča j ciapier prycisnuty. +Comment[bg]=Клавиш-модификатор (напр. Shift или Ctrl) промени състоянието си и е активен +Comment[bn]=একটি মডিফায়ার কী (e.g. Shift বা Ctrl) অবস্থা পরিবর্তন করে এখন সক্রিয় হয়েছে +Comment[bs]=Modifikatorski taster (npr. Shift ili Ctrl) promijenio je stanje i sada je aktivan +Comment[ca]=Ha canviat l'estat d'una tecla modificadora (p.ex. Majúscules o Control) i ara està activa +Comment[ca@valencia]=Ha canviat l'estat d'una tecla modificadora (p.ex. Majúscules o Control) i ara està activa +Comment[cs]=Modifikátor klávesnice (Shift nebo Ctrl) změnil stav a je právě aktivní +Comment[csb]=Klawisza zjinaczi (Shift abò Control) zmienia swój sztatus ë je aktiwòwónô +Comment[da]=En ændringstast (f.eks. Skift eller Ctrl) har ændret sin tilstand og er nu aktiv +Comment[de]=Eine Modifikator-Taste (Umschalt oder Strg) hat ihren Status verändert und ist nun aktiv +Comment[el]=Ένα πλήκτρο τροποποιητή (π.χ., Shift ή Ctrl) άλλαξε την κατάστασή του και είναι τώρα ενεργό +Comment[en_GB]=A modifier key (e.g. Shift or Ctrl) has changed its state and is now active +Comment[eo]=Modifa klavo (ekz. majuskliga klavo aŭ stirklavo) ŝanĝis sian staton kaj nun estas aktiva +Comment[es]=Una tecla modificadora (Mayús o Ctrl) ha cambiado su estado y está activa +Comment[et]=Muuteklahv (nt. Shift või Ctrl) muutis oma olekut ning on nüüd aktiivne +Comment[eu]=Tekla aldatzaile bat (adibidez, Maius edo Ktrl) egoeraz aldatu da, eta orain aktibo dago +Comment[fi]=Muunnosnäppäin (esim. Shift tai Ctrl) on vaihtanut tilaa ja on nyt aktiivinen +Comment[fr]=Une touche de modification (ex : « Maj. » ou « Ctrl. ») a changé d'état et est maintenant active +Comment[fy]=De tastân fan in modifikaasjetoets (lykas Shift of Control) is feroare en is no aktyf +Comment[ga]=Athraíodh staid eochrach mionathraithe (m.sh. Shift nó Ctrl) agus tá sí gníomhach anois +Comment[gl]=Unha tecla modificadora (por ex. Maiús ou Ctrl) cambiou de estado e agora está activa +Comment[gu]=બદલવાની કળે (દા.ત., શીફ્ટ અથવા કંટ્રોલ) હવે તેની સ્થિતિ બદલી છે અને હવે સક્રિય છે +Comment[he]=מקש משנה מצב (למשל Shift או Ctrl) שינה את מצבו וכרגע פעיל +Comment[hne]=कोई माडिफायर कुंजी (जइसन कि शिफ्ट या कंट्रोल) हर अपन स्थिति ल बदल दे हे अउ अब वो हर सक्रिय हो गे हे +Comment[hr]=Tipka modifikatora (npr. SHIFT ili CTRL) promijenila je svoje stanje i aktivna je +Comment[hsb]=Modifikowaca tasta (na př. Wulkopis abo Strg) je status změniła a je nětko aktiwna. +Comment[hu]=Egy módosító billentyű (pl. a Shift vagy a Ctrl) aktívvá vált +Comment[ia]=Un clave modificator (p.ex. Shift o Ctrl) ha cambiate su stato e ora es active +Comment[id]=Sebuah tombol pengubah (misalnya Shift atau Ctrl) telah diubah keadaannya dan sekarang aktif +Comment[is]=Breytilykill (t.d. Shift eða Ctrl) hefur breytt um stöðu og er nú virkur +Comment[it]=Un tasto modificatore (come Shift o Ctrl) ha cambiato stato ed è ora attivo +Comment[ja]=修飾キー (Shift や Ctrl) の状態が変更され、アクティブになりました +Comment[kk]=Түрлендіргіш перне (мыс. Shift не Ctrl) күйін ауыстырып енді белсенді +Comment[km]=គ្រាប់ចុច​កែប្រែ (ឧ. ប្ដូរ (Shift) ឬ​ជំនួស (Control)) បាន​ផ្លាស់ប្ដូរ​សភាព​របស់​វា ឥឡូវ​នេះ​សកម្ម +Comment[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿ (ಉ.ದಾ Shift ಅಥವಾ Ctrl) ನ ಸ್ಥಿತಿಯು ಬದಲಾಗಿದ್ದು ಈಗ ಸಕ್ರಿಯಗೊಂಡಿದೆ +Comment[ko]=Shift나 Control 키 같은 수정자 키의 상태가 바뀌었고 현재 사용 가능함 +Comment[ku]=Bişkojkeke Guherker (mînak Shift an jî Ctrl) ciyê xwe guherand û niha çalak e +Comment[lt]=Klavišo – modifikatoriaus (pvz., Lyg2(Shift) arba Vald(Ctrl)) būsena pakito ir jis dabar – aktyvus +Comment[lv]=Modifikatora taustiņš (piem. Shift vai Control) ir mainījis stāvokli un tagad ir aktīvs +Comment[mk]=Некое копче за модификација (пр. Shift или Ctrl) си ја смени состојбата и сега е активно +Comment[ml]=ഒരു മോഡിഫയര്‍ കീ (ഉദാ. ഷിഫ്ട് അല്ലെങ്കില്‍ കണ്ട്രോള്‍) അവസ്ഥ മാറി സജീവമായിരിക്കുന്നു +Comment[mr]=परिवर्तक कि (उ.दा. Shift किंवा Ctrl) याने स्तर बदलविले आहे व आता सक्रिय आहे +Comment[nb]=En valgtast (f.eks. Shift eller Ctrl) har endret status og er nå slått på +Comment[nds]=En Sünnertast (Ümschalt oder Stüern (Strg) a.B.) hett sien Tostand ännert un is nu aktiev +Comment[nl]=Een modificatietoets (bijv. Shift of Ctrl) is nu actief +Comment[nn]=Ein valtast (som «Shift» eller «Ctrl») har endra status, og er no aktiv +Comment[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି (ଯେପରିକି Shift କିମ୍ବା Ctrl) ସେମାନଙ୍କର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିଅଛି ଏବଂ ବର୍ତ୍ତମାନ ସକ୍ରିୟ ଅଛି +Comment[pl]=Klawisz modyfikujący (np. Shift lub Ctrl) zmienił stan i jest teraz włączony +Comment[pt]=Uma tecla modificadora (p.ex., o Shift ou o Control) mudou o seu estado e ficou agora activa +Comment[pt_BR]=Uma tecla modificadora (p.ex. Shift ou Ctrl) teve seu estado alterado e agora está ativa +Comment[ro]=Un modificator de taste (cum ar fi Shift sau Ctrl) și-a schimbat starea și este acum activ +Comment[ru]=Клавиша-модификатор (например, Shift или Ctrl) изменила состояние и сейчас активна +Comment[si]=වෙනස් කරීමේ යතුරේ (උදා. Shift හෝ Ctrl) තත්වය වෙනස් වී ඇති අතර එය දැන් සක්‍රීයයි +Comment[sk]=Modifikačný kláves (napr. Shift alebo Ctrl) zmenil svoj stav a je teraz aktívny. +Comment[sl]=Spremenilna tipka (npr. Shift ali Ctrl) je spremenila stanje in je sedaj dejavna +Comment[sr]=Модификаторски тастер (нпр. Shift или Ctrl) променио је стање и сада је активан +Comment[sr@ijekavian]=Модификаторски тастер (нпр. Shift или Ctrl) промијенио је стање и сада је активан +Comment[sr@ijekavianlatin]=Modifikatorski taster (npr. Shift ili Ctrl) promijenio je stanje i sada je aktivan +Comment[sr@latin]=Modifikatorski taster (npr. Shift ili Ctrl) promenio je stanje i sada je aktivan +Comment[sv]=En väljartangent (t.ex. Skift eller Ctrl) har ändrat tillstånd och är nu aktiv +Comment[ta]=A modifier key (e.g. Shift or Ctrl) has changed its state and is now active +Comment[te]=సవరింపు కీ (ఉ.దా. Shift లేదా Ctrl) దానియొక్క స్థితి మార్చుకుంది మరియు ఇప్పుడు క్రియాశీలంగా ఉంది +Comment[tg]=Клавиша модификатора (например, Shift или Ctrl) изменила состояние и сейчас активна +Comment[th]=ปุ่มพิมพ์ประกอบ (เช่น Shift หรือ Ctrl) มีการเปลี่ยนสถานะของตัวมัน และตอนนี้กำลังทำงานอยู่ +Comment[tr]=Bir değiştirici tuşun (Shift veya Ctrl gibi) durumu değiştirildi ve etkinleştirildi +Comment[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا (مەسىلەن، Shift ياكى Ctrl) ئۇنىڭ ھالىتىنى ئۆزگەرتىپ ھازىر ئاكتىپلاشتۇرۇلدى +Comment[uk]=Клавіша модифікатора (наприклад Shift або Ctrl) змінила свій стан і тепер активна +Comment[wa]=Ene modifieuse tape (metans MAJ oudonbén CTRL) a candjî si estat eyet est asteure active +Comment[x-test]=xxA modifier key (e.g. Shift or Ctrl) has changed its state and is now activexx +Comment[zh_CN]=修饰键(如 Shift 或 Ctrl)更改了其状态,现已激活 +Comment[zh_TW]=一個組合鍵(如 Shift 或 Ctrl 等)已改變了它的狀態成為啟動 +Sound=KDE-Sys-Special.ogg + +nopresentation=236 + +[Event/modifierkey-unlatched] +Name=A modifier key has become inactive +Name[af]='n Verandering sleutel was gedeaktiveer +Name[ar]=تم إلغاء مفتاح مغيِّر +Name[ast]=Desactivóse una tecla modificadora +Name[be@latin]=Klavišny madyfikatar adcisnuty +Name[bg]=Изключен е клавиш-модификатор +Name[bn]=একটি মডিফায়ার কী নিষ্ক্রীয় হয়েছে +Name[bs]=Modifikatorski taster je deaktiviran +Name[ca]=Alguna tecla modificadora ha esdevingut inactiva +Name[ca@valencia]=Alguna tecla modificadora ha esdevingut inactiva +Name[cs]=Modifikátor klávesnice je neaktivní +Name[csb]=Klawisza zjiaczi òsta deaktiwòwónô +Name[da]=En ændringstast er blevet deaktiveret +Name[de]=Eine Modifikator-Taste ist deaktiviert worden +Name[el]=Ένα πλήκτρο τροποποιητή έγινε ανενεργό +Name[en_GB]=A modifier key has become inactive +Name[eo]=Modifigita klavo malaktiviĝis +Name[es]=Se ha desactivado una tecla modificadora +Name[et]=Muuteklahv muutus mitteaktiivseks +Name[eu]=Tekla aldatzaile bat inaktibatu da +Name[fi]=Muunnosnäppäin ei ole enää aktiivinen +Name[fr]=Une touche de modification est devenue inactive +Name[fy]=In modifikaasjetoets wurdt ynaktief +Name[ga]=Tá eochair mhionathraithe neamhghníomhach anois +Name[gl]=Desactivouse unha tecla modificadora +Name[gu]=બદલવાની કળ નિષ્ક્રિય બની છે +Name[he]=מקש משנה מצב הפך ללא פעיל +Name[hi]=कोई मॉडिफ़ायर कुंजी अक्रिय हो गया है +Name[hne]=कोई माडिफायर कुंजी हर अक्रिय हो गे हे +Name[hr]=Tipka modifikatora je deaktivirana +Name[hsb]=Modifikowaca tasta je so stała njeaktiwna +Name[hu]=Egy módosító billentyű inaktívvá vált +Name[ia]=Un clave modificator ha devenite inactive +Name[id]=Sebuah tombol pengubah telah menjadi tidak aktif +Name[is]=Breytilykill er nú óvirkur +Name[it]=Un tasto modificatore è stato disattivato +Name[ja]=修飾キーが非アクティブになりました +Name[kk]=Түрлендіргіш перне белсенсіздірілді +Name[km]=គ្រាប់​ចុច​កែប្រែ​បាន​ក្លាយ​ជា​អសកម្ម +Name[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ +Name[ko]=수정자 키가 비활성화됨 +Name[ku]=Bişkojkeke Guherker hat neçalakirin +Name[lt]=Klavišas – modifikatorius tapo neaktyvus +Name[lv]=Modifikatora taustiņš tika deaktivizēts +Name[mk]=Некое копче за модификација стана неактивно +Name[ml]=ഒരു മോഡിഫയര്‍ കീ നിര്‍ജീവമായിരിക്കുന്നു +Name[mr]=परिवर्तक कि निष्क्रिय झाली आहे +Name[nb]=En valgtast er slått av +Name[nds]=En Sünnertast is nich mehr aktiev +Name[ne]=परिमार्जक कुञ्जी निष्क्रिय भएको छ +Name[nl]=Een modificatietoets is inactief geworden +Name[nn]=Ein valtast er no inaktiv +Name[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି ନିଷ୍କ୍ରିୟ ହୋଇଯାଇଛି +Name[pa]=ਮੋਡੀਫਾਇਰ ਸਵਿੱਚ ਇਨ-ਐਕਟਿਵ ਕੀਤੀ ਗਈ +Name[pl]=Klawisz modyfikujący został wyłączony +Name[pt]=Ficou inactiva uma tecla modificadora +Name[pt_BR]=Uma tecla modificadora se tornou inativa +Name[ro]=Un modificator de taste a devenit inactiv +Name[ru]=Клавиша модификатора отпущена +Name[se]=Válljenboallu ii leat šat aktiiva +Name[si]=වෙනස්කිරීම් යතුර අක්‍රීයයි +Name[sk]=Modifikačný kláves je neaktívny +Name[sl]=Spremenilna tipka se je onemogočila +Name[sr]=Модификаторски тастер је деактивиран +Name[sr@ijekavian]=Модификаторски тастер је деактивиран +Name[sr@ijekavianlatin]=Modifikatorski taster je deaktiviran +Name[sr@latin]=Modifikatorski taster je deaktiviran +Name[sv]=En väljartangent har inaktiverats +Name[ta]=A modifier key has become inactive +Name[te]=సవరింపు కీ క్రియాహీనం కాబోతోంది +Name[tg]=Клавиша модификатора не активна +Name[th]=ปุ่มพิมพ์ประกอบถูกปิดการทำงาน +Name[tr]=Bir değiştirici tuş pasifleştirildi +Name[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا پاسسىپلاشتۇرۇلدى +Name[uk]=Клавіша модифікатора стала неактивною +Name[wa]=Ene modifieuse tape a divnou nén active +Name[x-test]=xxA modifier key has become inactivexx +Name[zh_CN]=修饰键现已停用 +Name[zh_TW]=組合鍵已放開 +Comment=A modifier key (e.g. Shift or Ctrl) has changed its state and is now inactive +Comment[af]='n Veranderingsleutel (bv. Shift of Contorl) se toestand het verander en is nou onaktief +Comment[ar]=مفتاح مغيِّر (شفت او كنترول) غيّر حالته وأصبح ملغى +Comment[ast]=Una tecla modificadora (Shift o Ctrl) camudó l'estáu y nun ta activa +Comment[be@latin]=Klavišny madyfikatar (naprykład, „Shift” ci „Ctrl”) źmianiŭ svajo stanovišča j ciapier adcisnuty. +Comment[bg]=Клавиш-модификатор (напр. Shift или Ctrl) промени състоянието си и е изключен +Comment[bn]=একটি মডিফায়ার কী (e.g. Shift বা Ctrl) অবস্থা পরিবর্তন করে এখন নিষ্ক্রীয় হয়েছে +Comment[bs]=Modifikatorski taster (npr. Shift ili Ctrl) je promijenio stanje i nije više aktivan +Comment[ca]=Ha canviat l'estat d'una tecla modificadora (p.ex. Majúscules o Control) i ara està inactiva +Comment[ca@valencia]=Ha canviat l'estat d'una tecla modificadora (p.ex. Majúscules o Control) i ara està inactiva +Comment[cs]=Modifikátor klávesnice (Shift nebo Ctrl) změnil stav a je právě neaktivní +Comment[csb]=Klawisza zjinaczi (Shift abò Control) zmienia swój sztatus ë nie je ju aktiwnô +Comment[da]=En ændringstast (f.eks. Skift eller Ctrl) har ændret sin tilstand og er nu inaktiv +Comment[de]=Eine Modifikator-Taste (Umschalt oder Strg) hat ihren Status verändert und ist nun inaktiv +Comment[el]=Ένα πλήκτρο τροποποιητή (π.χ., Shift ή Ctrl) άλλαξε την κατάστασή του και είναι τώρα ανενεργό +Comment[en_GB]=A modifier key (e.g. Shift or Ctrl) has changed its state and is now inactive +Comment[eo]=Modifa klavo (ekz. majuskliga klavo aŭ stirklavo) ŝanĝis sian staton kaj nun estas malaktiva +Comment[es]=Una tecla modificadora (Mayus o Ctrl) ha cambiado su estado y no está activa +Comment[et]=Muuteklahv (nt. Shift või Ctrl) muutis oma olekut ning on nüüd mitteaktiivne +Comment[eu]=Tekla aldatzaile bat (adibidez, Maius edo Ktrl) egoeraz aldatu da, eta orain inaktibo dago +Comment[fi]=Muunnosnäppäin (esim. Shift tai Ctrl) on vaihtanut tilaa eikä ole enää aktiivinen +Comment[fr]=Une touche de modification (ex : « Maj. » ou « Ctrl. ») a changé d'état et est maintenant inactive +Comment[fy]=De tastân fan in modifikaasjetoets (lykas Shift of Control) is feroare en is no ynaktyf +Comment[ga]=Athraíodh staid eochrach mionathraithe (m.sh. Shift nó Ctrl) agus tá sí neamhghníomhach anois +Comment[gl]=Unha tecla modificadora (por ex. Maiús ou Ctrl) cambiou de estado e agora está inactiva +Comment[gu]=બદલવાની કળે (દા.ત., શીફ્ટ અથવા કંટ્રોલ) હવે તેની સ્થિતિ બદલી છે અને હવે નિષ્ક્રિય છે +Comment[he]=מקש משנה מצב (למשל Shift או Ctrl) שינה את מצבו וכרגע לא פעיל +Comment[hne]=कोई माडिफायर कुंजी (जइसन कि शिफ्ट या कंट्रोल) हर अपन स्थिति ल बदल दे हे अउ अब वो हर अक्रिय हो गे हे +Comment[hr]=Tipka modifikatora (npr. SHIFT ili CTRL) promijenila je svoje stanje i više nije aktivna +Comment[hsb]=Modifikowaca tasta (na př. Wulkopis abo Strg) je swój status změniła a je nětko njeaktiwna +Comment[hu]=Egy módosító billentyű (pl. a Shift vagy a Ctrl) inaktívvá vált +Comment[ia]=Un clave modificator (p.ex.Shift o Ctrl) ha cambiate su stato e ora es inactive +Comment[id]=Sebuah tombol pengubah (misalnya Shift atau Ctrl) tekah diubah keadaannya dan sekarang tidak aktif +Comment[is]=Breytilykill (t.d. Shift eða Ctrl) hefur breytt um stöðu og er nú óvirkur +Comment[it]=Un tasto modificatore (come Shift o Ctrl) ha cambiato stato ed è ora inattivo +Comment[ja]=修飾キー (Shift や Ctrl) の状態が変更され、非アクティブになりました +Comment[kk]=Түрлендіргіш перне (мыс. Shift не Ctrl) күйін ауыстырып енді белсенді емес +Comment[km]=គ្រាប់​ចុច​កែប្រែ (ឧ. ប្ដូរ (Shift) ឬ បញ្ជា (Control)) បាន​ផ្លាស់ប្ដូរ​សភាព​របស់​វា ហើយ​ឥឡូវ​នេះ​អសកម្ម +Comment[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿ (ಉ.ದಾ Shift ಅಥವಾ Ctrl) ನ ಸ್ಥಿತಿಯು ಬದಲಾಗಿದ್ದು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ +Comment[ko]=Shift나 Control 키 같은 수정자 키의 상태가 바뀌었고 현재 사용 불가능함 +Comment[ku]=Bişkojkeke Guherker (mînak Shift an jî Ctrl) ciyê xwe guherand û niha neçalak e +Comment[lt]=Klavišo – modifikatoriaus (pvz., Lyg2(Shift) arba Vald(Ctrl)) būsena pakito ir jis dabar – neaktyvus +Comment[lv]=Modifikatora taustiņš (piem. Shift vai Control) ir mainījis stāvokli un tagad ir neaktīvs +Comment[mk]=Некое копче за модификација (пр. Shift или Ctrl) си ја смени состојбата и сега е неактивно +Comment[ml]=ഒരു മോഡിഫയര്‍ കീ (ഉദാ. ഷിഫ്ട് അല്ലെങ്കില്‍ കണ്ട്രോള്‍) അവസ്ഥ മാറി നിര്‍ജീവമായിരിക്കുന്നു +Comment[mr]=परिवर्तक कि (उ.दा. Shift किंवा Ctrl) याने स्तर बदलविले आहे व आता निष्क्रिय आहे +Comment[nb]=En valgtast (f.eks. Shift eller Ctrl) har endret status og er nå slått av +Comment[nds]=En Sünnertast (Ümschalt oder Stüern (Strg) a.B.) hett sien Tostand ännert un is nu nich mehr aktiev +Comment[nl]=Een modificatietoets (bijv. Shift of Ctrl) is nu inactief +Comment[nn]=Ein valtast (som «Shift» eller «Ctrl») har endra status, og er no inaktiv +Comment[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି (ଯେପରିକି Shift କିମ୍ବା Ctrl) ସେମାନଙ୍କର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିଛନ୍ତି ଏବଂ ବର୍ତ୍ତମାନ ନିଷ୍କ୍ରିୟ ହୋଇଯାଇଛନ୍ତି +Comment[pl]=Klawisz modyfikujący (np. Shift lub Ctrl) zmienił stan i jest teraz wyłączony +Comment[pt]=Uma tecla modificadora (p.ex., o Shift ou o Control) mudou o seu estado e ficou agora inactiva +Comment[pt_BR]=Uma tecla modificadora (p.ex. Shift ou Ctrl) teve seu estado alterado e agora está inativa +Comment[ro]=Un modificator de taste (cum ar fi Shift sau Ctrl) și-a schimbat starea și este acum inactiv +Comment[ru]=Клавиша-модификатор (например, Shift или Ctrl) изменила своё состояние и сейчас неактивна +Comment[si]=වෙනස් කරීමේ යතුරේ (උදා. Shift හෝ Ctrl) තත්වය වෙනස් වී ඇති අතර එය දැන් අක්‍රීයයි +Comment[sk]=Modifikačný kláves (napr. Shift alebo Ctrl) zmenil svoj stav a je teraz neaktívny. +Comment[sl]=Spremenilna tipka (npr. Shift ali Ctrl) je spremenila stanje in je sedaj nedejavna +Comment[sr]=Модификаторски тастер (нпр. Shift или Ctrl) је променио стање и није више активан +Comment[sr@ijekavian]=Модификаторски тастер (нпр. Shift или Ctrl) је промијенио стање и није више активан +Comment[sr@ijekavianlatin]=Modifikatorski taster (npr. Shift ili Ctrl) je promijenio stanje i nije više aktivan +Comment[sr@latin]=Modifikatorski taster (npr. Shift ili Ctrl) je promenio stanje i nije više aktivan +Comment[sv]=En väljartangent (t.ex. Skift eller Ctrl) har ändrat tillstånd och är nu inaktiv +Comment[ta]=A modifier key (e.g. Shift or Ctrl) has changed its state and is now inactive +Comment[te]=సవరింపు కీ (ఉ.దా. Shift లేదా Ctrl) దాని స్థితి మార్చుకుంది మరియు ఇప్పుడు క్రియాహీనంగా ఉంది +Comment[tg]=Клавиша модификатора (например, Shift или Ctrl) изменила своё состояние и сейчас не активна +Comment[th]=ปุ่มพิมพ์ประกอบ (เช่น Shift หรือ Ctrl) มีการเปลี่ยนสถานะของตัวมัน และตอนนี้ได้ปิดการทำงาน +Comment[tr]=Bir değiştirici tuşun (Shift veya Ctrl gibi) durumu değiştirildi ve pasifleştirildi +Comment[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا (مەسىلەن، Shift ياكى Ctrl) ئۇنىڭ ھالىتىنى ئۆزگەرتىپ ھازىر پاسسىپلاشتۇرۇلدى +Comment[uk]=Клавіша модифікатора (наприклад Shift або Ctrl) змінила свій стан і тепер неактивна +Comment[wa]=Ene modifieuse tape (metans MAJ oudonbén CTRL) a candjî si estat eyet n' est asteure pus active +Comment[x-test]=xxA modifier key (e.g. Shift or Ctrl) has changed its state and is now inactivexx +Comment[zh_CN]=修饰键(如 Shift 或 Ctrl)更改了其状态,现已停用 +Comment[zh_TW]=一個組合鍵(如 Shift 或 Ctrl 等)已改變了它的狀態成為放開 +Sound=KDE-Sys-Special.ogg + +nopresentation=236 + +[Event/modifierkey-locked] +Name=A modifier key has been locked +Name[af]='n Verandering sleutel was gesluit +Name[ar]=تم قفل مفتاح مغيِّر +Name[ast]=Bloquióse una tecla modificadora +Name[be@latin]=Klavišny madyfikatar zamacavany +Name[bg]=Клавиш-модификатор е заключен +Name[bn]=একটি মডিফায়ার কী লক করা হয়েছে +Name[bs]=Modifikatorski taster je zaključan +Name[ca]=S'ha fixat alguna tecla modificadora +Name[ca@valencia]=S'ha fixat alguna tecla modificadora +Name[cs]=Modifikátor klávesnice je zamčený +Name[csb]=Klawisza zjinaczi òsta zablokòwónô +Name[da]=En ændringstast er blevet låst +Name[de]=Eine Modifikator-Taste ist gesperrt worden +Name[el]=Ένα πλήκτρο τροποποιητή κλειδώθηκε +Name[en_GB]=A modifier key has been locked +Name[eo]=Modifigita klavo ŝlosiĝis +Name[es]=Se ha bloqueado una tecla modificadora +Name[et]=Muuteklahv on lukustatud +Name[eu]=Tekla aldatzaile bat blokeatu da +Name[fi]=Muunnosnäppäin on lukittu +Name[fr]=Une touche de modification a été verrouillée +Name[fy]=In modifikaasjetoets is beskoattele +Name[ga]=Tá eochair mhionathraithe faoi ghlas anois +Name[gl]=Trancouse unha tecla modificadora +Name[gu]=બદલવાની કળને તાળું મારવામાં આવ્યું છે +Name[he]=מקש משנה מצב ננעל +Name[hi]=कोई मॉडिफ़ायर कुंजी तालाबंद हो गया है +Name[hne]=कोई माडिफायर कुंजी हर तालाबंद हो गे हे +Name[hr]=Tipka modifikatora je zaključana +Name[hsb]=Modifikowaca tasta bu stajnje zaswěćena +Name[hu]=Egy módosító billentyű zárolódott +Name[ia]=Un clave modificator ha essite blocate +Name[id]=Sebuah tombol pengubah telah dikunci +Name[is]=Breytilykli hefur verið læst +Name[it]=Un tasto modificatore è stato bloccato +Name[ja]=修飾キーがロックされました +Name[kk]=Түрлендіргіш перне басылды +Name[km]=គ្រាប់​ចុច​កែប្រែ​បាន​ត្រូវ​ចាក់​សោ +Name[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿಯನ್ನು ಬಂಧಿಸಲಾಗಿದೆ (ಲಾಕ್) +Name[ko]=수정자 키가 잠김 +Name[ku]=Bişkojkeke Guherker hat kilîtkirin +Name[lt]=Klavišas – modifikatorius buvo užrakintas +Name[lv]=Modifikatora taustiņš tika fiskēts +Name[mk]=Некое копче за модификација е заклучено +Name[ml]=ഒരു മോഡിഫയര്‍ കീ പൂട്ടിയിരിക്കുന്നു +Name[mr]=परिवर्तक कि कुलूपबंद केली गेली आहे +Name[nb]=En valgtast er blitt låst +Name[nds]=En Sünnertast wöör fastsett +Name[ne]=परिमार्जक कुञ्जी ताल्चा लगाइएको छ +Name[nl]=Een modificatietoets is vergrendeld +Name[nn]=Ein valtast er no låst +Name[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି ଅପରିବର୍ତ୍ତିତ ହୋଇଯାଇଛି +Name[pa]=ਮੋਡੀਫਾਇਰ ਸਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ +Name[pl]=Klawisz modyfikujący został zablokowany +Name[pt]=Uma tecla modificadora ficou bloqueada +Name[pt_BR]=Uma tecla modificadora foi bloqueada +Name[ro]=Un modificator de taste a fost blocat +Name[ru]=Клавиша-модификатор зафиксирована +Name[se]=Válljenboallu lea dál lohkkaduvvon +Name[si]=වෙනස්කිරීම් යතුර අගුළු දමා ඇත +Name[sk]=Modifikačný kláves je zamknutý +Name[sl]=Spremenilna tipka se je zaklenila +Name[sr]=Модификаторски тастер је закључан +Name[sr@ijekavian]=Модификаторски тастер је закључан +Name[sr@ijekavianlatin]=Modifikatorski taster je zaključan +Name[sr@latin]=Modifikatorski taster je zaključan +Name[sv]=En väljartangent har låsts +Name[ta]=A modifier key has been locked +Name[te]=సవరింపు కీ అప్రవేశంగా ఉంది +Name[tg]=Клавиша модификатора зафиксирована +Name[th]=ปุ่มพิมพ์ประกอบถูกล็อคอยู่ +Name[tr]=Bir değiştirici tuş kilitlendi +Name[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا قۇلۇپلاندى +Name[uk]=Клавішу модифікатора було заблоковано +Name[wa]=Ene modifieuse tape a stî eclawêye +Name[x-test]=xxA modifier key has been lockedxx +Name[zh_CN]=修饰键现已锁定 +Name[zh_TW]=組合鍵已鎖定 +Comment=A modifier key (e.g. Shift or Ctrl) has been locked and is now active for all of the following keypresses +Comment[af]='n Veranderingsleutel (bv. Shift of Control) is gesluit en is nou aktief vir al die volgende sleuteldrukke +Comment[ar]=تم قفل مفتاح مغيِّر (شفت أو كنترول) وأصبح مفعّلاً لكل الكتابات التالية +Comment[ast]=Bloquióse una tecla modificadora (Mayús o Ctrl) y afectará a toles tecles calcaes +Comment[be@latin]=Klavišny madyfikatar (naprykład, „Shift” ci „Ctrl”) zamacavaŭsia j ciapier prycisnuty dla ŭsich nastupnych klavišaŭ. +Comment[bg]=Клавиш-модификатор (напр. Shift или Ctrl) е заключен и е активен за всички последващи натискания на клавиши +Comment[bn]=একটি মডিফায়ার কী (e.g. Shift বা Ctrl) লক করা হয়েছে এবং পরবর্তী সবকটি কী-র (key) জন্য সক্রিয় থাকবে +Comment[bs]=Modifikatorski taster (npr. Shift ili Ctrl) upravo je zaključan i od sada aktivan za sve pritiske na tastere +Comment[ca]=S'ha fixat alguna tecla modificadora (p.ex. Majúscules o Control) i ara està activa per a totes les tecles que es premin a continuació +Comment[ca@valencia]=S'ha fixat alguna tecla modificadora (p.ex. Majúscules o Control) i ara està activa per a totes les tecles que es premen a continuació +Comment[cs]=Modifikátor klávesnice (např. Shift nebo Ctrl) je zamčený a je nyní aktivní pro všechny následující stisknuté klávesy +Comment[csb]=Klawisza zjinaczi (Shift abò Control) òsta zablokòwónô ë je terô dlô pòsobnegò wcësniącô klawiszów aktiwnô +Comment[da]=En ændringstast (f.eks. Skift eller Ctrl) er blevet låst og er nu aktiv for alle følgende tastetryk +Comment[de]=Eine Modifikator-Taste (Umschalt oder Strg) ist festgesetzt worden und ist nun für alle folgenden Tastendrücke aktiv +Comment[el]=Ένα πλήκτρο τροποποιητή (π.χ., Shift ή Ctrl) κλειδώθηκε και τώρα είναι ενεργό για όλα τα ακόλουθα πατήματα πλήκτρων +Comment[en_GB]=A modifier key (e.g. Shift or Ctrl) has been locked and is now active for all of the following keypresses +Comment[eo]=Modifa klavo (ekz. majuskliga klavo aŭ stirklavo) ŝlosiĝis kaj nun estas aktiva por la sekvaj klavpremoj +Comment[es]=Se ha bloqueado una tecla modificadora (Mayús o Ctrl) y afectará a todas las teclas que se pulsen +Comment[et]=Muuteklahv (nt. Shift või Ctrl) on lukustatud ja on nüüd aktiivne kõikide järgnevate klahvivajutuste jaoks +Comment[eu]=Tekla aldatzaile bat (adibidez, Maius edo Ktrl) blokeatu da, eta orain aktibo dago sakatzen diren tekla guztietarako +Comment[fi]=Muunnosnäppäin (esim. Shift tai Ctrl) on lukittu ja on nyt aktiivinen kaikille seuraaville näppäinpainalluksille +Comment[fr]=Une touche de modification (ex : « Maj. » ou « Ctrl. ») a été verrouillée et est maintenant active pour tous les appuis suivants de touches +Comment[fy]=In modifikaasjetoets (lykas Shift of Control) is beskoattele en is no aktyf foar de neikommende toetsoanslaggen +Comment[ga]=Cuireadh eochair mhionathraithe (m.sh., Shift nó Ctrl) faoi ghlas tá sí gníomhach anois le haghaidh na n-eochairbhrúnna seo a leanas +Comment[gl]=Unha tecla modificadora (por ex. Maiús ou Ctrl) foi trancada e agora está activa +Comment[gu]=બદલવાની કળને (દા.ત., શીફ્ટ અથવા કંટ્રોલ) હવે તાળું મારવામાં આવ્યું છે અને હવે તે નીચેની કળ દબાવવા માટે સક્રિય છે +Comment[he]=מקש משנה מצב (למשל Shift או Ctrl) ננעל וכרגע פעיל עבור לחיצות המקשים הבאות +Comment[hne]=कोई माडिफायर कुंजी (जइसन कि शिफ्ट या कंट्रोल) हर तालाबंद हो गे हे अउ अब वो हर नीचे के सब्बो कुंजीदबाव मं सक्रिय रहिही +Comment[hr]=Tipka modifikatora (npr. SHIFT ili CTRL) zaključana je i aktivna je tijekom svih sljedećih pritisaka tipki +Comment[hsb]=Modifikowaca tasta (na př. Wulkopis abo Strg) bu stajnje zaswěćena a je nětko za wšě přichodne tasty aktiwna +Comment[hu]=Egy módosító billentyű (pl. a Shift vagy a Ctrl) zárolódott, és ezután minden billentyűlenyomásnál aktív lesz +Comment[ia]=Un clave modificator (p.ex.Shift o CTRL) ha essite blocate e ora es active pro tote le sequente keypresses +Comment[id]=Sebuah tombol pengubah (misalnya Shift atau Ctrl) telah dikunci dan sekarang aktif untuk semua penekanan tombol berikut +Comment[is]=Breytilykli (t.d. Shift eða Ctrl) hefur verið læst og er nú virkur fyrir eftirfarandi lyklaborðsaðgerðir +Comment[it]=Un tasto modificatore (come Shift o Ctrl) è stato bloccato ed è ora attivo per tutte le battute successive +Comment[ja]=修飾キー (Shift や Ctrl) がロックされ、続くキー押下のすべてに対してアクティブになりました +Comment[kk]=Түрлендіргіш перне (мыс. Shift не Ctrl) басылып енді барлық басылмақ пернелер үшін белсенді +Comment[km]=គ្រាប់​ចុច​កែប្រែ (ឧ. ប្ដូរ (Shift) ឬ​បញ្ជា (Control)) ត្រូវ​បាន​ចាក់​សោ ហើយ​ឥឡូវ​នេះ​សកម្ម​សម្រាប់​ការ​ចុច​គ្រាប់​ចុច​ខាង​ក្រោម +Comment[kn]=ಒಂದು ಪರಿವರ್ತಕ (ಮಾಡಿಫಯರ್) ಕೀಲಿಯನ್ನು (ಉ.ದಾ Shift ಅಥವಾ Ctrl) ಬಂಧಿಸಲಾಗಿದ್ದು (ಲಾಕ್), ಮುಂದಿನ ಎಲ್ಲಾ ಕೀಲಿಮಣೆ ಒತ್ತುಗಳಿಗೆ ಈಗ ಸಕ್ರಿಯವಾಗಿದೆ +Comment[ko]=Shift나 Control 키 같은 수정자 키의 상태가 바뀌었고 현재 모든 키 입력에 대해서 활성화됨 +Comment[ku]=Bişkojka guhêrker (wekî Shift an jî Ctrl) hate qufl kirin û niha ji bo çalak kirina hemû bişkojkên jêr çalak e +Comment[lt]=Klavišas – modifikatorius (pvz., Lyg2(Shift) arba Vald(Ctrl)) buvo užrakintas ir dabar visiems būsimiems klavišų paspaudimams yra aktyvus +Comment[lv]=Modifikatora taustiņš (piem. Shift vai Control) tika fiksēts un ir aktīvs visiem nākamajiem taustiņu nospiedieniem +Comment[mk]=Некое копче за модификација (пр. Shift или Ctrl) беше заклучено и сега е активно за сите наредни притиснати копчиња +Comment[ml]=ഒരു മോഡിഫയര്‍ കീ (ഉദാ. ഷിഫ്ട് അല്ലെങ്കില്‍ കണ്ട്രോള്‍) പൂട്ടിയിരിക്കുന്നു. നിലവില്‍ താഴെ പറയുന്ന കീപ്രെസ്സുകള്‍ക്കു് ഇതു് സജീവമാണു് +Comment[mr]=परिवर्तक कि (उ.दा. Shift किंवा Ctrl) कुलूपबंद झाली आहे व खालील किप्रेस् करिता सक्रिय झाली आहे +Comment[nb]=En valgtast (f.eks. Shift eller Ctrl) har blitt låst og er nå skrudd på for alle etterfølgende tastetrykk +Comment[nds]=En Sünnertast (Ümschalt oder Stüern (Strg) a.B.) wöör fastsett un is nu för all nakamen Tasten aktiev +Comment[nl]=Een modificatietoets (bijv. Shift of Ctrl) is vergrendeld en nu actief voor alle volgende toetsaanslagen +Comment[nn]=Ein valtast (som «Shift» eller «Ctrl») er vorten låst, og er no aktiv for alle etterfølgjande tastetrykk +Comment[or]=ଗୋଟିଏ ପରିବର୍ତ୍ତକ କି (ଯେପରିକି Shift କିମ୍ବା Ctrl) ଅପରିବର୍ତ୍ତିତ ହୋଇଯାଇଛି ଏବଂ ବର୍ତ୍ତମାନ ନିମ୍ନଲିଖିତ ସମସ୍ତ କି ଚାପ ପାଇଁ ସକ୍ରିୟ ହୋଇଛି +Comment[pl]=Klawisz modyfikujący (np. Shift lub Ctrl) został zablokowany i będzie włączony przy następnych naciśnięciach klawiszy +Comment[pt]=Uma tecla modificadora (p.ex., o Shift ou o Control) foi bloqueada e ficou agora activa para todas as combinações de teclas seguintes +Comment[pt_BR]=Uma tecla modificadora (p.ex. Shift ou Ctrl) foi bloqueada e agora está ativa, para todos os pressionamentos de tecla seguintes +Comment[ro]=Un modificator de taste (cum ar fi Shift sau Ctrl) a fost blocat și este acum activ pentru următoarele apăsări de taste +Comment[ru]=Клавиша-модификатор (например, Shift или Ctrl) зафиксирована и активна при нажатии любых других клавиш +Comment[si]=වෙනස් කිරීමේ යතුර (උදා. Shift හෝ Ctrl) අගුළු දමා ඇති අතර පහත සියළුම යතුරු එබීම් සඳහා එය සක්‍රීයයි +Comment[sk]=Modifikačný kláves (napr. Shift alebo Ctrl) je zamknutý a je teraz aktívny pre všetky nasledujúce stlačenia kláves +Comment[sl]=Spremenilna tipka (npr. Shift ali Ctrl) se je zaklenila in je sedaj dejavna za vse prihodnje pritiske tipk +Comment[sr]=Модификаторски тастер (нпр. Shift или Ctrl) управо је закључан и од сада активан за све притиске на тастере +Comment[sr@ijekavian]=Модификаторски тастер (нпр. Shift или Ctrl) управо је закључан и од сада активан за све притиске на тастере +Comment[sr@ijekavianlatin]=Modifikatorski taster (npr. Shift ili Ctrl) upravo je zaključan i od sada aktivan za sve pritiske na tastere +Comment[sr@latin]=Modifikatorski taster (npr. Shift ili Ctrl) upravo je zaključan i od sada aktivan za sve pritiske na tastere +Comment[sv]=En väljartangent (t.ex. Skift eller Ctrl) har låsts och är nu aktiv för alla följande tangentnertryckningar +Comment[ta]=A modifier key (e.g. Shift or Ctrl) has been locked and is now active for all of the following keypresses +Comment[te]=సవరింపు కీ (ఉ.దా. Shift లేదా Ctrl) అప్రవేశం చేయబడింది మరియు ఇప్పుడు క్రింది అన్ని కీవత్తులకు క్రియాశీలంగా ఉంది +Comment[tg]=Клавиша модификатора (например, Shift или Ctrl) зафиксирована и активна при нажатии любых других клавиш +Comment[th]=ปุ่มพิมพ์ประกอบ (เช่น Shift หรือ Cntrl) ถูกล็อคอยู่ และขณะนี้อยู่ในสถานะทำงาน สำหรับใช้ร่วมกับการกดแป้นต่อจากนี้ +Comment[tr]=Bir değiştirici tuş (Shift ya da Ctrl gibi) kilitlendi ve bundan sonraki tüm tuş basma eylemleri için etkinleştirildi +Comment[ug]=بىر ئۆزگەرتكۈچى كۇنۇپكا (مەسىلەن، Shift ياكى Ctrl) قۇلۇپلاندى، بۇندىن كېيىنكى كۇنۇپكا بېسىشقا ئىشلىتىلىدۇ +Comment[uk]=Клавішу модифікатора (наприклад Shift або Ctrl) було зафіксовано і тепер вона активна для всіх наступних натискань клавіш +Comment[wa]=Ene modifieuse tape (metans MAJ oudonbén CTRL) a stî eclawêye eyet est asteure active po tos les tapes tchôkeyes ki shuront +Comment[x-test]=xxA modifier key (e.g. Shift or Ctrl) has been locked and is now active for all of the following keypressesxx +Comment[zh_CN]=修饰键(如 Shift 或 Ctrl)已被锁定,现在将适用于后续按键 +Comment[zh_TW]=一個組合鍵(如 Shift 或 Ctrl 等)已被鎖定,現在再按任何鍵都視為快捷鍵組合 +Sound=KDE-Sys-Special.ogg + +nopresentation=236 + +[Event/lockkey-locked] +Name=A lock key has been activated +Name[af]='n Sluitsleutel is geaktiveer +Name[ar]=تم تفعيل مفتاح قفل +Name[ast]=Activóse una tecla de bloquéu +Name[be@latin]=Klaviša macavańnia prycisnutaja +Name[bg]=Включен е клавиш за превключване на режим +Name[bn]=একটি লক কী (lock key) সক্রিয় হয়েছে +Name[bs]=Zaključavajući taster je aktiviran +Name[ca]=S'ha activat alguna tecla de fixació +Name[ca@valencia]=S'ha activat alguna tecla de fixació +Name[cs]=Klávesa pro přepnutí stavu byla aktivována +Name[csb]=Klawisza blokadë òsta aktiwòwónô +Name[da]=En låsetast er blevet aktiveret +Name[de]=Eine Sperrtaste ist aktiviert worden +Name[el]=Ένα πλήκτρο κλειδώματος έχει ενεργοποιηθεί +Name[en_GB]=A lock key has been activated +Name[eo]=Baskula klavo aktiviĝis +Name[es]=Se ha activado una tecla de bloqueo +Name[et]=Lukustusklahv on aktiveeritud +Name[eu]=Blokeo-tekla bat aktibatu da +Name[fi]=Lukitusnäppäin on aktivoitu +Name[fr]=Une touche de verrouillage a été activée +Name[fy]=In beskoatteltoets is aktivearre +Name[ga]=Tá eochair ghlasála gníomhach anois +Name[gl]=Activouse unha tecla de bloqueo +Name[gu]=તાળાં કળ સક્રિય કરવામાં આવી છે +Name[he]=מקש נועל הפך לפעיל +Name[hi]=कोई तालाबंद कुंजी सक्रिय किया जा चुका है +Name[hne]=कोई ताला कुंजी सक्रिय हो गे हे +Name[hr]=Tipka zaključavanja je aktivirana +Name[hsb]=Tasta za stajne zaswěćene je nětko aktiwna +Name[hu]=Egy zároló billentyű aktívvá vált +Name[ia]=Un clave de bloco ha essite activate +Name[id]=Sebuah tombol pengunci telah diaktifkan +Name[is]=Læsilykill hefur verið virkjaður +Name[it]=Un tasto di blocco è stato attivato +Name[ja]=ロックキーがアクティブになりました +Name[ka]=დაბლოკვის ღილაკი გააქტივებულია +Name[kk]=Қосқыш перне белсендірілді +Name[km]=គ្រាប់​ចុច​ចាក់​សោ​បាន​ត្រូវ​ធ្វើ​ឲ្យ​សកម្ម +Name[kn]=ಒಂದು ಭಂಧನ ಕೀಲಿಯನ್ನು (ಲಾಕ್) ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=잠금 키가 활성화됨 +Name[ku]=Bişkojkeke Mifteyê hat çalakirin +Name[lt]=Užrakto klavišas buvo aktyvuotas +Name[lv]=Slēga taustiņš tika aktivizēts +Name[mk]=Некое копче за заклучување беше активирано +Name[ml]=ഒരു ലോക്ക് കീ സജീവമാക്കിയിരിക്കുന്നു +Name[mr]=कुलूपबंद कि सक्रिय केली गेली +Name[nb]=En låsetast er blitt slått på +Name[nds]=En Tastrast is nu inleggt +Name[ne]=एउटा ताल्चा कुञ्जी सक्रिय बनाइएको छ +Name[nl]=Een vergrendeltoets is geactiveerd +Name[nn]=Ein låstetast er no aktiv +Name[or]=ଗୋଟିଏ ଅପରିବର୍ତ୍ତନୀୟ କିକୁ ସକ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਇੱਕ ਲਾਕ ਸਵਿੱਚ ਐਕਟਿਵੇਟ ਕੀਤੀ ਗਈ +Name[pl]=Klawisz blokujący został włączony +Name[pt]=Uma tecla de bloqueio foi activada +Name[pt_BR]=Uma tecla de bloqueio foi ativada +Name[ro]=O tastă de blocare a fost activată +Name[ru]=Активирована клавиша переключения режимов +Name[se]=Lohkkadanboallu lea aktiivalaš +Name[si]=අගුළු යතුර සක්‍රීයයි +Name[sk]=Zamykací kláves je aktívny +Name[sl]=Zaklepna tipka je bila omogočena +Name[sr]=Закључавајући тастер је активиран +Name[sr@ijekavian]=Закључавајући тастер је активиран +Name[sr@ijekavianlatin]=Zaključavajući taster je aktiviran +Name[sr@latin]=Zaključavajući taster je aktiviran +Name[sv]=En låstangent har aktiverats +Name[ta]=A lock key has been activated +Name[te]=అప్రవేశపు కీ క్రియాశీలం చేయబడింది +Name[tg]=Активирована клавиша переключения режимов +Name[th]=ปุ่มล็อคถูกเปิดการทำงาน +Name[tr]=Bir kilitleme tuşu etkinleştirildi +Name[ug]=بىر قۇلۇپلاش كۇنۇپكىسى ئاكتىپلاندى +Name[uk]=Було активовано клавішу фіксації +Name[wa]=Ene eclawåve tape a stî metowe en alaedje +Name[x-test]=xxA lock key has been activatedxx +Name[zh_CN]=锁定键现已激活 +Name[zh_TW]=已啟動鎖定鍵 +Comment=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now active +Comment[af]='n Sluitsleutel (bv. Caps Lock of Num Lock) se toestand het verander en is nou aktief +Comment[ar]=تغيرت حالة مفتاح قفل (Caps Lock أو Num Lock) وأصبح مفعلاً +Comment[ast]=Una tecla de bloquéu (como Bloq Mayús o Bloq Num) camudó l'estáu y ta activa +Comment[be@latin]=Klaviša macavańnia (naprykład, „Caps Lock” ci „Num Lock”) źmianiła svajo stanovišča j ciapier prycisnutaja. +Comment[bg]=Клавиш за превключване на режим (напр. Caps Lock или Num Lock) промени състоянието си и е активен +Comment[bn]=একটি লক কী (e.g. Caps Lock বা Num Lock) অবস্থা পরিবর্তন করে এখন সক্রিয় হয়েছে +Comment[bs]=Zaključavajući taster (npr. CapsLock ili NumLock) promijenio je stanje i sada je aktivan +Comment[ca]=Alguna tecla de fixació (p.ex. Bloq Maj o Bloq Núm) ha canviat el seu estat i ara està activa +Comment[ca@valencia]=Alguna tecla de fixació (p.ex. Bloq Maj o Bloq Núm) ha canviat el seu estat i ara està activa +Comment[cs]=Klávesa pro přepnutí stavu (např. CapsLock nebo NumLock) byla stisknuta a je nyní aktivní +Comment[csb]=Klawisza blokadë (Caps Lock abò Num Lock) zmienia swój sztatus ë je terô aktiwnô +Comment[da]=En låsetast (f.eks. Caps Lock eller Num Lock) har ændret sin tilstand og er nu aktiv +Comment[de]=Eine Sperrtaste (Feststelltaste oder Nummernblocksperre) hat ihren Status geändert und ist nun aktiv +Comment[el]=Ένα πλήκτρο κλειδώματος (π.χ., Caps Lock ή Num Lock) άλλαξε την κατάστασή του και είναι τώρα ενεργό +Comment[en_GB]=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now active +Comment[eo]=Baskula klavo (ekz. majuskla baskulo aŭ nombra baskulo) ŝanĝis sian staton kaj nun estas aktiva +Comment[es]=Una tecla de bloqueo (como Bloq Mayús o Bloq Num) ha cambiado su estado y está activa +Comment[et]=Lukustusklahv (nt. Caps Lock või Num Lock) muutis oma seisundit ning on nüüd aktiivne +Comment[eu]=Blokeo-tekla bat (adibidez, Blok Maius edo Blok Zenb) egoeraz aldatu da, eta orain aktibo dago +Comment[fi]=Lukitusnäppäin (esim. CapsLock tai NumLock) on vaihtanut tilaansa ja on nyt aktiivinen +Comment[fr]=Une touche de verrouillage (ex : « Verr. Maj. » ou « Verr. Num. ») a changé d'état et est maintenant active +Comment[fy]=De tastân fan in beskoatteltoets (lykas kaptaalskoattel of Nûm skoattel) is feroare en is no aktyf +Comment[ga]=Athraíodh staid eochrach glasála (m.sh. Caps Lock nó Num Lock) agus tá sí gníomhach anois +Comment[gl]=Unha tecla de bloqueo (por ex. Bloq Maiús ou Bloq Num) cambiou de estado e agora está activa +Comment[gu]=તાળાં કળે (દા.ત., કેપ્સ લોક અથવા નમ લોક) હવે તેની સ્થિતિ બદલી છે અને હવે સક્રિય છે +Comment[he]=מקש נועל (למשל Caps Lock או Num Lock) שינה את מצבו וכרגע פעיל +Comment[hne]=कोई ताला कुंजी (जइसन कि कैप्स लाक या न्यूम लाक) हर अपन स्थिति ल बदल दे हे अउ अब वो हर सक्रिय हो गे हे +Comment[hr]=Tipka zaključavanja (npr. CapsLock ili NumLock) promijenila je svoje stanje i aktivna je +Comment[hsb]=Tasta za stajne zaswěćene (na př. Stajny Wulkopis) je status změniła a je nětko aktiwna +Comment[hu]=Egy zároló billentyű (pl. a Caps Lock vagy a Num Lock) aktívvá vált +Comment[ia]=Un clave de bloco (p.ex. Caps Lock o Num Lock) ha cambiate su stato e ora es active +Comment[id]=Sebuah tombol kunci (misalnya Caps Lock atau Num Lock) telah diubah keadaannya dan sekarang aktif +Comment[is]=Læsilykill (t.d. Caps Lock eða Num Lock) hefur breytt um stöðu og er nú virkur +Comment[it]=Un tasto di blocco (ad es. Bloc Maiusc o Bloc Num) ha cambiato stato ed è ora attivo +Comment[ja]=ロックキー (Caps Lock や Num Lock) の状態が変更され、非アクティブになりました +Comment[kk]=Қосқыш перне (мыс. Caps Lock не Num Lock) күйін ауыстырып енді белсенді +Comment[km]=គ្រាប់ចុច​ចាក់សោ (ឧ. ប្ដូរ​ជាប់(Caps Lock) ឬ​គ្រាប់ចុច​លេខ) បាន​ផ្លាស់ប្ដូរ​ស្ថានភាព​របស់វា ហើយ​តឡូវ​សកម្ម +Comment[kn]=ಒಂದು ಭಂಧನ (ಲಾಕ್) ಕೀಲಿ (ಉ.ದಾ Shift ಅಥವಾ Ctrl) ನ ಸ್ಥಿತಿಯು ಬದಲಾಗಿದ್ದು ಈಗ ಸಕ್ರಿಯಗೊಂಡಿದೆ +Comment[ko]=Caps Lock이나 Num Lock 키 같은 잠금 키의 상태가 바뀌었고 현재 활성화됨 +Comment[ku]=Rewşa Bişkojkeke Mifteyê (wekî Caps Lock an jî Num Lock) hate guhertin û niha çalak e +Comment[lt]=Užrakto klavišo (pvz., Didž(Caps Lock) arba Sk(Num Lock)) būsena pakito ir jis dabar – aktyvus +Comment[lv]=Slēga taustiņš (piem. Caps Lock vai Num Lock) ir mainījis stāvokli un tagad ir aktīvs +Comment[mk]=Некое копче за заклучување (пр. Caps Lock или Num Lock) си ја смени состојбата и сега е активно +Comment[ml]=ഒരു ലോക്ക് കീ (ഉദാ. കാപ്സ് ലോക്ക് അല്ലെങ്കില്‍ നം ലോക്ക്) അവസ്ഥ മാറി സജീവമാക്കിയിരിക്കുന്നു +Comment[mr]=कुलूपबंद कि (उ.दा. Caps Lock किंवा Num Lock) ने स्तर बदलविले आहे व आता सक्रिय आहे +Comment[nb]=En låsetast (f.eks. Caps Lock eller Num Lock) har endret status og er nå slått på +Comment[nds]=En Tastrast (Tallen- oder Grootschriev-Rast a.B.) hett sien Tostand ännert un is nu inleggt +Comment[nl]=Een vergrendeltoets (bijv. Caps Lock of Num Lock ) is nu actief +Comment[nn]=Ein låsetast (som «Caps Lock» eller «Num Lock») har endra status, og er no aktiv +Comment[or]=ଗୋଟିଏ ଅପରିବର୍ତ୍ତନୀୟ କି (ଯେପରିକି Caps Lock କିମ୍ବା Num Lock) ସେମାନଙ୍କର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିଛନ୍ତି ଏବଂ ବର୍ତ୍ତମାନ ସକ୍ରିୟ ଅଛନ୍ତି +Comment[pl]=Klawisz blokujący (np. Caps Lock lub Num Lock) zmienił stan i jest teraz włączony +Comment[pt]=Uma tecla de bloqueio (p.ex., a Caps Lock ou a Num Lock) alterou o seu estado e ficou agora activa +Comment[pt_BR]=Uma tecla de bloqueio (p.ex. Caps Lock ou Num Lock) teve seu estado alterado e agora está ativa +Comment[ro]=O tastă de blocare (cum ar fi Caps Lock sau Num Lock) și-a schimbat starea și este acum activă +Comment[ru]=Клавиша переключения режимов (например, Caps Lock или Num Lock) изменила своё состояние и сейчас активна +Comment[si]=අගුළු යතුරේ (උදා. Caps Lock හෝ Num Lock) තත්වය වෙනස් වී ඇති අතර එය දැන් සක්‍රීයයි +Comment[sk]=Zamykací kláves (napr. Caps Lock alebo Num Lock) zmenil svoj stav a je teraz aktívny +Comment[sl]=Zaklepna tipka (npr. Caps Lock ali Num Lock) je spremenila stanje in je sedaj dejavna +Comment[sr]=Закључавајући тастер (нпр. CapsLock или NumLock) променио је стање и сада је активан +Comment[sr@ijekavian]=Закључавајући тастер (нпр. CapsLock или NumLock) промијенио је стање и сада је активан +Comment[sr@ijekavianlatin]=Zaključavajući taster (npr. CapsLock ili NumLock) promijenio je stanje i sada je aktivan +Comment[sr@latin]=Zaključavajući taster (npr. CapsLock ili NumLock) promenio je stanje i sada je aktivan +Comment[sv]=En låstangent (t.ex. Caps Lock eller Num Lock) har ändrat tillstånd och är nu aktiv +Comment[ta]=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now active +Comment[te]=అప్రవేశపు కీ (ఉ.దా. Caps Lock లేదా Num Lock) దాని స్థితి మార్చుకుంది మరియు ఇప్పుడు క్రియాశీలంగా ఉంది +Comment[tg]=Клавиша переключения режимов (например, Caps Lock или Num Lock) изменила своё состояние и сейчас активна +Comment[th]=ปุ่มล็อค (เช่น Caps Lock หรือ Num Lock) ได้มีการเปลี่ยนสถานะของมันและขณะนี้กำลังทำงานอยู่ +Comment[tr]=Bir kilitleme tuşunun (Caps Lock ya da Num Lock gibi.) durumu değiştirildi ve etkinleştirildi +Comment[ug]=بىر قۇلۇپلاش كۇنۇپكىسى (مەسىلەن، Caps Lock ياكى Num Lock) ئۇنىڭ ھالىتىنى ئۆزگەرتتى، ھازىر ئاكتىپلاندى +Comment[uk]=Клавіша фіксації (наприклад Caps Lock або Num Lock) змінила свій стан і тепер активна +Comment[wa]=Ene eclawåve tape ki sere (metans CAPS LOCK oudonbén NUM LOCK) a candjî si estat eyet est asteure ovrante +Comment[x-test]=xxA lock key (e.g. Caps Lock or Num Lock) has changed its state and is now activexx +Comment[zh_CN]=锁定键(如 Caps Lock 或 Num Lock)更改了其状态,现已激活 +Comment[zh_TW]=一個鎖定鍵(如 CapsLock 或 NumLock)已改變它的狀態為啟用 +Sound=KDE-Sys-Special.ogg + +nopresentation=236 + +[Event/lockkey-unlocked] +Name=A lock key has been deactivated +Name[af]='n Sluit sleutel was gedeaktiveer +Name[ar]=تم إلغاء تفعيل مفتاح قفل +Name[ast]=Desactivóse una tecla de bloquéu +Name[be@latin]=Klaviša macavańnia adcisnutaja +Name[bg]=Изключен е клавиш за превключване на режим +Name[bn]=একটি লক কী (lock key) নিষ্ক্রীয় হয়েছে +Name[bs]=Zaključavajući taster je deaktiviran +Name[ca]=S'ha desactivat alguna tecla de fixació +Name[ca@valencia]=S'ha desactivat alguna tecla de fixació +Name[cs]=Klávesa pro přepnutí stavu byla deaktivována +Name[csb]=Klawisza blokadë òsta deaktiwòwónô +Name[da]=En låsetast er blevet deaktiveret +Name[de]=Eine Sperrtaste ist deaktiviert worden +Name[el]=Ένα πλήκτρο κλειδώματος απενεργοποιήθηκε +Name[en_GB]=A lock key has been deactivated +Name[eo]=Baskula klavo malaktiviĝis +Name[es]=Se ha desactivado una tecla de bloqueo +Name[et]=Lukustusklahv on nüüd mitteaktiivne +Name[eu]=Blokeo-tekla bat desaktibatu da +Name[fi]=Lukitusnäppäin on nyt deaktivoitu +Name[fr]=Une touche de verrouillage a été désactivée +Name[fy]=In beskoatteltoets is ûntaktivearre +Name[ga]=Tá eochair ghlasála neamhghníomhach anois +Name[gl]=Desactivouse unha tecla de bloqueo +Name[gu]=તાળાં કળ નિષ્ક્રિય કરવામાં આવી છે +Name[he]=מקש נועל הפך ללא פעיל +Name[hi]=कोई तालाबंद कुंजी अक्रिय किया जा चुका है +Name[hne]=कोई ताला कुंजी हर अक्रिय हो गे हे +Name[hr]=Tipka zaključavanja je deaktivirana +Name[hsb]=Tasta za stajne zaswěćene je nětko njeaktiwna +Name[hu]=Egy zároló billentyű inaktívvá vált +Name[ia]=Un clave de bloco ha essite deactivate +Name[id]=Sebuah tombol kunci telah dinonaktifkan +Name[is]=Læsilykill hefur verið aftengdur +Name[it]=Un tasto di blocco è stato disattivato +Name[ja]=ロックキーが非アクティブになりました +Name[ka]=დაბლოკვის ღილაკი ამორთულია +Name[kk]=Қосқыш перне белсенсіздірілді +Name[km]=គ្រាប់​ចុច​ចាក់​សោ​ត្រូវ​បាន​​​ធ្វើ​ឲ្យ​អសកម្ម +Name[kn]=ಒಂದು ಭಂಧನ ಕೀಲಿಯನ್ನು (ಲಾಕ್) ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=잠금 키가 비활성화됨 +Name[ku]=Bişkojkeke Mifteyê hate neçalakirin +Name[lt]=Užrakto klavišas buvo išjungtas +Name[lv]=Slēga taustiņš tika deaktivizēts +Name[mk]=Некое копче за заклучување беше активирано +Name[ml]=ഒരു ലോക്ക് കീ നിര്‍ജ്ജീവമാക്കിയിരിക്കുന്നു +Name[mr]=कुलूपबंद कि निष्क्रिय केली गेली +Name[nb]=En låsetast er blitt slått av +Name[nds]=En Tastrast is nich mehr inleggt +Name[ne]=लक कुञ्जी निष्क्रिय भएको छ +Name[nl]=Een vergrendeltoets is gedeactiveerd +Name[nn]=Ein låsetast er no inaktiv +Name[or]=ଗୋଟିଏ ଅପରିବର୍ତ୍ତନୀୟ କିକୁ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਇੱਕ ਲਾਕ ਸਵਿੱਚ ਡਿਐਕਟਿਵੇਟ ਕੀਤੀ ਗਈ +Name[pl]=Klawisz blokujący został wyłączony +Name[pt]=Uma tecla de bloqueio foi desactivada +Name[pt_BR]=Uma tecla de bloqueio foi desativada +Name[ro]=O tastă de blocare a fost dezactivată +Name[ru]=Деактивирована клавиша переключения режимов +Name[se]=Lohkkadanboallu ii leat šat aktiivalaš +Name[si]=අගුළු යතුර අක්‍රීයයි +Name[sk]=Zamykací kláves je neaktívny +Name[sl]=Zaklepna tipka je bila onemogočena +Name[sr]=Закључавајући тастер је деактивиран +Name[sr@ijekavian]=Закључавајући тастер је деактивиран +Name[sr@ijekavianlatin]=Zaključavajući taster je deaktiviran +Name[sr@latin]=Zaključavajući taster je deaktiviran +Name[sv]=En låstangent har inaktiverats +Name[ta]=A lock key has been deactivated +Name[te]=అప్రవేశపు కీ క్రియాహీనం చేయబడింది +Name[tg]=Деактивирована клавиша переключения режимов +Name[th]=ปุ่มล็อคถูกปิดการทำงาน +Name[tr]=Bir kilitleme tuşu pasifleştirildi +Name[ug]=بىر قۇلۇپلاش كۇنۇپكىسى پاسسىپلاشتۇرۇلدى +Name[uk]=Було деактивовано клавішу фіксації +Name[wa]=Ene eclawåve tape a stî dismetowe +Name[x-test]=xxA lock key has been deactivatedxx +Name[zh_CN]=锁定键现已停用 +Name[zh_TW]=已解除鎖定鍵 +Comment=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now inactive +Comment[af]='n Sluitsleutel (bv. Caps Lock of Num Lock) se toestand het verander en is nou onaktief +Comment[ar]=تغيرت حالة مفتاح قفل (Caps Lock أو Num Lock) وألغي تفعيله +Comment[ast]=Una tecla de bloquéu (como Bloq Mayús o Bloq Num) camudó l'estáu y non ta activada +Comment[be@latin]=Klaviša macavańnia (naprykład, „Caps Lock” ci „Num Lock”) źmianiła svajo stanovišča j ciapier adcisnutaja. +Comment[bg]=Клавиш за превключване на режим (напр. Caps Lock или Num Lock) промени състоянието си и е неактивен +Comment[bn]=একটি লক কী (e.g. Caps Lock বা Num Lock) অবস্থা পরিবর্তন করে এখন নিষ্ক্রীয় হয়েছে +Comment[bs]=Zaključavajući taster (npr. CapsLock ili NumLock) promijenio je stanje i više nije aktivan +Comment[ca]=Alguna tecla de fixació (p.ex. Bloq Maj o Bloq Núm) ha canviat el seu estat i ara està inactiva +Comment[ca@valencia]=Alguna tecla de fixació (p.ex. Bloq Maj o Bloq Núm) ha canviat el seu estat i ara està inactiva +Comment[cs]=Klávesa pro přepnutí stavu (např. CapsLock nebo NumLock) byla stisknuta a je nyní neaktivní +Comment[csb]=Klawisza blokadë (Caps Lock abò Num Lock) zmienia swój sztatus ë nie je ju aktiwnô +Comment[da]=En låsetast (f.eks. Caps Lock eller Num Lock) har ændret sin tilstand og er nu inaktiv +Comment[de]=Eine Sperrtaste (Feststelltaste oder Nummernblocksperre) hat ihren Status geändert und ist nun inaktiv +Comment[el]=Ένα πλήκτρο κλειδώματος (π.χ. Caps Lock ή Num Lock) άλλαξε την κατάστασή του και είναι τώρα ανενεργό +Comment[en_GB]=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now inactive +Comment[eo]=Baskula klavo (ekz. majuskla baskulo aŭ nombra baskulo) ŝanĝis sian staton kaj nun estas malaktiva +Comment[es]=Una tecla de bloqueo (como Bloq Mayús o Bloq Num) ha cambiado su estado y no está activa +Comment[et]=Lukustusklahv (nt. Caps Lock või Num Lock) muutis oma seisundit ning on nüüd mitteaktiivne +Comment[eu]=Blokeo-tekla bat (adibidez, Blok Maius edo Blok Zenb) egoeraz aldatu da, eta orain inaktibo dago +Comment[fi]=Lukitusnäppäin (esim. CapsLock tai NumLock) on vaihtanut tilaansa ja ei ole enää aktiivinen +Comment[fr]=Une touche de verrouillage (ex : « Verr. Maj. » ou « Verr. Num. ») a changé d'état et est maintenant inactive +Comment[fy]=De tastân fan in beskoatteltoets (lykas kaptaalskoattel of Nûm skoattel) is feroare en is no ynaktyf +Comment[ga]=Athraíodh staid eochrach glasála (m.sh. Caps Lock nó Num Lock) agus tá sí neamhghníomhach anois +Comment[gl]=Unha tecla de bloqueo (por ex., Bloq Maiús ou Bloq Num) cambiou de estado e agora está inactiva +Comment[gu]=તાળાં કળે (દા.ત., કેપ્સ લોક અથવા નમ લોક) હવે તેની સ્થિતિ બદલી છે અને હવે તે નિષ્ક્રિય છે +Comment[he]=מקש נועל (למשל Caps Lock או Num Lock) שינה את מצבו וכרגע לא פעיל +Comment[hne]=कोई ताला कुंजी (जइसन कि कैप्स लाक या न्यूम लाक) हर अपन स्थिति ल बदल दे हे अउ अब वो हर अक्रिय हो गे हे +Comment[hr]=Tipka zaključavanja (npr. CapsLock ili NumLock) promijenila je svoje stanje i više nije aktivna +Comment[hsb]=Tasta za stajne zaswěćene (na př. Stajny Wulkopis) je status změniła a je nětko njeaktiwna +Comment[hu]=Egy zároló billentyű (pl. a Caps Lock vagy a Num Lock) inaktívvá vált +Comment[ia]=Un clave de bloco (p.ex. Caps Lock o Num Lock) ha cambiate su stato e ora es inactive +Comment[id]=Sebuah tombol kunci (misalnya Caps Lock atau Num Lock) telah diubah keadaannya dan sekarang tidak aktif +Comment[is]=Læsilykill (t.d. Caps Lock eða Num Lock) hefur breytt um stöðu og er nú óvirkur +Comment[it]=Un tasto di blocco (ad es. Bloc Maiusc o Bloc Num) ha cambiato stato ed è ora disattivo +Comment[ja]=ロックキー (Caps Lock や Num Lock) の状態が変更され、非アクティブになりました +Comment[kk]=Қосқыш перне (мыс. Caps Lock не Num Lock) күйін ауыстырып енді белсенді емес +Comment[km]=គ្រាប់​ចុច​ចាក់​សោ (ឧ. ប្ដូរ​ជាប់ (Caps Lock) ឬ​គ្រាប់ចុច​លេខ (Num Lock)) បាន​ត្រូវ​ផ្លាស់ប្ដូរ​ស្ថានភាព​​របស់​​វា ហើយ​ឥឡូវ​នេះ​អសកម្ម +Comment[kn]=ಒಂದು ಭಂಧನ (ಲಾಕ್) ಕೀಲಿ (ಉ.ದಾ Shift ಅಥವಾ Ctrl) ನ ಸ್ಥಿತಿಯು ಬದಲಾಗಿದ್ದು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ +Comment[ko]=Caps Lock이나 Num Lock 키 같은 잠금 키의 상태가 바뀌었고 현재 비활성화됨 +Comment[ku]=Rewşa bişkojkeke mifteyê (wekî Caps Lock an jî Num Lock) hate guhertin û niha neçalak e +Comment[lt]=Užrakto klavišo (pvz., Didž(Caps Lock) arba Sk(Num Lock)) būsena pakito ir jis dabar – neaktyvus +Comment[lv]=Slēga taustiņš (piem. Caps Lock vai Num Lock) ir mainījis stāvokli un tagad ir neaktīvs +Comment[mk]=Некое копче за заклучување (пр. Caps Lock или Num Lock) си ја смени состојбата и сега е неактивно +Comment[ml]=ഒരു ലോക്ക് കീ (ഉദാ. കാപ്സ് ലോക്ക് അല്ലെങ്കില്‍ നം ലോക്ക്) അവസ്ഥ മാറി നിര്‍ജീവമായിരിക്കുന്നു +Comment[mr]=कुलूपबंद कि (उ.दा. Caps Lock किंवा Num Lock) ने स्तर बदलविले आहे व आता निष्क्रिय आहे +Comment[nb]=En låsetast (f.eks. Caps Lock eller Num Lock) har endret status og er nå slått av +Comment[nds]=En Tastrast (Tallen- oder Grootschriev-Rast a.B.) hett sien Tostand ännert un is nu nich mehr inleggt +Comment[nl]=Een vergrendeltoets (bijv. Caps Lock of Num Lock ) is nu inactief +Comment[nn]=Ein låsetast (som «Caps Lock» eller «Num Lock») har endra status, og er no inaktiv +Comment[or]=ଗୋଟିଏ ଅପରିବର୍ତ୍ତନୀୟ କି (ଯେପରିକି Caps Lock କିମ୍ବା Num Lock) ସେମାନଙ୍କର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିଛନ୍ତି ଏବଂ ବର୍ତ୍ତମାନ ନିଷ୍କ୍ରିୟ ଅଛି +Comment[pl]=Klawisz blokujący (np. Caps Lock lub Num Lock) zmienił stan i jest teraz wyłączony +Comment[pt]=Uma tecla de bloqueio (p.ex., a Caps Lock ou a Num Lock) alterou o seu estado e ficou agora inactiva +Comment[pt_BR]=Uma tecla de bloqueio (p.ex. Caps Lock ou Num Lock) teve seu estado alterado e agora está inativa +Comment[ro]=O tastă de blocare (cum ar fi Caps Lock sau Num Lock) și-a schimbat starea și este acum inactivă +Comment[ru]=Клавиша переключения режимов (например, Caps Lock или Num Lock) изменила своё состояние и сейчас не активна +Comment[si]=අගුළු යතුරේ (උදා. Caps Lock හෝ Num Lock) තත්වය වෙනස් වී ඇති අතර එය දැන් අක්‍රීයයි +Comment[sk]=Zamykací kláves (napr. Caps Lock alebo Num Lock) zmenil svoj stav a je teraz neaktívny +Comment[sl]=Zaklepna tipka (npr. Caps Lock ali Num Lock) je spremenila stanje in je sedaj nedejavna +Comment[sr]=Закључавајући тастер (нпр. CapsLock или NumLock) променио је стање и више није активан +Comment[sr@ijekavian]=Закључавајући тастер (нпр. CapsLock или NumLock) промијенио је стање и више није активан +Comment[sr@ijekavianlatin]=Zaključavajući taster (npr. CapsLock ili NumLock) promijenio je stanje i više nije aktivan +Comment[sr@latin]=Zaključavajući taster (npr. CapsLock ili NumLock) promenio je stanje i više nije aktivan +Comment[sv]=En låstangent (t.ex. Caps Lock eller Num Lock) har ändrat tillstånd och är nu inaktiv +Comment[ta]=A lock key (e.g. Caps Lock or Num Lock) has changed its state and is now inactive +Comment[te]=అప్రవేశపు కీ (ఉ.దా. Caps Lock లేదా Num Lock) దాని స్థితిని మార్చుకుంది మరియు ఇప్పుడు క్రియాహీనంగా ఉంది +Comment[tg]=Клавиша переключения режимов (например, Caps Lock или Num Lock) изменила своё состояние и сейчас не активна +Comment[th]=ปุ่มล็อค (เช่น Caps Lock หรือ Num Lock) ได้มีการเปลี่ยนสถานะของมันและขณะนี้ได้ปิดการทำงาน +Comment[tr]=Bir kilitleme tuşunun (Caps Lock ya da Num Lock gibi.) durumu değiştirildi ve pasifleştirildi +Comment[ug]=بىر قۇلۇپلاش كۇنۇپكىسى (مەسىلەن، Caps Lock ياكى Num Lock) ئۇنىڭ ھالىتىنى ئۆزگەرتتى، ھازىر پاسسىپلاشتۇرۇلدى +Comment[uk]=Клавіша фіксації (наприклад Caps Lock або Num Lock) змінила свій стан і тепер неактивна +Comment[wa]=Ene eclawåve tape (metans CAPS LOCK oudonbén NUM LOCK) a candjî si estat eyet n' est asteure pus ovrante +Comment[x-test]=xxA lock key (e.g. Caps Lock or Num Lock) has changed its state and is now inactivexx +Comment[zh_CN]=锁定键(如 Caps Lock 或 Num Lock)更改了其状态,现已停用 +Comment[zh_TW]=一個鎖定鍵(如 CapsLock 或 NumLock)已改變它的狀態成為未啟用 +Sound=KDE-Sys-Special.ogg + +nopresentation=236 + +[Event/stickykeys] +Name=Sticky keys has been enabled or disabled +Name[af]=Sticky sleutels was geaktiveer of gedeaktiveer +Name[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح المثبتة +Name[ast]=Les tecles pegañoses activáronse o desactiváronse +Name[be@latin]=Lipučyja klavišy źmianili svajo stanovišča +Name[bg]="Лепкави" клавиши са включени или изключени +Name[bs]=Ljepljivi tasteri su uključeni ili isključeni +Name[ca]=Les tecles apegaloses s'han activat o desactivat +Name[ca@valencia]=Les tecles apegaloses s'han activat o desactivat +Name[cs]=Lepivé klávesy byly povoleny nebo zakázány +Name[csb]=Klawisze przëlepieniô òstałë aktiwòwòné abò deaktiwòwóné +Name[da]=Klæbende taster er aktiveret eller deaktiveret +Name[de]=Klebende Tasten sind aktiviert oder deaktiviert worden +Name[el]=Τα κολλημένα πλήκτρα ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Name[en_GB]=Sticky keys has been enabled or disabled +Name[eo]=Fiksaj klavoj validiĝis aŭ malvalidiĝis +Name[es]=Las teclas pegajosas se han activado o desactivado +Name[et]=Kleepuvad klahvid on keelatud või lubatud +Name[eu]=Tekla itsaskorrak gaitu edo desgaitu dira +Name[fi]=Tahmeat näppäimet on otettu käyttöön tai poistettu käytöstä +Name[fr]=L'auto-maintien des touches a été activé ou désactivé +Name[fy]=Plakkende toetsen binne aktivearre of deaktivearre +Name[ga]=Cumasaíodh nó díchumasaíodh eochracha greamaitheacha +Name[gl]=As teclas pegañentas activáronse ou desactiváronse +Name[gu]=સ્થિત કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Name[he]=מקשים דביקים הופעלו או בוטלו +Name[hi]=चिपकती कुंजियों को सक्षम या अक्षम किया गया है +Name[hne]=स्टिकी कुंजी सक्रिय या अक्रिय हो गे हे +Name[hr]=Ljepljive tipke su omogućene ili onemogućene +Name[hsb]=Lěpjate tasty zaswěćene abo hasnjene +Name[hu]=Ragadós billentyűk átkapcsolva +Name[ia]=Le claves collate ha essite habilitate o dishabilitate +Name[id]=Tombol lekat telah diaktifkan atau dinonaktifkan +Name[is]=Slökkt eða kveikt hefur verið á klístruðum lyklum +Name[it]=I tasti permanenti sono stati attivati o disattivati +Name[ja]=スティッキーキーが有効または無効になりました +Name[kk]=Жабысқақ пернелер рұқсат етілді/етілмеді +Name[km]=គ្រាប់​ចុច​ស្អិត​​ត្រូវ​បាន​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Name[kn]=ಅಂಟು (ಸ್ಟಿಕಿ) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=끈적이 키가 활성화되거나 비활성화됨 +Name[ku]=Bişkojkên Mezeloqî hatin çalakirin an jî neçalakirin +Name[lt]=Lipnūs klavišai buvo įgalinti arba išjungti +Name[lv]=Lipīgie taustiņi tika ieslēgti vai izslēgti +Name[mk]=„Лепливи копчиња“ е овозможено или оневозможено +Name[ml]=സ്റ്റിക്കി കീ പ്രവര്‍ത്തന സജ്ജം അല്ലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതം ആക്കിയിരിക്കുന്നു +Name[mr]=स्टिकी कि कार्यन्वीत किंवा अकार्यान्वित केली गेली आहे +Name[nb]=Faste valgtaster er blitt slått på eller av +Name[nds]="Backig Tasten" wöör an- oder utmaakt +Name[ne]=स्टिकी कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Name[nl]=Plakkende toetsen is geactiveerd of gedeactiveerd +Name[nn]=Faste valtastar er no slått på eller av +Name[or]=Sticky କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਸਟਿੱਕੀ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Name[pl]=Lepkie klawisze zostały włączone lub wyłączone +Name[pt]=As teclas fixas foram activadas ou desactivadas +Name[pt_BR]=Teclas de aderência foram habilitadas ou desabilitadas +Name[ro]=Taste lipicioase au fost activate sau dezactivate +Name[ru]=Включены или отключены «залипающие» клавиши +Name[se]=Giddes válljenboallu lea rievdadan stáhtusa +Name[si]=ඇළෙන යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Name[sk]=Lepkavé klávesy zmenili svoj stav +Name[sl]=Lepljive tipke so bile omogočene ali onemogočene +Name[sr]=Лепљиви тастери су укључени или искључени +Name[sr@ijekavian]=Љепљиви тастери су укључени или искључени +Name[sr@ijekavianlatin]=Ljepljivi tasteri su uključeni ili isključeni +Name[sr@latin]=Lepljivi tasteri su uključeni ili isključeni +Name[sv]=Klistriga tangenter har aktiverats eller inaktiverats +Name[ta]=Sticky keys has been enabled or disabled +Name[te]=స్టికీ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Name[tg]=Тугмаҳои часпанда фаъол ё ғайрифаъол шудаанд +Name[th]=การกดปุ่มพิมพ์ค้างได้ถูกเปิดหรือปิดการทำงาน +Name[tr]=Yapışkan tuşlar etkinleştirildi ya da pasifleştirildi +Name[ug]=چاپلىشاڭغۇ(Sticky) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Name[uk]=Було активовано або деактивовано липкі клавіші +Name[wa]=Les aclapantès tapes on stî metowes oudonbén dismetowes +Name[x-test]=xxSticky keys has been enabled or disabledxx +Name[zh_CN]=启用或禁用了粘滞键 +Name[zh_TW]=相黏鍵已改變狀態 +Comment=Sticky keys has been enabled or disabled +Comment[af]=Sticky sleutels was geaktiveer of gedeaktiveer +Comment[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح المثبتة +Comment[ast]=Les tecles pegañoses activáronse o desactiváronse +Comment[be@latin]=Lipučyja klavišy źmianili svajo stanovišča. +Comment[bg]="Лепкави" клавиши са включени или изключени +Comment[bs]=Ljepljivi tasteri su uključeni ili isključeni +Comment[ca]=Les tecles apegaloses s'han activat o desactivat +Comment[ca@valencia]=Les tecles apegaloses s'han activat o desactivat +Comment[cs]=Lepivé klávesy byly povoleny nebo zakázány +Comment[csb]=Klawisze przëlepieniô òstałë aktiwòwòné abò deaktiwòwóné +Comment[da]=Klæbende taster er aktiveret eller deaktiveret +Comment[de]=Die Funktion „Klebende Tasten“ ist aktiviert oder deaktiviert worden +Comment[el]=Τα κολλημένα πλήκτρα ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Comment[en_GB]=Sticky keys has been enabled or disabled +Comment[eo]=Fiksaj klavoj validiĝis aŭ malvalidiĝis +Comment[es]=Las teclas pegajosas se han activado o desactivado +Comment[et]=Kleepuvad klahvid on keelatud või lubatud +Comment[eu]=Tekla itsaskorrak gaitu edo desgaitu dira +Comment[fi]=Tahmeat näppäimet on otettu käyttöön tai poistettu käytöstä +Comment[fr]=L'auto-maintien des touches a été activé ou désactivé +Comment[fy]=Plakkende toetsen binne aktivearre of deaktivearre +Comment[ga]=Cumasaíodh nó díchumasaíodh eochracha greamaitheacha +Comment[gl]=As teclas pegañentas foron ou activadas ou desactivadas +Comment[gu]=સ્થિત કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Comment[he]=מקשים דביקים הופעלו או בוטלו +Comment[hi]=चिपकती कुंजियों को सक्षम या अक्षम किया गया है +Comment[hne]=स्टिकी कुंजी सक्रिय या अक्रिय हो गे हे +Comment[hr]=Ljepljive tipke su omogućene ili onemogućene +Comment[hsb]=Lěpjate tasty zaswěćene abo hasnjene +Comment[hu]=A ragadós billentyűk használatát ki- vagy bekapcsolták +Comment[ia]=Le claves collate ha essite habilitate o dishabilitate +Comment[id]=Tombol lekat telah diaktifkan atau dinonaktifkan +Comment[is]=Slökkt eða kveikt hefur verið á klístruðum lyklum +Comment[it]=I tasti permanenti sono stati attivati o disattivati +Comment[ja]=スティッキーキーが有効または無効になりました +Comment[kk]=Жабысқақ пернелер рұқсат етілді/етілмеді +Comment[km]=គ្រាប់​ចុច​ស្អិត​​ត្រូវ​បាន​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Comment[kn]=ಅಂಟು (ಸ್ಟಿಕಿ) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Comment[ko]=끈적이 키가 활성화되거나 비활성화됨 +Comment[ku]=Bişkojkên Mezeloqî hatin çalakirin an jî neçalakirin +Comment[lt]=Lipnūs klavišai buvo įjungti arba išjungti +Comment[lv]=Lipīgie taustiņi tika ieslēgti vai izslēgti +Comment[mk]=„Лепливи копчиња“ е овозможено или оневозможено +Comment[ml]=സ്റ്റിക്കി കീ പ്രവര്‍ത്തന സജ്ജം അല്ലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതം ആക്കിയിരിക്കുന്നു +Comment[mr]=स्टिकी कि कार्यन्वीत किंवा अकार्यान्वित केली गेली आहे +Comment[nb]=Faste valgtaster er blitt slått på eller av +Comment[nds]="Backig Tasten" wöör an- oder utmaakt +Comment[ne]=स्टिकी कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Comment[nl]=Plakkende toetsen is geactiveerd of gedeactiveerd +Comment[nn]=Faste valtastar er no slått på eller av +Comment[or]=Sticky କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Comment[pa]=ਸਟਿੱਕੀ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Comment[pl]=Lepkie klawisze zostały włączone lub wyłączone +Comment[pt]=As teclas fixas foram activadas ou desactivadas +Comment[pt_BR]=Teclas de aderência foram habilitadas ou desabilitadas +Comment[ro]=Taste lipicioase au fost activate sau dezactivate +Comment[ru]=Включены или отключены «залипающие» клавиши +Comment[se]=Giddes válljenboallu lea rievdadan stáhtusa +Comment[si]=ඇළෙන යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Comment[sk]=Lepkavé klávesy zmenili svoj stav +Comment[sl]=Lepljive tipke so bile omogočene ali onemogočene +Comment[sr]=Лепљиви тастери су укључени или искључени +Comment[sr@ijekavian]=Љепљиви тастери су укључени или искључени +Comment[sr@ijekavianlatin]=Ljepljivi tasteri su uključeni ili isključeni +Comment[sr@latin]=Lepljivi tasteri su uključeni ili isključeni +Comment[sv]=Klistriga tangenter har aktiverats eller inaktiverats +Comment[ta]=Sticky keys has been enabled or disabled +Comment[te]=స్టికీ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Comment[tg]=Тугмаҳои часпанда фаъол ё ғайрифаъол шудаанд +Comment[th]=การกดปุ่มพิมพ์ค้างได้ถูกเปิดหรือปิดการทำงาน +Comment[tr]=Yapışkan tuşlar etkinleştirildi ya da pasifleştirildi +Comment[ug]=چاپلىشاڭغۇ(Sticky) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Comment[uk]=Було активовано або деактивовано липкі клавіші +Comment[wa]=Les aclapantès tapes on stî metowes oudonbén dismetowes +Comment[x-test]=xxSticky keys has been enabled or disabledxx +Comment[zh_CN]=启用或禁用了粘滞键 +Comment[zh_TW]=相黏鍵已啟動或關閉。 +Sound=KDE-Sys-Special.ogg +Action=Popup +nopresentation=236 + +[Event/slowkeys] +Name=Slow keys has been enabled or disabled +Name[af]=Stadige sleutels was geaktiveer of gedeaktiveer +Name[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح البطيئة +Name[ast]=Les tecles lentes activáronse o desactiváronse +Name[be@latin]=Pavolnyja klavišy źmianili svajo stanovišča +Name[bg]="Бавни" клавиши са включени или изключени +Name[bs]=Spori tasteri su uključeni ili isključeni +Name[ca]=Les tecles lentes s'han activat o desactivat +Name[ca@valencia]=Les tecles lentes s'han activat o desactivat +Name[cs]=Pomalé klávesy byly povoleny nebo zakázány +Name[csb]=Pòwòlné klawisze òstałë aktiwòwòné abò deaktiwòwóné +Name[da]=Langsomme taster er aktiveret eller deaktiveret +Name[de]=Langsame Tasten sind aktiviert oder deaktiviert worden +Name[el]=Τα αργά πλήκτρα ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Name[en_GB]=Slow keys has been enabled or disabled +Name[eo]=Malrapidklavoj validiĝis aŭ malvalidiĝis +Name[es]=Las teclas lentas se han activado o desactivado +Name[et]=Aeglased klahvid on keelatud või lubatud +Name[eu]=Tekla motelak gaitu edo desgaitu dira +Name[fi]=Hitaat näppäimet on otettu käyttöön tai poistettu käytöstä +Name[fr]=Les touches lentes ont été activées ou désactivées +Name[fy]=Trage toetsen binne aktivearre of ûntaktivearre +Name[ga]=Cumasaíodh nó díchumasaíodh eochracha malla +Name[gl]=As teclas lentas activáronse ou desactiváronse +Name[gu]=ધીમી કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Name[he]=תגובה איטית של המקשים הופעלה או הופסקה +Name[hi]=धीमी कुंजियों को सक्षम या अक्षम किया गया है +Name[hne]=धीमा कुंजी सक्रिय या अक्रिय हो गे हे +Name[hr]=Spore tipke su omogućene ili onemogućene +Name[hsb]=Pomałe tasty zaswěćene abo hasnjene +Name[hu]=A lassú billentyűk használata átkapcsolva +Name[ia]=Le claves lente ha essite activate o disactivate +Name[id]=Tombol pelan telah diaktifkan atau dinonaktifkan +Name[is]=Slökkt eða kveikt hefur verið á hægum lyklum +Name[it]=Il rallentamento dei tasti è stato attivato o disattivato +Name[ja]=スローキーが有効または無効になりました +Name[kk]=Баяу пернелер рұқсат етілді/етілмеді +Name[km]=គ្រាប់​ចុច​យឺត​​ត្រូវ​បាន​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Name[kn]=ನಿಧಾನ (ಸ್ಲೋ) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=느린 키가 활성화되거나 비활성화됨 +Name[ku]=Bişkojkên Hêdî hatin çalakirin an jî neçalakirin +Name[lt]=Lėti klavišai buvo įgalinti arba išjungti +Name[lv]=Lēnie taustiņi tika ieslēgti vai izslēgti +Name[mk]=„Спори копчиња“ е овозможено или оневозможено +Name[ml]=സ്ലോ കീ പ്രവര്‍ത്തന സജ്ജമലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതമാക്കിയിരിക്കുന്നു +Name[mr]=हळु कार्यरत कि आता कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Name[nb]=Trege taster er blitt slått på eller av +Name[nds]="Langsam Tasten" wöör an- oder utmaakt +Name[ne]=शो कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Name[nl]=Langzame toetsen is geactiveerd of gedeactiveerd +Name[nn]=Trege tastar er no slått på eller av +Name[or]=Slow କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਸਲੋਅ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Name[pl]=Powolne klawisze zostały włączone lub wyłączone +Name[pt]=As teclas lentas foram activadas ou desactivadas +Name[pt_BR]=Teclas lentas foram habilitadas ou desabilitadas +Name[ro]=Taste lente (Slow keys) au fost activate sau dezactivate +Name[ru]=Включены или отключены замедленные клавиши +Name[si]=වේගය අඩු යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Name[sk]=Pomalé klávesy zmenili svoj stav +Name[sl]=Počasne tipke so bile omogočene ali onemogočene +Name[sr]=Спори тастери су укључени или искључени +Name[sr@ijekavian]=Спори тастери су укључени или искључени +Name[sr@ijekavianlatin]=Spori tasteri su uključeni ili isključeni +Name[sr@latin]=Spori tasteri su uključeni ili isključeni +Name[sv]=Långsamma tangenter har aktiverats eller inaktiverats +Name[ta]=Slow keys has been enabled or disabled +Name[te]=స్లో కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Name[tg]=Тугмаҳои суст фаъол ё ғайрифаъол шудаанд +Name[th]=การกดปุ่มพิมพ์ช้าได้ถูกเปิดหรือปิดการทำงาน +Name[tr]=Yavaş tuşlar etkinleştirildi ya da pasifleştirildi +Name[ug]=ئاستا(Slow) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Name[uk]=Було активовано або деактивовано повільні клавіші +Name[wa]=Des londjinnès tapes ont stî metowe en alaedje oudonbén dismetowes +Name[x-test]=xxSlow keys has been enabled or disabledxx +Name[zh_CN]=启用或禁用了筛选键 +Name[zh_TW]=慢速鍵已改變狀態 +Comment=Slow keys has been enabled or disabled +Comment[af]=Stadige sleutels was geaktiveer of gedeaktiveer +Comment[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح البطيئة +Comment[ast]=Les tecles lentes activáronse o desactiváronse +Comment[be@latin]=Pavolnyja klavišy źmianili svajo stanovišča. +Comment[bg]="Бавни" клавиши са включени или изключени +Comment[bs]=Spori tasteri su uključeni ili isključeni +Comment[ca]=Les tecles lentes s'han activat o desactivat +Comment[ca@valencia]=Les tecles lentes s'han activat o desactivat +Comment[cs]=Pomalé klávesy byly povoleny nebo zakázány +Comment[csb]=Pòwòlné klawisze òstałë aktiwòwòné abò deaktiwòwóné +Comment[da]=Langsomme taster er aktiveret eller deaktiveret +Comment[de]=Die Funktion „Langsame Tasten“ ist aktiviert oder deaktiviert worden +Comment[el]=Τα αργά πλήκτρα ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Comment[en_GB]=Slow keys has been enabled or disabled +Comment[eo]=Malrapidklavoj validiĝis aŭ malvalidiĝis +Comment[es]=Las teclas lentas se han activado o desactivado +Comment[et]=Aeglased klahvid on keelatud või lubatud +Comment[eu]=Tekla motelak gaitu edo desgaitu dira +Comment[fi]=Hitaat näppäimet on otettu käyttöön tai poistettu käytöstä +Comment[fr]=Les touches lentes ont été activées ou désactivées +Comment[fy]=Trage toetsen binne aktivearre of ûntaktivearre +Comment[ga]=Cumasaíodh nó díchumasaíodh eochracha malla +Comment[gl]=As teclas lentas foron ou activadas ou desactivadas +Comment[gu]=ધીમી કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Comment[he]=תגובה איטית של המקשים הופעלה או הופסקה +Comment[hi]=धीमी कुंजियों को सक्षम या अक्षम किया गया है +Comment[hne]=धीमा कुंजी सक्छम या अक्छम हो गे हे +Comment[hr]=Spore tipke su omogućene ili onemogućene +Comment[hsb]=Pomałe tasty zaswěćene abo hasnjene +Comment[hu]=A lassú billentyűk használatát ki- vagy bekapcsolták +Comment[ia]=Le claves lente ha essite activate o disactivate +Comment[id]=Tombol pelan telah diaktifkan atau dinonaktifkan +Comment[is]=Slökkt eða kveikt hefur verið á hægum lyklum +Comment[it]=Il rallentamento dei tasti è stato attivato o disattivato +Comment[ja]=スローキーが有効または無効になりました +Comment[kk]=Баяу пернелер рұқсат етілді/етілмеді +Comment[km]=គ្រាប់​ចុច​យឺត​ត្រូវ​បាន​​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Comment[kn]=ನಿಧಾನ (ಸ್ಲೋ) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Comment[ko]=느린 키가 활성화되거나 비활성화됨 +Comment[ku]=Bişkojkên Hêdî hatin çalakirin an jî neçalakirin +Comment[lt]=Lėti klavišai buvo įgalinti arba išjungti +Comment[lv]=Lēnie taustiņi tika ieslēgti vai izslēgti +Comment[mk]=„Спори копчиња“ е овозможено или оневозможено +Comment[ml]=സ്ലോ കീ പ്രവര്‍ത്തന സജ്ജമലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതമാക്കിയിരിക്കുന്നു +Comment[mr]=हळु कार्यरत कि आता कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Comment[nb]=Trege taster er blitt slått på eller av +Comment[nds]="Langsam Tasten" wöör an- oder utmaakt +Comment[ne]=शो कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Comment[nl]=Langzame toetsen is geactiveerd of gedeactiveerd +Comment[nn]=Trege tastar er no slått på eller av +Comment[or]=Slow କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Comment[pa]=ਸਲੋਅ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Comment[pl]=Powolne klawisze zostały włączone lub wyłączone +Comment[pt]=As teclas lentas foram activadas ou desactivadas +Comment[pt_BR]=Teclas lentas foram habilitadas ou desabilitadas +Comment[ro]=Taste lente (Slow keys) au fost activate sau dezactivate +Comment[ru]=Включены или отключены замедленные клавиши +Comment[si]=වේගය අඩු යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Comment[sk]=Pomalé klávesy zmenili svoj stav +Comment[sl]=Počasne tipke so bile omogočene ali onemogočene +Comment[sr]=Спори тастери су укључени или искључени +Comment[sr@ijekavian]=Спори тастери су укључени или искључени +Comment[sr@ijekavianlatin]=Spori tasteri su uključeni ili isključeni +Comment[sr@latin]=Spori tasteri su uključeni ili isključeni +Comment[sv]=Långsamma tangenter har aktiverats eller inaktiverats +Comment[ta]=Slow keys has been enabled or disabled +Comment[te]=స్లో కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Comment[tg]=Тугмаҳои суст фаъол ё ғайрифаъол шудаанд +Comment[th]=การกดปุ่มพิมพ์ช้าได้ถูกเปิดหรือปิดการทำงาน +Comment[tr]=Yavaş tuşlar etkinleştirildi ya da pasifleştirildi +Comment[ug]=ئاستا(Slow) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Comment[uk]=Було активовано або деактивовано повільні клавіші +Comment[wa]=Des londjinnès tapes ont stî metowe en alaedje oudonbén dismetowes +Comment[x-test]=xxSlow keys has been enabled or disabledxx +Comment[zh_CN]=启用或禁用了筛选键 +Comment[zh_TW]=慢速鍵已啟動或關閉。 +Sound=KDE-Sys-Special.ogg +Action=Popup +nopresentation=236 + +[Event/bouncekeys] +Name=Bounce keys has been enabled or disabled +Name[af]=Hopsleutels is geaktiveer of gedeaktiveer +Name[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح المكررة +Name[ast]=Les tecles de rebote activáronse o desactiváronse +Name[bg]="Подскачащи" клавиши са включени или изключени +Name[bs]=Odskačući tasteri su uključeni ili isključeni +Name[ca]=Les tecles de repetició s'han activat o desactivat +Name[ca@valencia]=Les tecles de repetició s'han activat o desactivat +Name[cs]=Zdvojené klávesy byly povoleny nebo zakázány +Name[csb]=Òdbijającé klawisze òstałë aktiwòwòné abò deaktiwòwóné +Name[da]=Elastiske taster er aktiveret eller deaktiveret +Name[de]=Zurückschnellende Tasten sind aktiviert oder deaktiviert worden +Name[el]=Τα πλήκτρα αναπήδησης ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Name[en_GB]=Bounce keys has been enabled or disabled +Name[eo]=Prokrastaj klavoj validiĝis aŭ malvalidiĝis +Name[es]=Se ha activado o desactivado el rebote de teclas +Name[et]=Põrkavad klahvid on keelatud või lubatud +Name[eu]=Errebote-teklak gaitu edo desgaitu dira +Name[fi]=Kimpoavat näppäimet on otettu käyttöön tai poistettu käytöstä +Name[fr]=Les touches non répétées ont été activées ou désactivées +Name[fy]=Stuiterjende toetsen binne aktivearre of ûntaktivearre +Name[ga]=Cumasaíodh nó díchumasaíodh eochracha preabtha +Name[gl]=A anulación temporal de teclas activouse ou desactivouse +Name[gu]=ઉછાળ કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Name[he]=מניעת לחיצה על מספר מקשים בו־זמנית הופעלה או הופסקה +Name[hi]=बाउंस कुंजियों को सक्षम या अक्षम किया गया है +Name[hne]=बाउन्स कुंजी सक्छम या अक्छम हो गे हे +Name[hr]=Obijanje tipki je omogućeno ili onemogućeno +Name[hsb]=Zamkane tasty zaswěćene abo hasnjene +Name[hu]=A visszaugró billentyűk átkapcsolva +Name[ia]=Le claves elastic ha essite activate o disactivate +Name[id]=Tombol pantul telah diaktifkan atau dinonaktifkan +Name[is]=Slökkt eða kveikt hefur verið á skoppandi lyklum +Name[it]=I tasti ravvicinati sono stati abilitati o disattivati +Name[ja]=バウンスキーが有効または無効になりました +Name[kk]=Қалшылға қарсы пернелер рұқсат етілді/етілмеді +Name[km]=គ្រាប់​ចុច​លោត​​ត្រូវ​បាន​​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Name[kn]=ಪುಟಿಯುವ (ಬೌನ್ಸ್) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=튕김 키가 활성화되거나 비활성화됨 +Name[ku]=Bişkojkên Zindî hatin çalakirin an jî neçalakirin +Name[lt]=Tamprūs klavišai buvo įgalinti arba išjungti +Name[lv]=Atlecošie taustiņi tika ieslēgti vai izslēgti +Name[mk]=„Скокачки копчиња“ е овозможено или оневозможено +Name[ml]=ബൌണ്‍സ് കീ പ്രവര്‍ത്തന സജ്ജം അല്ലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതം ആക്കിയിരിക്കുന്നു +Name[mr]=बाउन्स् कि कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Name[nb]=Filtertaster er blitt slått på eller av +Name[nds]="Jumpen Tasten" wöör an- oder utmaakt +Name[ne]=बाउन्स कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Name[nl]=Stuiterende toetsen is geactiveerd of gedeactiveerd +Name[nn]=Filtertastar er no slått på eller av +Name[or]=Bounce କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਬਾਊਂਸ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Name[pl]=Odbijające klawisze zostały włączone lub wyłączone +Name[pt]=As teclas sonoras foram activadas ou desactivadas +Name[pt_BR]=As teclas de repercussão foram ativadas ou desativadas +Name[ro]=Taste săltărețe (Bounce keys) au fost activate sau dezactivate +Name[ru]=Включены или отключены «прыгающие» клавиши +Name[se]=Sillenboallu lea rievdadan stáhtusa +Name[si]=පොළාපැනුම් යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Name[sk]=Bounce klávesy zmenili svoj stav +Name[sl]=Odbijajoče tipke so bile omogočene ali onemogočene +Name[sr]=Одскачући тастери су укључени или искључени +Name[sr@ijekavian]=Одскачући тастери су укључени или искључени +Name[sr@ijekavianlatin]=Odskačući tasteri su uključeni ili isključeni +Name[sr@latin]=Odskačući tasteri su uključeni ili isključeni +Name[sv]=Studsande tangenter har aktiverats eller inaktiverats +Name[ta]=Bounce keys has been enabled or disabled +Name[te]=బౌన్‍స్ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Name[tg]=Тугмаҳои ҷаҳиш фаъол ё ғайрифаъол шудаанд +Name[th]=การกดปุ่มพิมพ์รัวได้ถูกเปิดหรือปิดการทำงาน +Name[tr]=Sıçrama tuşları etkinleştirildi ya da pasifleştirildi +Name[ug]=سەكرىمە(Bounce) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Name[uk]=Було активовано або деактивовано пружні клавіші +Name[vi]=Phím nảy đã được bật hoặc tắt +Name[wa]=Des dimey-eclawåvès tapes ont stî metowe en alaedje oudonbén dismetowes +Name[x-test]=xxBounce keys has been enabled or disabledxx +Name[zh_CN]=启用或禁用了撞击键 +Name[zh_TW]=彈回鍵已改變狀態 +Comment=Bounce keys has been enabled or disabled +Comment[af]=Hop sleutels was geaktiveer of gedeaktiveer +Comment[ar]=تم تفعيل أو إلغاء تفعيل المفاتيح المكررة +Comment[ast]=Les tecles de rebote activáronse o desactiváronse +Comment[bg]="Подскачащи" клавиши са включени или изключени +Comment[bs]=Odskačući tasteri su uključeni ili isključeni +Comment[ca]=Les tecles de repetició s'han activat o desactivat +Comment[ca@valencia]=Les tecles de repetició s'han activat o desactivat +Comment[cs]=Zdvojené klávesy byly povoleny nebo zakázány +Comment[csb]=Òdbijającé klawisze òstałë aktiwòwòné abò deaktiwòwóné +Comment[da]=Elastiske taster er aktiveret eller deaktiveret +Comment[de]=Die Funktion „Zurückschnellende Tasten“ ist aktiviert oder deaktiviert worden +Comment[el]=Τα πλήκτρα αναπήδησης ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Comment[en_GB]=Bounce keys has been enabled or disabled +Comment[eo]=Prokrastaj klavoj validiĝis aŭ malvalidiĝis +Comment[es]=Se ha activado o desactivado el rebote de teclas +Comment[et]=Põrkavad klahvid on keelatud või lubatud +Comment[eu]=Errebote-teklak gaitu edo desgaitu dira +Comment[fi]=Kimpoavat näppäimet on otettu käyttöön tai poistettu käytöstä +Comment[fr]=Les touches non répétées ont été activées ou désactivées +Comment[fy]=Stuiterjende toetsen binne aktivearre of ûntaktivearre +Comment[ga]=Cumasaíodh nó díchumasaíodh eochracha preabtha +Comment[gl]=A anulación temporal de teclas foi ou activada ou desactivada +Comment[gu]=ઉછાળ કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Comment[he]=מניעת לחיצה על מספר מקשים בו־זמנית הופעלה או הופסקה +Comment[hi]=बाउंस कुंजियों को सक्षम या अक्षम किया गया है +Comment[hne]=बाउन्स कुंजी सक्छम या अक्छम हो गे हे +Comment[hr]=Obijanje tipki je omogućeno ili onemogućeno +Comment[hsb]=Zamkane tasty zaswěćene abo hasnjene +Comment[hu]=A visszaugró billentyűk használatát ki- vagy bekapcsolták +Comment[ia]=Le claves elastic ha essite activate o disactivate +Comment[id]=Tombol pantul telah diaktifkan atau dinonaktifkan +Comment[is]=Slökkt eða kveikt hefur verið á skoppandi lyklum +Comment[it]=I tasti ravvicinati sono stati abilitati o disattivati +Comment[ja]=バウンスキーが有効または無効になりました +Comment[kk]=Қалшылға қарсы пернелер рұқсат етілді/етілмеді +Comment[km]=គ្រាប់​ចុច​លោត​​ត្រូវបាន​​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Comment[kn]=ಪುಟಿಯುವ (ಬೌನ್ಸ್) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Comment[ko]=튕김 키가 활성화되거나 비활성화됨 +Comment[ku]=Bişkojkên Zindî hatin çalakirin an jî neçalakirin +Comment[lt]=Tamprūs klavišai buvo įgalinti arba išjungti +Comment[lv]=Atlecošie taustiņi tika ieslēgti vai izslēgti +Comment[mk]=„Скокачки копчиња“ е овозможено или оневозможено +Comment[ml]=ബൌണ്‍സ് കീ പ്രവര്‍ത്തന സജ്ജം അല്ലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതം ആക്കിയിരിക്കുന്നു +Comment[mr]=बाउन्स् कि कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Comment[nb]=Filtertaster er blitt slått på eller av +Comment[nds]="Jumpen Tasten" wöör an- oder utmaakt +Comment[ne]=बाउन्स कुञ्जीहरू सक्षम वा अक्षम बनाइएको छ +Comment[nl]=Stuiterende toetsen is geactiveerd of gedeactiveerd +Comment[nn]=Filtertastar er no slått på eller av +Comment[or]=Bounce କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Comment[pa]=ਬਾਊਂਸ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Comment[pl]=Powolne klawisze zostały włączone lub wyłączone +Comment[pt]=As teclas sonoras foram activadas ou desactivadas +Comment[pt_BR]=As teclas de repercussão foram ativadas ou desativadas +Comment[ro]=Taste săltărețe (Bounce keys) au fost activate sau dezactivate +Comment[ru]=Включены или отключены «прыгающие» клавиши +Comment[se]=Sillenboallu lea rievdadan stáhtusa +Comment[si]=පොළාපැනුම් යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Comment[sk]=Bounce klávesy zmenili svoj stav +Comment[sl]=Odbijajoče tipke so bile omogočene ali onemogočene +Comment[sr]=Одскачући тастери су укључени или искључени +Comment[sr@ijekavian]=Одскачући тастери су укључени или искључени +Comment[sr@ijekavianlatin]=Odskačući tasteri su uključeni ili isključeni +Comment[sr@latin]=Odskačući tasteri su uključeni ili isključeni +Comment[sv]=Studsande tangenter har aktiverats eller inaktiverats +Comment[ta]=Bounce keys has been enabled or disabled +Comment[te]=బౌన్‍స్ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Comment[tg]=Тугмаҳои ҷаҳиш фаъол ё ғайрифаъол шудаанд +Comment[th]=การกดปุ่มพิมพ์รัวได้ถูกเปิดหรือปิดการทำงาน +Comment[tr]=Sıçrama tuşları etkinleştirildi ya da pasifleştirildi +Comment[ug]=سەكرىمە(Bounce) كۇنۇپكىلار ئىناۋەتلىك ياكى چەكلەنگەن +Comment[uk]=Було активовано або деактивовано пружні клавіші +Comment[vi]=Phím nảy đã được bật hoặc tắt +Comment[wa]=Des dimey-eclawåvès tapes ont stî metowes en alaejde oudonbén essoctêyes +Comment[x-test]=xxBounce keys has been enabled or disabledxx +Comment[zh_CN]=启用或禁用了撞击键 +Comment[zh_TW]=彈回鍵已啟動或關閉。 +Sound=KDE-Sys-Special.ogg +Action=PassivePopup +nopresentation=236 + +[Event/mousekeys] +Name=Mouse keys has been enabled or disabled +Name[af]=Muis sleutels was geaktiveer of gedeaktiveer +Name[ar]=تم تفعيل أو إلغاء تفعيل فأرة لوحة المفاتيح +Name[ast]=Tecles de mur activáronse o desactiváronse +Name[be@latin]=Klavišy kiravańnia kursoram źmianili svajo stanovišča +Name[bg]=Клавишите за управление на курсора са включени или изключени +Name[bs]=Miš‑tasteri su uključeni ili isključeni +Name[ca]=Les tecles del ratolí s'han activat o desactivat +Name[ca@valencia]=Les tecles del ratolí s'han activat o desactivat +Name[cs]=Klávesy myší byly povoleny nebo zakázány +Name[csb]=Knąpë mëszë òstałë aktiwòwòné abò deaktiwòwóné +Name[da]=Musetaster er aktiveret eller deaktiveret +Name[de]=Maustasten sind aktiviert oder deaktiviert worden +Name[el]=Τα πλήκτρα ποντικιού ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Name[en_GB]=Mouse keys has been enabled or disabled +Name[eo]=Musklavoj validiĝis aŭ malvalidiĝis +Name[es]=Se han activado o desactivado los botones del ratón +Name[et]=Hiire klahvid on keelatud või lubatud +Name[eu]=Saguaren botoiak gaitu edo desgaitu dira +Name[fi]=Hiiren näppäimet on otettu käyttöön tai poistettu käytöstä +Name[fr]=Les touches gérant la souris ont été activées ou désactivées +Name[fy]=Mûstoetsen binne aktivearre of ûntaktivearre +Name[ga]=Cumasaíodh nó díchumasaíodh eochracha luiche +Name[gl]=O control do rato co teclado activouse ou desactivouse +Name[gu]=માઉસ કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Name[he]=חיקוי כפתורי העכבר הופעל או הופסק +Name[hi]=माउस कुंजियों को सक्षम या अक्षम किया गया है +Name[hne]=मुसुवा कुंजी सच्छम या अच्छम हो गिस +Name[hr]=Mišolike tipke su omogućene ili onemogućene +Name[hsb]=Myšace tasty zaswěćene abo hasnjene +Name[hu]=Az egérmozgató billentyűk átkapcsolva +Name[ia]=Le claves de mus ha essite activate o disactivate +Name[id]=Tombol tetikus telah diaktifkan atau dinonaktifkan +Name[is]=Slökkt eða kveikt hefur verið á músarlyklum +Name[it]=Il mouse da tastiera è stato attivato o disattivato +Name[ja]=マウスキーが有効または無効になりました +Name[kk]=Тышқан пернелер рұқсат етілді/етілмеді +Name[km]=គ្រាប់​ចុច​កណ្ដុរ​ត្រូវ​បាន​​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Name[kn]=ಮೂಷಕ (ಮೌಸ್) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Name[ko]=마우스 키가 활성화되거나 비활성화됨 +Name[ku]=Bişkojkên Mişkî hatin çalakirin an jî neçalakirin +Name[lt]=Pelės klavišai buvo įgalinti arba išjungti +Name[lv]=Peles taustiņi tika ieslēgti vai izslēgti +Name[mk]=„Копчиња на глушецот“ е овозможено или оневозможено +Name[ml]=മൌസ് കീ പ്രവര്‍ത്തന സജ്ജം അല്ലെങ്കില്‍ പ്രവര്‍ത്തന രഹിതം ആക്കിയിരിക്കുന്നു +Name[mr]=माऊस कि कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Name[nb]=Musetaster er blitt slått på eller av +Name[nds]="Muustasten" wöör an- oder utmaakt +Name[ne]=माउस कुञ्जी सक्षम वा अक्षम बानइएको छ +Name[nl]=Muistoetsen is geactiveerd of gedeactiveerd +Name[nn]=Musetastar er no slått på eller av +Name[or]=ମାଉସ କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Name[pa]=ਮਾਊਸ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Name[pl]=Klawisze myszy zostały włączone lub wyłączone +Name[pt]=As teclas do rato foram activadas ou desactivadas +Name[pt_BR]=Teclas de mouse foram habilitadas ou desabilitadas +Name[ro]=Taste de maus au fost activate sau dezactivate +Name[ru]=Нажаты или отжаты клавиши мыши +Name[se]=Sáhppánboalut leat rievdadan stáhtusa +Name[si]=මවුස යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Name[sk]=Klávesy myši zmenili svoj stav +Name[sl]=Miškine tipke so bile omogočene ali onemogočene +Name[sr]=Миш‑тастери су укључени или искључени +Name[sr@ijekavian]=Миш‑тастери су укључени или искључени +Name[sr@ijekavianlatin]=Miš‑tasteri su uključeni ili isključeni +Name[sr@latin]=Miš‑tasteri su uključeni ili isključeni +Name[sv]=Mustangenter har aktiverats eller inaktiverats +Name[ta]=Mouse keys has been enabled or disabled +Name[te]=మౌస్ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Name[tg]=Тугмаҳои муш фаъол ё ғайрифаъол шудаанд +Name[th]=การจำลองเมาส์ด้วยแป้นพิมพ์ได้ถูกเปิดหรือปิดการทำงาน +Name[tr]=Fare tuşları etkinleştirildi ya da pasifleştirildi +Name[ug]=چاشقىنەك كۇنۇپكىلىرى ئىناۋەتلىك ياكى چەكلەنگەن +Name[uk]=Було активовано або деактивовано клавіші мишки +Name[wa]=Des tapes di soris ont stî metowe en alaedje oudonbén essoctêyes +Name[x-test]=xxMouse keys has been enabled or disabledxx +Name[zh_CN]=启用或禁用了鼠标键 +Name[zh_TW]=滑鼠鍵已改變狀態 +Comment=Mouse keys has been enabled or disabled +Comment[af]=Muis sleutels was geaktiveer of gedeaktiveer +Comment[ar]=تم تفعيل أو إلغاء تفعيل فأرة لوحة المفاتيح +Comment[ast]=Tecles de mur activáronse o desactiváronse +Comment[be@latin]=Klavišy kiravańnia kursoram źmianili svajo stanovišča +Comment[bg]=Клавишите за управление на курсора са включени или изключени +Comment[bs]=Miš‑tasteri su uključeni ili isključeni +Comment[ca]=Les tecles del ratolí s'han activat o desactivat +Comment[ca@valencia]=Les tecles del ratolí s'han activat o desactivat +Comment[cs]=Klávesy myší byly povoleny nebo zakázány +Comment[csb]=Knąpë mëszë òstałë aktiwòwòné abò deaktiwòwóné +Comment[da]=Musetaster er aktiveret eller deaktiveret +Comment[de]=Die Funktion „Maustasten“ ist aktiviert oder deaktiviert worden +Comment[el]=Τα πλήκτρα ποντικιού ενεργοποιήθηκαν ή απενεργοποιήθηκαν +Comment[en_GB]=Mouse keys has been enabled or disabled +Comment[eo]=Musklavoj validiĝis aŭ malvalidiĝis +Comment[es]=Se han activado o desactivado los botones del ratón +Comment[et]=Hiire klahvid on keelatud või lubatud +Comment[eu]=Saguaren botoiak gaitu edo desgaitu dira +Comment[fi]=Hiiren näppäimet on otettu käyttöön tai poistettu käytöstä +Comment[fr]=Les touches gérant la souris ont été activées ou désactivées +Comment[fy]=Mûstoetsen binne aktivearre of ûntaktivearre +Comment[ga]=Cumasaíodh nó díchumasaíodh eochracha luiche +Comment[gl]=O control do rato co teclado foi ou activado ou desactivado +Comment[gu]=માઉસ કળો સક્રિય અથવા નિષ્ક્રિય કરવામાં આવી છે +Comment[he]=חיקוי כפתורי העכבר הופעל או הופסק +Comment[hi]=माउस कुंजियों को सक्षम या अक्षम किया गया है +Comment[hne]=मुसुवा कुंजी सच्छम या अच्छम हो गिस +Comment[hr]=Mišolike tipke su omogućene ili onemogućene +Comment[hsb]=Myšace tasty zaswěćene abo hasnjene +Comment[hu]=Az egérmozgató billentyűk használatát ki- vagy bekapcsolták +Comment[ia]=Le claves de mus ha essite activate o disactivate +Comment[id]=Tombol tetikus telah diaktifkan atau dinonaktifkan +Comment[is]=Slökkt eða kveikt hefur verið á músarlyklum +Comment[it]=Il mouse da tastiera è stato attivato o disattivato +Comment[ja]=マウスキーが有効または無効になりました +Comment[kk]=Тышқан пернелер рұқсат етілді/етілмеді +Comment[km]=គ្រាប់​ចុច​កណ្ដុរ​ត្រូវ​បាន​​​អនុញ្ញាត ឬ​មិន​អនុញ្ញាត +Comment[kn]=ಮೂಷಕ (ಮೌಸ್) ಕೀಲಿಯನ್ನು ಸಕ್ರಿಯ ಅಥವಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ +Comment[ko]=마우스 키가 활성화되거나 비활성화됨 +Comment[ku]=Bişkojkên Mişkî hatin çalakirin an jî neçalakirin +Comment[lt]=Pelės mygtukai buvo įgalinti arba išjungti +Comment[lv]=Peles taustiņi tika ieslēgti vai izslēgti +Comment[mk]=„Копчиња на глушецот“ е овозможено или оневозможено +Comment[ml]=മൌസ് കീ പ്രവര്‍ത്തന സജ്ജം അല്ല്ലെങ്കില്‍ പ്രവര്‍ത്തനരഹിതം ആക്കിയിരിക്കുന്നു +Comment[mr]=माऊस कि कार्यान्वित किंवा अकार्यान्वित केली गेली आहे +Comment[nb]=Musetaster er blitt slått på eller av +Comment[nds]="Muustasten" wöör an- oder utmaakt +Comment[ne]=माउस कुञ्जी सक्षम वा अक्षम बनाइएको छ +Comment[nl]=Muistoetsen is geactiveerd of gedeactiveerd +Comment[nn]=Musetastar er no slått på eller av +Comment[or]=ମାଉସ କିଗୁଡ଼ିକୁ ସକ୍ରିୟ ଅଥବା ନିଷ୍କ୍ରିୟ କରାଯାଇଛି +Comment[pa]=ਮਾਊਸ ਸਵਿੱਚਾਂ ਯੋਗ ਜਾਂ ਆਯੋਗ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ +Comment[pl]=Klawisze myszy zostały włączone lub wyłączone +Comment[pt]=As teclas do rato foram activadas ou desactivadas +Comment[pt_BR]=Teclas de mouse foram habilitadas ou desabilitadas +Comment[ro]=Taste de maus au fost activate sau dezactivate +Comment[ru]=Нажаты или отжаты клавиши мыши +Comment[se]=Sáhppánboalut leat rievdadan stáhtusa +Comment[si]=මවුස යතුරු සක්‍රීය හෝ අක්‍රීය කර ඇත +Comment[sk]=Klávesy myši zmenili svoj stav +Comment[sl]=Miškine tipke so bile omogočene ali onemogočene +Comment[sr]=Миш‑тастери су укључени или искључени +Comment[sr@ijekavian]=Миш‑тастери су укључени или искључени +Comment[sr@ijekavianlatin]=Miš‑tasteri su uključeni ili isključeni +Comment[sr@latin]=Miš‑tasteri su uključeni ili isključeni +Comment[sv]=Mustangenter har aktiverats eller inaktiverats +Comment[ta]=Mouse keys has been enabled or disabled +Comment[te]=మౌస్ కీలు చేతనంకాబడినవి లేదా అచేతనంకాబడినవి +Comment[tg]=Тугмаҳои муш фаъол ё ғайрифаъол шудаанд +Comment[th]=การจำลองเมาส์ด้วยแป้นพิมพ์ได้ถูกเปิดหรือปิดการทำงาน +Comment[tr]=Fare tuşları etkinleştirildi ya da pasifleştirildi +Comment[ug]=چاشقىنەك كۇنۇپكىلىرى ئىناۋەتلىك ياكى چەكلەنگەن +Comment[uk]=Було активовано або деактивовано клавіші мишки +Comment[wa]=Des tapes di soris ont stî metowe en alaedje oudonbén essoctêyes +Comment[x-test]=xxMouse keys has been enabled or disabledxx +Comment[zh_CN]=启用或禁用了鼠标键 +Comment[zh_TW]=滑鼠鍵已啟動或關閉。 +Sound=KDE-Sys-Special.ogg +Action=PassivePopup +nopresentation=236 diff --git a/kcms/access/kcmaccess.cpp b/kcms/access/kcmaccess.cpp new file mode 100644 index 0000000000..2ffafe7d5a --- /dev/null +++ b/kcms/access/kcmaccess.cpp @@ -0,0 +1,786 @@ +/** + * kcmaccess.cpp + * + * Copyright (c) 2000 Matthias H�zer-Klpfel + * + */ + +#include "kcmaccess.h" + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + +//Added by qt3to4: +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include +#include + +K_PLUGIN_FACTORY(KAccessConfigFactory, registerPlugin();) +K_EXPORT_PLUGIN(KAccessConfigFactory("kcmaccess")) + +QString mouseKeysShortcut (Display *display) { + // Calculate the keycode + KeySym sym = XK_MouseKeys_Enable; + KeyCode code = XKeysymToKeycode(display, sym); + if (code == 0) { + sym = XK_Pointer_EnableKeys; + code = XKeysymToKeycode(display, sym); + if (code == 0) + return ""; // No shortcut available? + } + + // Calculate the modifiers by searching the keysym in the X keyboard mapping + XkbDescPtr xkbdesc = XkbGetMap(display, XkbKeyTypesMask | XkbKeySymsMask, XkbUseCoreKbd); + if (!xkbdesc) + return ""; // Failed to obtain the mapping from server + + bool found = false; + unsigned char modifiers = 0; + int groups = XkbKeyNumGroups(xkbdesc, code); + for (int grp = 0; grp < groups && !found; grp++) + { + int levels = XkbKeyGroupWidth(xkbdesc, code, grp); + for (int level = 0; level < levels && !found; level++) + { + if (sym == XkbKeySymEntry(xkbdesc, code, level, grp)) + { + // keysym found => determine modifiers + int typeIdx = xkbdesc->map->key_sym_map[code].kt_index[grp]; + XkbKeyTypePtr type = &(xkbdesc->map->types[typeIdx]); + for (int i = 0; i < type->map_count && !found; i++) + { + if (type->map[i].active && (type->map[i].level == level)) + { + modifiers = type->map[i].mods.mask; + found = true; + } + } + } + } + } + XkbFreeClientMap (xkbdesc, 0, true); + + if (!found) + return ""; // Somehow the keycode -> keysym mapping is flawed + + XEvent ev; + ev.type = KeyPress; + ev.xkey.display = display; + ev.xkey.keycode = code; + ev.xkey.state = 0; + int key; + KKeyServer::xEventToQt(&ev, &key); + QString keyname = QKeySequence(key).toString(); + + unsigned int AltMask = KKeyServer::modXAlt(); + unsigned int WinMask = KKeyServer::modXMeta(); + unsigned int NumMask = KKeyServer::modXNumLock(); + unsigned int ScrollMask= KKeyServer::modXScrollLock(); + + unsigned int MetaMask = XkbKeysymToModifiers (display, XK_Meta_L); + unsigned int SuperMask = XkbKeysymToModifiers (display, XK_Super_L); + unsigned int HyperMask = XkbKeysymToModifiers (display, XK_Hyper_L); + unsigned int AltGrMask = XkbKeysymToModifiers (display, XK_Mode_switch) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Shift) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Latch) + | XkbKeysymToModifiers (display, XK_ISO_Level3_Lock); + + unsigned int mods = ShiftMask | ControlMask | AltMask | WinMask + | LockMask | NumMask | ScrollMask; + + AltGrMask &= ~mods; + MetaMask &= ~(mods | AltGrMask); + SuperMask &= ~(mods | AltGrMask | MetaMask); + HyperMask &= ~(mods | AltGrMask | MetaMask | SuperMask); + + if ((modifiers & AltGrMask) != 0) + keyname = i18n("AltGraph") + '+' + keyname; + if ((modifiers & HyperMask) != 0) + keyname = i18n("Hyper") + '+' + keyname; + if ((modifiers & SuperMask) != 0) + keyname = i18n("Super") + '+' + keyname; + if ((modifiers & WinMask) != 0) + keyname = QKeySequence(Qt::META).toString() + '+' + keyname; + if ((modifiers & AltMask) != 0) + keyname = QKeySequence(Qt::ALT).toString() + '+' + keyname; + if ((modifiers & ControlMask) != 0) + keyname = QKeySequence(Qt::CTRL).toString() + '+' + keyname; + if ((modifiers & ShiftMask) != 0) + keyname = QKeySequence(Qt::SHIFT).toString() + '+' + keyname; + + QString result; + if ((modifiers & ScrollMask) != 0) + if ((modifiers & LockMask) != 0) + if ((modifiers & NumMask) != 0) + result = i18n("Press %1 while NumLock, CapsLock and ScrollLock are active", keyname); + else + result = i18n("Press %1 while CapsLock and ScrollLock are active", keyname); + else if ((modifiers & NumMask) != 0) + result = i18n("Press %1 while NumLock and ScrollLock are active", keyname); + else + result = i18n("Press %1 while ScrollLock is active", keyname); + else if ((modifiers & LockMask) != 0) + if ((modifiers & NumMask) != 0) + result = i18n("Press %1 while NumLock and CapsLock are active", keyname); + else + result = i18n("Press %1 while CapsLock is active", keyname); + else if ((modifiers & NumMask) != 0) + result = i18n("Press %1 while NumLock is active", keyname); + else + result = i18n("Press %1", keyname); + + return result; +} + +KAccessConfig::KAccessConfig(QWidget *parent, const QVariantList& args) + : KCModule(parent, args) +{ + + KAboutData *about = + new KAboutData(I18N_NOOP("kaccess"), QString(), i18n("KDE Accessibility Tool"), + QString(), QString(), KAboutData::License_GPL, + i18n("(c) 2000, Matthias Hoelzer-Kluepfel")); + + about->addAuthor(i18n("Matthias Hoelzer-Kluepfel"), i18n("Author") , QStringLiteral("hoelzer@kde.org")); + + setAboutData( about ); + + QVBoxLayout *main = new QVBoxLayout(this); + main->setMargin(0); + QTabWidget *tab = new QTabWidget(this); + main->addWidget(tab); + + // bell settings --------------------------------------- + QWidget *bell = new QWidget(this); + + QVBoxLayout *vbox = new QVBoxLayout(bell); + + QGroupBox *grp = new QGroupBox(i18n("Audible Bell"), bell); + QHBoxLayout *layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + QVBoxLayout *vvbox = new QVBoxLayout(); + layout->addLayout( vvbox ); + + systemBell = new QCheckBox(i18n("Use &system bell"), grp); + vvbox->addWidget(systemBell); + customBell = new QCheckBox(i18n("Us&e customized bell"), grp); + vvbox->addWidget(customBell); + systemBell->setWhatsThis( i18n("If this option is checked, the default system bell will be used. See the" + " \"System Bell\" control module for how to customize the system bell." + " Normally, this is just a \"beep\".") ); + customBell->setWhatsThis( i18n("

    Check this option if you want to use a customized bell, playing" + " a sound file. If you do this, you will probably want to turn off the system bell.

    Please note" + " that on slow machines this may cause a \"lag\" between the event causing the bell and the sound being played.

    ") ); + + QHBoxLayout *hbox = new QHBoxLayout(); + vvbox->addLayout( hbox ); + hbox->addSpacing(24); + soundEdit = new QLineEdit(grp); + soundLabel = new QLabel(i18n("Sound &to play:"), grp); + soundLabel->setBuddy(soundEdit); + hbox->addWidget(soundLabel); + hbox->addWidget(soundEdit); + soundButton = new QPushButton(i18n("Browse..."), grp); + hbox->addWidget(soundButton); + QString wtstr = i18n("If the option \"Use customized bell\" is enabled, you can choose a sound file here." + " Click \"Browse...\" to choose a sound file using the file dialog."); + soundEdit->setWhatsThis( wtstr ); + soundLabel->setWhatsThis( wtstr ); + soundButton->setWhatsThis( wtstr ); + + connect(soundButton, SIGNAL(clicked()), this, SLOT(selectSound())); + + connect(customBell, SIGNAL(clicked()), this, SLOT(checkAccess())); + + connect(systemBell, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(customBell, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(soundEdit, SIGNAL(textChanged(QString)), this, SLOT(configChanged())); + + // ----------------------------------------------------- + + // visible bell ---------------------------------------- + grp = new QGroupBox(i18n("Visible Bell"), bell); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout( vvbox ); + + visibleBell = new QCheckBox(i18n("&Use visible bell"), grp); + vvbox->addWidget(visibleBell); + visibleBell->setWhatsThis( i18n("This option will turn on the \"visible bell\", i.e. a visible" + " notification shown every time that normally just a bell would occur. This is especially useful" + " for deaf people.") ); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + invertScreen = new QRadioButton(i18n("I&nvert screen"), grp); + hbox->addWidget(invertScreen); + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + invertScreen->setWhatsThis( i18n("All screen colors will be inverted for the amount of time specified below.") ); + hbox->addSpacing(24); + flashScreen = new QRadioButton(i18n("F&lash screen"), grp); + hbox->addWidget(flashScreen); + flashScreen->setWhatsThis( i18n("The screen will turn to a custom color for the amount of time specified below.") ); + hbox->addSpacing(12); + colorButton = new KColorButton(grp); + colorButton->setFixedWidth(colorButton->sizeHint().height()*2); + hbox->addWidget(colorButton); + hbox->addStretch(); + colorButton->setWhatsThis( i18n("Click here to choose the color used for the \"flash screen\" visible bell.") ); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + + durationSlider = new KDoubleNumInput(grp); + durationSlider->setRange(100, 2000, 100); + durationSlider->setExponentRatio(2); + durationSlider->setDecimals(0); + durationSlider->setLabel(i18n("Duration:")); + durationSlider->setSuffix(i18n(" msec")); + hbox->addWidget(durationSlider); + durationSlider->setWhatsThis( i18n("Here you can customize the duration of the \"visible bell\" effect being shown.") ); + + connect(invertScreen, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(flashScreen, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(visibleBell, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(visibleBell, SIGNAL(clicked()), this, SLOT(checkAccess())); + connect(colorButton, SIGNAL(clicked()), this, SLOT(changeFlashScreenColor())); + + connect(invertScreen, SIGNAL(clicked()), this, SLOT(invertClicked())); + connect(flashScreen, SIGNAL(clicked()), this, SLOT(flashClicked())); + + connect(durationSlider, SIGNAL(valueChanged(double)), this, SLOT(configChanged())); + + vbox->addStretch(); + + // ----------------------------------------------------- + + tab->addTab(bell, i18n("&Bell")); + + + // modifier key settings ------------------------------- + QWidget *modifiers = new QWidget(this); + + vbox = new QVBoxLayout(modifiers); + + grp = new QGroupBox(i18n("S&ticky Keys"), modifiers); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout(vvbox); + + stickyKeys = new QCheckBox(i18n("Use &sticky keys"), grp); + vvbox->addWidget(stickyKeys); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + stickyKeysLock = new QCheckBox(i18n("&Lock sticky keys"), grp); + hbox->addWidget(stickyKeysLock); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + stickyKeysAutoOff = new QCheckBox(i18n("Turn sticky keys off when two keys are pressed simultaneously"), grp); + hbox->addWidget(stickyKeysAutoOff); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + stickyKeysBeep = new QCheckBox(i18n("Use system bell whenever a modifier gets latched, locked or unlocked"), grp); + hbox->addWidget(stickyKeysBeep); + + grp = new QGroupBox(i18n("Locking Keys"), modifiers); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout(vvbox); + + toggleKeysBeep = new QCheckBox(i18n("Use system bell whenever a locking key gets activated or deactivated"), grp); + vvbox->addWidget(toggleKeysBeep); + + kNotifyModifiers = new QCheckBox(i18n("Use KDE's system notification mechanism whenever a modifier or locking key changes its state"), grp); + vvbox->addWidget(kNotifyModifiers); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addStretch(1); + kNotifyModifiersButton = new QPushButton(i18n("Configure &Notifications..."), grp); + kNotifyModifiersButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + hbox->addWidget(kNotifyModifiersButton); + + connect(stickyKeys, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(stickyKeysLock, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(stickyKeysAutoOff, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(stickyKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); + + connect(stickyKeysBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(toggleKeysBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(kNotifyModifiers, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(kNotifyModifiers, SIGNAL(clicked()), this, SLOT(checkAccess())); + connect(kNotifyModifiersButton, SIGNAL(clicked()), this, SLOT(configureKNotify())); + + vbox->addStretch(); + + tab->addTab(modifiers, i18n("&Modifier Keys")); + + // key filter settings --------------------------------- + QWidget *filters = new QWidget(this); + + vbox = new QVBoxLayout(filters); + grp = new QGroupBox(i18n("Slo&w Keys"), filters); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout(vvbox); + + slowKeys = new QCheckBox(i18n("&Use slow keys"), grp); + vvbox->addWidget(slowKeys); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + slowKeysDelay = new KDoubleNumInput(grp); + slowKeysDelay->setRange(50, 10000, 100); + slowKeysDelay->setExponentRatio(2); + slowKeysDelay->setDecimals(0); + slowKeysDelay->setSuffix(i18n(" msec")); + slowKeysDelay->setLabel(i18n("Acceptance dela&y:"), Qt::AlignVCenter|Qt::AlignLeft); + hbox->addWidget(slowKeysDelay); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + slowKeysPressBeep = new QCheckBox(i18n("&Use system bell whenever a key is pressed"), grp); + hbox->addWidget(slowKeysPressBeep); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + slowKeysAcceptBeep = new QCheckBox(i18n("&Use system bell whenever a key is accepted"), grp); + hbox->addWidget(slowKeysAcceptBeep); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + slowKeysRejectBeep = new QCheckBox(i18n("&Use system bell whenever a key is rejected"), grp); + hbox->addWidget(slowKeysRejectBeep); + + grp = new QGroupBox(i18n("Bounce Keys"), filters); + layout= new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout( vvbox ); + + bounceKeys = new QCheckBox(i18n("Use bou&nce keys"), grp); + vvbox->addWidget(bounceKeys); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + bounceKeysDelay = new KDoubleNumInput(grp); + bounceKeysDelay->setRange(100, 5000, 100); + bounceKeysDelay->setExponentRatio(2); + bounceKeysDelay->setDecimals(0); + bounceKeysDelay->setSuffix(i18n(" msec")); + bounceKeysDelay->setLabel(i18n("D&ebounce time:"), Qt::AlignVCenter|Qt::AlignLeft);; + hbox->addWidget(bounceKeysDelay); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + bounceKeysRejectBeep = new QCheckBox(i18n("Use the system bell whenever a key is rejected"), grp); + hbox->addWidget(bounceKeysRejectBeep); + + connect(slowKeysDelay, SIGNAL(valueChanged(double)), this, SLOT(configChanged())); + connect(slowKeys, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(slowKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); + + connect(slowKeysPressBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(slowKeysAcceptBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(slowKeysRejectBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + + connect(bounceKeysDelay, SIGNAL(valueChanged(double)), this, SLOT(configChanged())); + connect(bounceKeys, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(bounceKeysRejectBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(bounceKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); + + vbox->addStretch(); + + tab->addTab(filters, i18n("&Keyboard Filters")); + + // gestures -------------------------------------------- + QWidget *features = new QWidget(this); + + vbox = new QVBoxLayout(features); + + grp = new QGroupBox(i18n("Activation Gestures"), features); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout( vvbox ); + + gestures = new QCheckBox(i18n("Use gestures for activating sticky keys and slow keys"), grp); + vvbox->addWidget(gestures); + QString shortcut = mouseKeysShortcut(QX11Info::display()); + if (shortcut.isEmpty()) + gestures->setWhatsThis( i18n("Here you can activate keyboard gestures that turn on the following features: \n" + "Sticky keys: Press Shift key 5 consecutive times\n" + "Slow keys: Hold down Shift for 8 seconds")); + else + gestures->setWhatsThis( i18n("Here you can activate keyboard gestures that turn on the following features: \n" + "Mouse Keys: %1\n" + "Sticky keys: Press Shift key 5 consecutive times\n" + "Slow keys: Hold down Shift for 8 seconds", shortcut)); + + timeout = new QCheckBox(i18n("Turn sticky keys and slow keys off after a certain period of inactivity."), grp); + vvbox->addWidget(timeout); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addSpacing(24); + timeoutDelay = new KIntNumInput(grp); + timeoutDelay->setSuffix(i18n(" min")); + timeoutDelay->setRange(1, 30, 4); + timeoutDelay->setLabel(i18n("Timeout:"), Qt::AlignVCenter|Qt::AlignLeft);; + hbox->addWidget(timeoutDelay); + + grp = new QGroupBox(i18n("Notification"), features); + layout = new QHBoxLayout; + grp->setLayout(layout); + vbox->addWidget(grp); + + vvbox = new QVBoxLayout(); + layout->addLayout(vvbox); + + accessxBeep = new QCheckBox(i18n("Use the system bell whenever a gesture is used to turn an accessibility feature on or off"), grp); + vvbox->addWidget(accessxBeep); + + gestureConfirmation = new QCheckBox(i18n("Show a confirmation dialog whenever a keyboard accessibility feature is turned on or off"), grp); + vvbox->addWidget(gestureConfirmation); + gestureConfirmation->setWhatsThis( i18n("If this option is checked, KDE will show a confirmation dialog whenever a keyboard accessibility feature is turned on or off.\nEnsure you know what you are doing if you uncheck it, as the keyboard accessibility settings will then always be applied without confirmation.") ); + + kNotifyAccessX = new QCheckBox(i18n("Use KDE's system notification mechanism whenever a keyboard accessibility feature is turned on or off"), grp); + vvbox->addWidget(kNotifyAccessX); + + hbox = new QHBoxLayout(); + vvbox->addLayout(hbox); + hbox->addStretch(1); + kNotifyAccessXButton = new QPushButton(i18n("Configure &Notifications..."), grp); + kNotifyAccessXButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + hbox->addWidget(kNotifyAccessXButton); + + connect(gestures, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(timeout, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(timeout, SIGNAL(clicked()), this, SLOT(checkAccess())); + connect(timeoutDelay, SIGNAL(valueChanged(int)), this, SLOT(configChanged())); + connect(accessxBeep, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(gestureConfirmation, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(kNotifyAccessX, SIGNAL(clicked()), this, SLOT(configChanged())); + connect(kNotifyAccessX, SIGNAL(clicked()), this, SLOT(checkAccess())); + connect(kNotifyAccessXButton, SIGNAL(clicked()), this, SLOT(configureKNotify())); + + vbox->addStretch(); + + tab->addTab(features, i18n("Activation Gestures")); +} + + +KAccessConfig::~KAccessConfig() +{ +} + +void KAccessConfig::configureKNotify() +{ + KNotifyConfigWidget::configure (this, "kaccess"); +} + +void KAccessConfig::changeFlashScreenColor() +{ + invertScreen->setChecked(false); + flashScreen->setChecked(true); + configChanged(); +} + +void KAccessConfig::selectSound() +{ + QStringList list = KGlobal::dirs()->findDirs("sound", ""); + QString start; + if (list.count()>0) + start = list[0]; + QString fname = QFileDialog::getOpenFileName(this, QString(), start); + if (!fname.isEmpty()) + soundEdit->setText(fname); +} + + +void KAccessConfig::configChanged() +{ + emit changed(true); +} + + +void KAccessConfig::load() +{ + KConfigGroup cg(KSharedConfig::openConfig("kaccessrc"), "Bell"); + + systemBell->setChecked(cg.readEntry("SystemBell", true)); + customBell->setChecked(cg.readEntry("ArtsBell", false)); + soundEdit->setText(cg.readPathEntry("ArtsBellFile", QString())); + + visibleBell->setChecked(cg.readEntry("VisibleBell", false)); + invertScreen->setChecked(cg.readEntry("VisibleBellInvert", true)); + flashScreen->setChecked(!invertScreen->isChecked()); + colorButton->setColor(cg.readEntry("VisibleBellColor", QColor(Qt::red))); + + durationSlider->setValue(cg.readEntry("VisibleBellPause", 500)); + + KConfigGroup keyboardGroup(KSharedConfig::openConfig("kaccessrc"),"Keyboard"); + + stickyKeys->setChecked(keyboardGroup.readEntry("StickyKeys", false)); + stickyKeysLock->setChecked(keyboardGroup.readEntry("StickyKeysLatch", true)); + stickyKeysAutoOff->setChecked(keyboardGroup.readEntry("StickyKeysAutoOff", false)); + stickyKeysBeep->setChecked(keyboardGroup.readEntry("StickyKeysBeep", true)); + toggleKeysBeep->setChecked(keyboardGroup.readEntry("ToggleKeysBeep", false)); + kNotifyModifiers->setChecked(keyboardGroup.readEntry("kNotifyModifiers", false)); + + slowKeys->setChecked(keyboardGroup.readEntry("SlowKeys", false)); + slowKeysDelay->setValue(keyboardGroup.readEntry("SlowKeysDelay", 500)); + slowKeysPressBeep->setChecked(keyboardGroup.readEntry("SlowKeysPressBeep", true)); + slowKeysAcceptBeep->setChecked(keyboardGroup.readEntry("SlowKeysAcceptBeep", true)); + slowKeysRejectBeep->setChecked(keyboardGroup.readEntry("SlowKeysRejectBeep", true)); + + bounceKeys->setChecked(keyboardGroup.readEntry("BounceKeys", false)); + bounceKeysDelay->setValue(keyboardGroup.readEntry("BounceKeysDelay", 500)); + bounceKeysRejectBeep->setChecked(keyboardGroup.readEntry("BounceKeysRejectBeep", true)); + + gestures->setChecked(keyboardGroup.readEntry("Gestures", false)); + timeout->setChecked(keyboardGroup.readEntry("AccessXTimeout", false)); + timeoutDelay->setValue(keyboardGroup.readEntry("AccessXTimeoutDelay", 30)); + + accessxBeep->setChecked(keyboardGroup.readEntry("AccessXBeep", true)); + gestureConfirmation->setChecked(keyboardGroup.readEntry("GestureConfirmation", false)); + kNotifyAccessX->setChecked(keyboardGroup.readEntry("kNotifyAccessX", false)); + + checkAccess(); + + emit changed(false); +} + + +void KAccessConfig::save() +{ + KConfigGroup cg(KSharedConfig::openConfig("kaccessrc"), "Bell"); + + cg.writeEntry("SystemBell", systemBell->isChecked()); + cg.writeEntry("ArtsBell", customBell->isChecked()); + cg.writePathEntry("ArtsBellFile", soundEdit->text()); + + cg.writeEntry("VisibleBell", visibleBell->isChecked()); + cg.writeEntry("VisibleBellInvert", invertScreen->isChecked()); + cg.writeEntry("VisibleBellColor", colorButton->color()); + + cg.writeEntry("VisibleBellPause", durationSlider->value()); + + KConfigGroup keyboardGroup(KSharedConfig::openConfig("kaccessrc"),"Keyboard"); + + keyboardGroup.writeEntry("StickyKeys", stickyKeys->isChecked()); + keyboardGroup.writeEntry("StickyKeysLatch", stickyKeysLock->isChecked()); + keyboardGroup.writeEntry("StickyKeysAutoOff", stickyKeysAutoOff->isChecked()); + keyboardGroup.writeEntry("StickyKeysBeep", stickyKeysBeep->isChecked()); + keyboardGroup.writeEntry("ToggleKeysBeep", toggleKeysBeep->isChecked()); + keyboardGroup.writeEntry("kNotifyModifiers", kNotifyModifiers->isChecked()); + + keyboardGroup.writeEntry("SlowKeys", slowKeys->isChecked()); + keyboardGroup.writeEntry("SlowKeysDelay", slowKeysDelay->value()); + keyboardGroup.writeEntry("SlowKeysPressBeep", slowKeysPressBeep->isChecked()); + keyboardGroup.writeEntry("SlowKeysAcceptBeep", slowKeysAcceptBeep->isChecked()); + keyboardGroup.writeEntry("SlowKeysRejectBeep", slowKeysRejectBeep->isChecked()); + + + keyboardGroup.writeEntry("BounceKeys", bounceKeys->isChecked()); + keyboardGroup.writeEntry("BounceKeysDelay", bounceKeysDelay->value()); + keyboardGroup.writeEntry("BounceKeysRejectBeep", bounceKeysRejectBeep->isChecked()); + + keyboardGroup.writeEntry("Gestures", gestures->isChecked()); + keyboardGroup.writeEntry("AccessXTimeout", timeout->isChecked()); + keyboardGroup.writeEntry("AccessXTimeoutDelay", timeoutDelay->value()); + + keyboardGroup.writeEntry("AccessXBeep", accessxBeep->isChecked()); + keyboardGroup.writeEntry("GestureConfirmation", gestureConfirmation->isChecked()); + keyboardGroup.writeEntry("kNotifyAccessX", kNotifyAccessX->isChecked()); + + + cg.sync(); + keyboardGroup.sync(); + + if (systemBell->isChecked() || + customBell->isChecked() || + visibleBell->isChecked()) + { + KConfig _cfg("kdeglobals", KConfig::NoGlobals); + KConfigGroup cfg(&_cfg, "General"); + cfg.writeEntry("UseSystemBell", true); + cfg.sync(); + } + + // make kaccess reread the configuration + // turning a11y features off needs to be done by kaccess + // so run it to clear any enabled features and it will exit if it should + KToolInvocation::startServiceByDesktopName("kaccess"); + + emit changed(false); +} + + +void KAccessConfig::defaults() +{ + systemBell->setChecked(true); + customBell->setChecked(false); + soundEdit->setText(""); + + visibleBell->setChecked(false); + invertScreen->setChecked(true); + flashScreen->setChecked(false); + colorButton->setColor(QColor(Qt::red)); + + durationSlider->setValue(500); + + slowKeys->setChecked(false); + slowKeysDelay->setValue(500); + slowKeysPressBeep->setChecked(true); + slowKeysAcceptBeep->setChecked(true); + slowKeysRejectBeep->setChecked(true); + + bounceKeys->setChecked(false); + bounceKeysDelay->setValue(500); + bounceKeysRejectBeep->setChecked(true); + + stickyKeys->setChecked(false); + stickyKeysLock->setChecked(true); + stickyKeysAutoOff->setChecked(false); + stickyKeysBeep->setChecked(true); + toggleKeysBeep->setChecked(false); + kNotifyModifiers->setChecked(false); + + gestures->setChecked(false); + timeout->setChecked(false); + timeoutDelay->setValue(30); + + accessxBeep->setChecked(true); + gestureConfirmation->setChecked(true); + kNotifyAccessX->setChecked(false); + + checkAccess(); + + emit changed(true); +} + + +void KAccessConfig::invertClicked() +{ + flashScreen->setChecked(false); +} + + +void KAccessConfig::flashClicked() +{ + invertScreen->setChecked(false); +} + + +void KAccessConfig::checkAccess() +{ + bool custom = customBell->isChecked(); + soundEdit->setEnabled(custom); + soundButton->setEnabled(custom); + soundLabel->setEnabled(custom); + + bool visible = visibleBell->isChecked(); + invertScreen->setEnabled(visible); + flashScreen->setEnabled(visible); + colorButton->setEnabled(visible); + durationSlider->setEnabled(visible); + + bool sticky = stickyKeys->isChecked(); + stickyKeysLock->setEnabled(sticky); + stickyKeysAutoOff->setEnabled(sticky); + stickyKeysBeep->setEnabled(sticky); + + bool slow = slowKeys->isChecked(); + slowKeysDelay->setEnabled(slow); + slowKeysPressBeep->setEnabled(slow); + slowKeysAcceptBeep->setEnabled(slow); + slowKeysRejectBeep->setEnabled(slow); + + bool bounce = bounceKeys->isChecked(); + bounceKeysDelay->setEnabled(bounce); + bounceKeysRejectBeep->setEnabled(bounce); + + bool useTimeout = timeout->isChecked(); + timeoutDelay->setEnabled(useTimeout); +} + +extern "C" +{ + /* This one gets called by kcminit + + */ + Q_DECL_EXPORT void kcminit_access() + { + KConfig config("kaccessrc", KConfig::NoGlobals); + KToolInvocation::startServiceByDesktopName("kaccess"); + } +} + +#include "kcmaccess.moc" + diff --git a/kcms/access/kcmaccess.desktop b/kcms/access/kcmaccess.desktop new file mode 100644 index 0000000000..e855b8eb7b --- /dev/null +++ b/kcms/access/kcmaccess.desktop @@ -0,0 +1,240 @@ +[Desktop Entry] +Exec=kcmshell5 kcmaccess +Icon=preferences-desktop-accessibility +Type=Service +X-KDE-ServiceTypes=KCModule,KCModuleInit +X-DocPath=kcontrol/kcmaccess/index.html + +X-KDE-Library=kcm_access +X-KDE-Init-Symbol=kcminit_access +X-KDE-ParentApp=kcontrol +X-KDE-OnlyShowOnQtPlatforms=xcb; + +X-KDE-System-Settings-Parent-Category=accessibility + +Name=Accessibility +Name[af]=Toeganklikheid +Name[ar]=الإتاحة +Name[as]=অভিগম্যতা +Name[ast]=Accesibilidá +Name[be]=Даступнасць +Name[be@latin]=Dastupnaść +Name[bg]=Равностоен достъп +Name[bn]=সহায়ক প্রযুক্তি +Name[bn_IN]=বিশেষ ব্যবহারের সহায়তা +Name[br]=Haezadusted +Name[bs]=Pristupačnost +Name[ca]=Accessibilitat +Name[ca@valencia]=Accessibilitat +Name[cs]=Zpřístupnění +Name[csb]=Pòmòce przëstãpù +Name[cy]=Hygyrchedd +Name[da]=Tilgængelighed +Name[de]=Zugangshilfen +Name[el]=Προσιτότητα +Name[en_GB]=Accessibility +Name[eo]=Alirebleco +Name[es]=Accesibilidad +Name[et]=Hõlbustus +Name[eu]=Erabilerraztasuna +Name[fa]=دستیابی‌پذیری +Name[fi]=Esteettömyys +Name[fr]=Accessibilité +Name[fy]=Tagonklikens +Name[ga]=Inrochtaineacht +Name[gl]=Accesibilidade +Name[gu]=ઉપયોગિતા +Name[he]=נגישות +Name[hi]=पहुँच +Name[hne]=पहुंच +Name[hr]=Pristupačnost +Name[hsb]=Přistupnosć +Name[hu]=Kezelési segítség +Name[ia]=Accessibilitate +Name[id]=Aksesibilitas +Name[is]=Aðgengi +Name[it]=Accessibilità +Name[ja]=アクセシビリティ +Name[ka]=სპეციალური შესაძლბლობები +Name[kk]=Арнайы мүмкіндіктер +Name[km]=មធ្យោបាយ​ងាយស្រួល +Name[kn]=ನಿಲುಕಣೆ (ಆಕ್ಸೆಸಿಬಿಲಿಟಿ) +Name[ko]=내게 필요한 설정 +Name[ku]=Gihîştin +Name[lt]=Pritaikymas neįgaliesiems +Name[lv]=Pieejamība +Name[mai]=अभिगम्यता +Name[mk]=Пристапливост +Name[ml]=സാമീപ്യത +Name[mr]=सुलभता +Name[ms]=Kebolehcapaian +Name[nb]=Tilgjengelighet +Name[nds]=Toganghülp +Name[ne]=पहुँचता +Name[nl]=Toegankelijkheid +Name[nn]=Tilgjenge +Name[oc]=Accessibilitat +Name[or]=ଅଭିଗମ୍ୟତା +Name[pa]=ਸਹੂਲਤਾਂ +Name[pl]=Ułatwienia dostępu +Name[pt]=Acessibilidade +Name[pt_BR]=Acessibilidade +Name[ro]=Accesibilitate +Name[ru]=Специальные возможности +Name[se]=Álkkibut geavaheapmi +Name[si]=ප්‍රවේශණය +Name[sk]=Prístupnosť +Name[sl]=Dostopnost +Name[sr]=Приступачност +Name[sr@ijekavian]=Приступачност +Name[sr@ijekavianlatin]=Pristupačnost +Name[sr@latin]=Pristupačnost +Name[sv]=Handikappstöd +Name[ta]=அணுகல் +Name[te]=అందుబాటు +Name[tg]=Имкониятҳо +Name[th]=ช่วยการใช้งาน +Name[tr]=Erişilebilirlik +Name[ug]=ياردەم ئىقتىدارى +Name[uk]=Доступність +Name[uz]=Qulayliklar +Name[uz@cyrillic]=Қулайликлар +Name[vi]=Hỗ trợ truy cập +Name[wa]=Accessibilité +Name[xh]=Unikezelo +Name[x-test]=xxAccessibilityxx +Name[zh_CN]=辅助 +Name[zh_TW]=無障礙輔助 + +Comment=Improve accessibility for disabled persons +Comment[af]=Verbeter toeganklikheid vir gestremde persone +Comment[ar]=تحسين إمكانية الوصول للمعوقين +Comment[ast]=Ameyorar accesibilidá pa discapacitaos +Comment[be]=Павялічвае даступнасць для людзей з фізічнымі адхіленнямі +Comment[be@latin]=Palapšeńnie dastupnaści prahramaŭ dla invalidaŭ +Comment[bg]=Настройки на достъпа за хора с увреждания +Comment[bn]=অসমর্থ ব্যবহারকারীদের সহায়তা করার প্রযুক্তি +Comment[bs]=Poboljšava pristupačnost hendikepiranim osobama +Comment[ca]=Accessibilitat millorada per a persones discapacitades +Comment[ca@valencia]=Accessibilitat millorada per a persones discapacitades +Comment[cs]=Zlepšení přístupnosti pro osoby s postižením +Comment[csb]=Pòprôwionô przëstãpnosc dlô niefùlsprôwnëch lëdzi +Comment[cy]=Gwella hygyrchedd i bobl anabl +Comment[da]=Forbedr tilgængelighed for handicappede personer +Comment[de]=Verbesserte Benutzbarkeit für Menschen mit Behinderung +Comment[el]=Βελτίωση της προσιτότητας για άτομα με ειδικές ανάγκες +Comment[en_GB]=Improve accessibility for disabled persons +Comment[eo]=Plibonigita alirebleco por handikapitoj +Comment[es]=Mejorar accesibilidad para discapacitados +Comment[et]=Puuetega inimestele mõeldud seaded +Comment[eu]=Ezinduentzako erabilerraztasuna handitzen du +Comment[fa]=بهبود دستیابی‌پذیری برای اشخاص ناتوان +Comment[fi]=Esteettömyysasetukset +Comment[fr]=Améliore l'accessibilité pour les personnes handicapées +Comment[fy]=Ferbeterde tagonklikens foar brûkers mei in handikap +Comment[ga]=Feabhsaigh an inrochtaineacht do dhaoine míchumasaithe +Comment[gl]=Accesibilidade mellorada para persoas con discapacidades +Comment[gu]=અસક્ષમ વ્યક્તિઓ માટે ઉપયોગિતામાં સુધારો કરો +Comment[he]=שיפור הנגישות עבור אנשים עם מוגבלויות +Comment[hi]=विकलांग व्यक्तियों के लिए पहुँच बेहतर बनाए +Comment[hne]=विकलांग मनखे बर पहुंच बेहतर बनाए +Comment[hr]=Unaprijedite pristupačnost osobama s invaliditetom +Comment[hsb]=Přistupnosć za zbrašenych zlěpšić +Comment[hu]=A számítógép kezelését segítő eszközök fogyatékosoknak +Comment[ia]=Il meliora le accessibilitate pro personas invalide +Comment[id]=Tingkatkan aksesibilitas bagi orang cacat +Comment[is]=Endurbætt aðgengi fyrir þá sem þurfa +Comment[it]=Migliora l'accessibilità per i disabili +Comment[ja]=障害を持った方達のためのアクセシビリティの改善 +Comment[ka]=ფიზიკური ნაკლოვანებბის მქონი ხალხისთვის სპეციალური შესაძლბლობები +Comment[kk]=Мүмкіншіліктері шектеулі адамдарға көмек +Comment[km]=បង្កើន​មធ្យោបាយ​ងាយស្រួល​សម្រាប់​មនុស្ស​ពិការ +Comment[kn]=ದುರ್ಬಲೀನರಾದ ವ್ಯಕ್ತಿಗಳಿಗೆ ನಿಲುಕಣೆಯನ್ನು (ಅಕ್ಸೆಸಿಬಲಿಟಿ) ಸುಧಾರಿಸಿ +Comment[ko]=장애인들을 위하여 접근성을 향상시킵니다 +Comment[ku]=Ji bo kesên neçalak gihîştinê biguherîne +Comment[lt]=Pagerintas prieinamumas neįgaliems asmenims +Comment[lv]=Uzlabo pieejamību cilvēkiem ar īpašām vajadzībām +Comment[mai]=विकलांग लोकनिसभक लेल पहुँच बेहतर बनाएल जाए +Comment[mk]=Ја подобрува пристапливоста за хендикепирани лица +Comment[ml]=അംഗവൈകല്യമുള്ളവര്‍ക്കുള്ള സാമീപ്യത നന്നാക്കുക +Comment[mr]=विकलांग व्यक्तिं करिता सुधारित सुलभता +Comment[ms]=Tingkatkan keaksesan bagi orang kurang upaya +Comment[nb]=Forbedret tilgjengelighet for funksjonshemmede +Comment[nds]=Verbetert den Togang för behinnert Lüüd +Comment[ne]=अक्षम व्यक्तिका लागि पहुँच क्षमता सुधार गर्नुहोस् +Comment[nl]=Verbeterde toegankelijkheid voor gebruikers met een handicap +Comment[nn]=Forbetra tilgjenge for funksjonshemma personar +Comment[or]=ନିଷ୍କ୍ରିୟ ବ୍ୟକ୍ତିମାନଙ୍କ ପାଇଁ ଅଭିଗମ୍ୟତା ଉନ୍ନତ କରନ୍ତୁ +Comment[pa]=ਅਪੰਗ ਵਿਅਕਤੀ ਲਈ ਸੁਧਾਰੀਆਂ ਸਹੂਲਤਾਂ +Comment[pl]=Poprawiona dostępność dla osób niepełnosprawnych +Comment[pt]=Acessibilidade melhorada para pessoas com necessidades especiais +Comment[pt_BR]=Melhora a acessibilidade para pessoas com deficiência +Comment[ro]=Îmbunătățește accesibilitate KDE pentru persoanele cu handicap +Comment[ru]=Специальные возможности для людей с физическими ограничениями +Comment[se]=Álkkibut geavaheapmi doaimmahehttejuvvon olbmuide +Comment[si]=අබාධිත අයෙකුට පහසුවෙන් පිවිසීමට ඉඩදෙයි +Comment[sk]=Zlepšenie prístupnosti pre postihnutých užívateľov +Comment[sl]=Izboljšana dostopnost za prizadete osebe +Comment[sr]=Побољшава приступачност хендикепираним особама +Comment[sr@ijekavian]=Побољшава приступачност хендикепираним особама +Comment[sr@ijekavianlatin]=Poboljšava pristupačnost hendikepiranim osobama +Comment[sr@latin]=Poboljšava pristupačnost hendikepiranim osobama +Comment[sv]=Förbättrad tillgänglighet för handikappade personer +Comment[ta]=பார்க்க முடியாதவர்களுக்கு அணுகலை அதிகப்படுத்தவும் +Comment[te]=వికలాంగులైన వ్యక్తులకు అందుబాటును మెరుగుపరుచుము +Comment[tg]=Специальные возможности для людей с физическими недостатками +Comment[th]=ช่วยให้ผู้พิการหรือผู้บกพร่องทางร่างกายด้านอื่นๆ สามารถใช้งานได้สะดวกสบายมากขึ้น +Comment[tr]=Görme sorunu olanlar için erişilebilirlik seçenekleri +Comment[ug]=مېيىپلەر ئۈچۈن ئىشلىتىلىدىغان ياردەمچى ئىقتىدارلار +Comment[uk]=Підвищення зручності для інвалідів +Comment[uz]=Nogiron kishilar uchun qulayliklar +Comment[uz@cyrillic]=Ногирон кишилар учун қулайликлар +Comment[vi]=Cải thiện khả năng truy cập cho người khuyết tật +Comment[wa]=Rind pus åjhey l' accessibilité po les sakîs k' ont des andicapes +Comment[xh]=Phucula unikekezeleko lwabantu abakhubazekileyo +Comment[x-test]=xxImprove accessibility for disabled personsxx +Comment[zh_CN]=方便残疾人使用的辅助功能 +Comment[zh_TW]=方便傷殘人士使用 + +X-KDE-Keywords=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys +X-KDE-Keywords[bs]=pristup,pristupačnost,gluh,oštećen,sluh,gubitak sluha,zvono,zvučno zvono,vidljivo zvono,tastatura,tipke,ljepljive tipke,odskočne tipke,usporene tipke,navigacija uz pomoć miša,numerički dio, +X-KDE-Keywords[ca]=accés,accessibilitat,sord,discapacitat,oïda,pèrdua d'oïda,campana,campana audible,campana visible,Teclat,tecles apegaloses,repetició de tecles,tecles lentes,navegació de ratolí,teclat numèric,activació de gestos,gestors,apegalós,tecles modificadores,modificador,tecles bloquejadores +X-KDE-Keywords[ca@valencia]=accés,accessibilitat,sord,discapacitat,oïda,pèrdua d'oïda,campana,campana audible,campana visible,Teclat,tecles apegaloses,repetició de tecles,tecles lentes,navegació de ratolí,teclat numèric,activació de gestos,gestors,apegalós,tecles modificadores,modificador,tecles bloquejadores +X-KDE-Keywords[da]=tilgang,tilgængelighed,døv,hæmmet,handicappet,hørelse,hørehæmmet,klokke,hørbar klokke,synlig klokke,tastatur,taster,sticky keys,elastiske taster,bounce,langsomme taster,musenavigation,numerisk tastatur,aktiveringsgestusser,gestusser,klæbende,ændringstaster,låsetaster +X-KDE-Keywords[de]=Behinderung,Maussteuerung,Signale,Tastatur,Tasten,Klebende Tasten,Taubheit,Verlangsamte Tasten,Zahlenblock,Zugang,Zugangshilfen,Zahlentasten,Gesten aktivieren,Sondertasten,Sperrtasten +X-KDE-Keywords[el]=πρόσβαση,προσβασιμότητα,βαρήκοος,πρόβλημα,ακοής,απώλεια ακοής,κουδούνι,ηχητικό κουδούνι,οπτικό κουδούνι,πληκτρολόγιο,πλήκτρα,κολλημένα πλήκτρα,πλήκτρα αναπήδησης,αργά πλήκτρα,πλοήγηση με ποντίκι,αριθμητικό πληκτρολόγιο,numpad,νεύματα ενεργοποίησης,νεύματα,κολλημένο,πλήκτρα τροποποιητή,τροποποιητής,πλήκτρα κλειδώματος +X-KDE-Keywords[es]=acceso,accesibilidad,sordera,descapacitado,oído,pérdida de oído,campana,campana audible,campana visible,teclado,teclas,teclas pegajosas,repetición de teclas,teclas lentas,navegación con ratón,teclado numérico,gestos de activación,gestos,pegajoso,teclas modificadoras,modificador,teclas de bloqueo +X-KDE-Keywords[et]=hõlbustus,kasutusmugavus,kurdid,tummad,kuulmine,kuulmiskaotus,puuetega,kell,kuuldav kell,nähtav kell,klaviatuur,klahvid,kleepuvad klahvid,põrkuvad klahvid,aeglased klahvid,hiirega liikumine,numbriklahvistik,aktiveerimisžestid,žestid,muuteklahvid,lukustusklahvid +X-KDE-Keywords[eu]=sarbide,erabilerraztasun,gor,baliaezintasun,entzumen,entzumena galdu,kanpai,kanpai entzungarri,kanpai ikusgarri,teklatu,tekla,tekla itsaskor,errebote-teklak,tekla motel,sagu bidezko nabigazio,zenbakizko teklatu,zk teklatu,aktibazio-keinu,keinu,itsaskor,tekla aldatzaile,aldatzaile,blokeo-tekla +X-KDE-Keywords[fi]=esteettömyys,kuuro,heikentynyt,heikko,kuulo,varoitus,äänimerkki,visuaalinen äänimerkki,näppäimistö,näppäimet,alas jäävät näppäimet,tahmeat näppäimet,ponnahdusnäppäimet,hitaat näppäimet,hiirinäppäimet,numeronäppäimistö +X-KDE-Keywords[fr]=accès, accessibilité, sourd, affaiblis, audition, perte d'audition, cloche, cloche audible, cloche visuelle, clavier, touches, touches collantes, touche rebondissante, touches lentes, navigation de la souris, pavé numérique, gestes d'activation, gestes, collant, touches modifiantes, modificateur, touche de verrouillage +X-KDE-Keywords[ga]=rochtain,inrochtaineacht,bodhar,lagaithe,cloigín,cloigín inchloiste,clog infheicthe,Méarchlár,eochracha,eochracha greamaitheacha,eochracha preabtha,eochracha go mall,nascleanúint luiche,eochaircheap uimhriúil,gothaí,greamaitheach,mionthraitheoir,eochracha glasála +X-KDE-Keywords[gl]=acceso, accesibilidade, xordo, discapacitado, minusválido, audición, auditiva, campá audíbel, campá visíbel, teclado, tecla pegañentas, teclas lentas, navegación co rato, teclado numérico, acenos, teclas modificadoras +X-KDE-Keywords[hu]=kezelés,akadálymentesítés,süket,károsodott,hallás,halláskárosodás,csengő,hallható csengő,látható csengő,Billentyűzet,billentyűk,ragadós billentyűk,pattogó billentyűk,lassú billentyűk,egérnavigáció,numerikus billentyűzet +X-KDE-Keywords[ia]=accesso,accessibilitate,surde,debilitate,audito, perdita de audito,campana,campana audibile,campana visibile,Claviero,claves,claves collose,claves saltante,claves lente,navigation de mus,pad numeric, gestos de activation,gestos,collose, claves modificator, modificator, claves blocante +X-KDE-Keywords[it]=accesso,accessibilità,sordo,handicap,udito,sordità,campanella,campanella udibile,campanella visiva,tastiera,tasti,permanenza dei tasti,pressione ravvicinata dei tasti,rallentamento dei tasti,navigazione col mouse,tastierino numerico,attivazione gesti,gesti,tasti modificatori,modificatore,tasti di blocco +X-KDE-Keywords[kk]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys +X-KDE-Keywords[km]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys +X-KDE-Keywords[ko]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,접근,접근성,청각장애,청각 장애,시각장애,시각 장애,키보드,키,고정키,튕김키,느린키,마우스키,숫자패드 +X-KDE-Keywords[mr]=एक्सेस, एक्सेसिबिलिटी, कर्णबधीर, विकलांग, ऐकणे, कमी ऐकू येणे, घंटा, घंटानाद , दृश्यघंटा, कि-बोर्ड, कीज, स्टिकी किज, बाउन्स किज, स्लो किज, माऊस दिशानिर्देश, न्यूम पॅड, एक्टिव्हेशन गेस्चर्स, गेस्चर्स, स्टिकी, मॉडीफायर कीज, मॉडीफायर, लॉकिंग किज, +X-KDE-Keywords[nb]=tilgang,tilgjengelighet,døv,hemmet,hørsel, hørselstap,bjelle,hørbar bjelle,synlig bjelle,Tastatur,låsetaster,faste taster,trege taster,musnavigering, talltastatur,tallboks,aktiveringsbevegelser,bevegelser,valgtaster +X-KDE-Keywords[nds]=Togang,Toganghülp,doof,kiekbehinnert,Hören,Pingel,Ogenpingel,Tastatuur,Tasten,backige Tasten,jumpen Tasten,langsame Tasten,Muusstüern,Tallenblock,Tekens,Anmaaktekens,backig,Sünnertasten,Wesseltekens,Fastsetttasten +X-KDE-Keywords[nl]=toegang,toegankelijkheid,doof,gehandicapt,gehoor,gehoorverlies,bel,hoorbare bel,zichtbare bel,toetsenbord,toetsen,dode toetsen,herhaaltoetsen,langzame toetsen,muisnavigatie,numeriek toetsenbord,activeringsgebaren,gebaren,plakkerig,modificatietoetsen,modificator,vergrendeltoets +X-KDE-Keywords[pl]=dostęp,dostępność,głuchota,upośledzenie,dzwonek,słyszalny dzwonek,widoczny dzwonek,Klawiatura,klawisze,lepkie klawisze,odbijające klawisze,powolne klawisze, ruchy myszą,klawiatura numeryczna +X-KDE-Keywords[pt]=acesso,acessibilidade,surdo,deficiente,campainha,campainha audível,campainha visível,teclado,teclas,teclas fixas,teclas lentas,teclas sonoras,navegação do rato,teclado numérico,numérico,gestos de activação,gestos,fixo,teclas modificadoras,modificadora,teclas de bloqueio +X-KDE-Keywords[pt_BR]=acesso,acessibilidade,surdo,deficiente,audição,campainha,campainha audível,campainha visível,Teclado,teclas,teclas de aderência,teclas lentas,teclas de repercussão,navegação do mouse,teclado numérico numérico,gestos de ativação,gestos,fixo,teclas modificadoras,modificadora,teclas de bloqueio +X-KDE-Keywords[ru]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,доступ,доступность,глухота,слух,ослабленный,слабый,потеря слуха,звонок,сигнал,звуковой сигнал,визуальный сигнал,видимый сигнал,клавиатура,кнопки,клавиши,залипание,залипающие клавиши,липкие клавиши,прыгающие клавиши,«прыгающие» клавиши,замедленные клавиши,навигация с помощью мыши,управление мышью,цифровая клавиатура,жесты активации,жесты,клавиши-модификаторы,модификаторы,модификатор,клавиши индикаторов +X-KDE-Keywords[sk]=prístup,prístupnosť,hluchý,opitý,sluch,strata sluchu,zvonček,hlasný zvonček,viditeľný zvonček,Klávesnica,klávesy,lepkavé klávesy,skákajúce klávesy,pomalé klávesy,navigácia myšou,numerická klávesnica,numpad,aktivačné gestá,lepkavé,modifikačné klávesy,modifikátor,zamykacie klávesy +X-KDE-Keywords[sl]=dostop,dostopnost,gluh,oviran,sluh,slušno,izguba sluha,zvonec,slišen zvonec,viden zvonec,tipkovnica,tipke,lepljive tipke,odbijajoče tipke,počasne tipke,krmarjenje z miško,številčnica,številske tipke,kretnje za vklop,kretnje,lepljivo,spremenilne tipke,zaklepne tipke +X-KDE-Keywords[sr]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,приступ,приступачност,глув,оштећен,слух,губитак,звоно,чујно,видно,тастатура,тастери,лепљиви,одскачући,спори,нумеричка,активација,гестови,лепљив,модификаторски,тастери,закључавање +X-KDE-Keywords[sr@ijekavian]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,приступ,приступачност,глув,оштећен,слух,губитак,звоно,чујно,видно,тастатура,тастери,лепљиви,одскачући,спори,нумеричка,активација,гестови,лепљив,модификаторски,тастери,закључавање +X-KDE-Keywords[sr@ijekavianlatin]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,pristup,pristupačnost,gluv,oštećen,sluh,gubitak,zvono,čujno,vidno,tastatura,tasteri,lepljivi,odskačući,spori,numerička,aktivacija,gestovi,lepljiv,modifikatorski,tasteri,zaključavanje +X-KDE-Keywords[sr@latin]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,pristup,pristupačnost,gluv,oštećen,sluh,gubitak,zvono,čujno,vidno,tastatura,tasteri,lepljivi,odskačući,spori,numerička,aktivacija,gestovi,lepljiv,modifikatorski,tasteri,zaključavanje +X-KDE-Keywords[sv]=åtkomst,handikappstöd,döva,funktionshindrade,hörsel,hörselförlust,summer,hörbar summer,synlig summer,Tangentbord,tangenter,klistriga tangenter,studsande tangenter,långsamma tangenter,musnavigering,numeriskt tangentbord,aktiveringsgester,gester,klistrig,väljartangenter,väljare,låstangenter +X-KDE-Keywords[tr]=erişim,erişebilirlik,sağır,bozukluk,işitme,kayıp,zil,işitilebilir zil,görünen zil,Klayve,tuşlar,yapışkan tuşlar,sıçrama tuşları,yavaş tuşlar,fare gezinmesi,rakamlar klavyesi,jestler,yapışkan,hızlandırma tuşları,hızlandırıcı,kilitleme tuşları +X-KDE-Keywords[uk]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,доступ,доступність,спеціальні можливості,глухий,слух,враження,вади,звук,сповіщення,видиме сповіщення,візуальне сповіщення,клавіатура,клавіші,клавіша,липкі клавіші,утримування,повільні клавіші,миша,навігація,цифри,цифровий блок,слух,втрата слуху,жести активування,жести,липка,липкі клавіші,клавіші-модифікатори,модифікатори,клавіші фіксування,фіксування,блокування +X-KDE-Keywords[x-test]=xxaccess,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keysxx +X-KDE-Keywords[zh_CN]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys,访问,可访问性,耳聋,聋,听力,听力丧失,铃声,可视铃声,按键,残疾人,残疾,聋哑,键盘,粘滞键,鼠标,小键盘,撞击键,鼠标导航,数字键盘,激活手势,手势,粘滞,修饰键,锁定键 +X-KDE-Keywords[zh_TW]=access,accessibility,deaf,impaired,hearing,hearing loss,bell,audible bell,visible bell,Keyboard,keys,sticky keys,bounce keys,slow keys,mouse navigation,num pad,numpad,activation gestures,gestures,sticky,modifier keys,modifier,locking keys + +Categories=Qt;KDE;X-KDE-settings-accessibility; diff --git a/kcms/access/kcmaccess.h b/kcms/access/kcmaccess.h new file mode 100644 index 0000000000..d3cb40f304 --- /dev/null +++ b/kcms/access/kcmaccess.h @@ -0,0 +1,76 @@ +/** + * kcmaccess.h + * + * Copyright (c) 2000 Matthias H�zer-Klpfel + * + */ + +#ifndef __kcmaccess_h__ +#define __kcmaccess_h__ + + +#define KDE3_SUPPORT +#include +#undef KDE3_SUPPORT +#include + + +class QCheckBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QRadioButton; +class KColorButton; +class QSlider; + +class KAccessConfig : public KCModule +{ + Q_OBJECT + +public: + + KAccessConfig( QWidget *parent, const QVariantList& ); + virtual ~KAccessConfig(); + + void load(); + void save(); + void defaults(); + +protected Q_SLOTS: + + void configChanged(); + void checkAccess(); + void invertClicked(); + void flashClicked(); + void selectSound(); + void changeFlashScreenColor(); + void configureKNotify(); + +private: + + QCheckBox *systemBell, *customBell, *visibleBell; + QRadioButton *invertScreen, *flashScreen; + QLabel *soundLabel, *colorLabel; + QLineEdit *soundEdit; + QPushButton *soundButton; + KColorButton *colorButton; + KDoubleNumInput *durationSlider; + + QCheckBox *stickyKeys, *stickyKeysLock, *stickyKeysAutoOff; + QCheckBox *stickyKeysBeep, *toggleKeysBeep, *kNotifyModifiers; + QPushButton *kNotifyModifiersButton; + + QCheckBox *slowKeys, *bounceKeys; + KDoubleNumInput *slowKeysDelay, *bounceKeysDelay; + QCheckBox *slowKeysPressBeep, *slowKeysAcceptBeep; + QCheckBox *slowKeysRejectBeep, *bounceKeysRejectBeep; + + QCheckBox *gestures, *gestureConfirmation; + QCheckBox *timeout; + KIntNumInput *timeoutDelay; + QCheckBox *accessxBeep, *kNotifyAccessX; + QPushButton *kNotifyAccessXButton; +}; + + +#endif diff --git a/kcms/access/main.cpp b/kcms/access/main.cpp new file mode 100644 index 0000000000..09864fc2f8 --- /dev/null +++ b/kcms/access/main.cpp @@ -0,0 +1,55 @@ + +#include "kaccess.h" +#include +#include +#include +#include +#include +#include + +extern "C" Q_DECL_EXPORT int kdemain(int argc, char * argv[] ) +{ + K4AboutData about(I18N_NOOP("kaccess"), 0, ki18n("KDE Accessibility Tool"), + 0, KLocalizedString(), K4AboutData::License_GPL, + ki18n("(c) 2000, Matthias Hoelzer-Kluepfel")); + + about.addAuthor(ki18n("Matthias Hoelzer-Kluepfel"), ki18n("Author") , "hoelzer@kde.org"); + + KCmdLineArgs::init( argc, argv, &about ); + + if (!KAccessApp::start()) + return 0; + + // verify the Xlib has matching XKB extension + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + if (!XkbLibraryVersion(&major, &minor)) + { + kError() << "Xlib XKB extension does not match" << endl; + return 1; + } + kDebug() << "Xlib XKB extension major=" << major << " minor=" << minor; + + // we need an application object for QX11Info + KAccessApp app; + + // verify the X server has matching XKB extension + // if yes, the XKB extension is initialized + int opcode_rtrn; + int error_rtrn; + int xkb_opcode; + if (!XkbQueryExtension(QX11Info::display(), &opcode_rtrn, &xkb_opcode, &error_rtrn, + &major, &minor)) + { + kError() << "X server has not matching XKB extension" << endl; + return 1; + } + kDebug() << "X server XKB extension major=" << major << " minor=" << minor; + + //Without that, the application dies when the dialog is closed only once. + app.setQuitOnLastWindowClosed(false); + + app.setXkbOpcode(xkb_opcode); + app.disableSessionManagement(); + return app.exec(); +} diff --git a/kcms/autostart/AUTHORS b/kcms/autostart/AUTHORS new file mode 100644 index 0000000000..ab559c90d6 --- /dev/null +++ b/kcms/autostart/AUTHORS @@ -0,0 +1,2 @@ +Stephen Leaf +Montel Laurent diff --git a/kcms/autostart/CMakeLists.txt b/kcms/autostart/CMakeLists.txt new file mode 100644 index 0000000000..d1832e8774 --- /dev/null +++ b/kcms/autostart/CMakeLists.txt @@ -0,0 +1,18 @@ +project (autostart) + +set(kcm_autostart_PART_SRCS + autostartitem.cpp + addscriptdialog.cpp + advanceddialog.cpp + autostart.cpp ) + +qt5_wrap_ui(kcm_autostart_PART_SRCS autostartconfig.ui ) + +add_library(kcm_autostart MODULE ${kcm_autostart_PART_SRCS}) + +target_link_libraries(kcm_autostart KF5::KCMUtils KF5::I18n KF5::KIOCore KF5::KIOWidgets) + +########### install files ############### + +install(TARGETS kcm_autostart DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES autostart.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcms/autostart/Messages.sh b/kcms/autostart/Messages.sh new file mode 100644 index 0000000000..8f32b9b787 --- /dev/null +++ b/kcms/autostart/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcm_autostart.pot +rm -f rc.cpp diff --git a/kcms/autostart/addscriptdialog.cpp b/kcms/autostart/addscriptdialog.cpp new file mode 100644 index 0000000000..8077c24919 --- /dev/null +++ b/kcms/autostart/addscriptdialog.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2007 by Stephen Leaf * + * smileaf@gmail.com * + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#include "addscriptdialog.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +AddScriptDialog::AddScriptDialog (QWidget* parent) + : QDialog( parent ) +{ + QVBoxLayout *lay= new QVBoxLayout; + setLayout( lay ); + QLabel *lab = new QLabel( i18n( "Shell script path:" ), this ); + lay->addWidget( lab ); + m_url = new KUrlRequester( this ); + lay->addWidget( m_url ); + m_symlink = new QCheckBox( i18n( "Create as symlink" ), this ); //TODO fix text + m_symlink->setChecked( true ); + lay->addWidget( m_symlink ); + connect( m_url->lineEdit(), SIGNAL(textChanged(QString)), SLOT(textChanged(QString)) ); + m_url->lineEdit()->setFocus(); + + m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this); + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); + lay->addWidget(m_buttons); + connect(m_buttons, SIGNAL(accepted()), SLOT(accept())); + connect(m_buttons, SIGNAL(rejected()), SLOT(reject())); +} + +AddScriptDialog::~AddScriptDialog() +{ +} + +void AddScriptDialog::textChanged(const QString &text) +{ + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); +} + +void AddScriptDialog::accept() +{ + if ( doBasicSanityCheck() ) + QDialog::accept(); +} + +bool AddScriptDialog::doBasicSanityCheck() +{ + const QString& path = KShell::tildeExpand(m_url->text()); + + QFileInfo file(path); + + if ( ! file.isAbsolute() ) { + KMessageBox::sorry( 0, i18n("\"%1\" is not an absolute path.", path) ); + return false; + } else if ( ! file.exists() ) { + KMessageBox::sorry( 0, i18n("\"%1\" does not exist.", path) ); + return false; + } else if ( !file.isFile() ) { + KMessageBox::sorry( 0, i18n("\"%1\" is not a file.", path) ); + return false; + } else if ( ! file.isReadable() ) { + KMessageBox::sorry( 0, i18n("\"%1\" is not readable.", path) ); + return false; + } + + return true; +} + +QUrl AddScriptDialog::importUrl() const +{ + return m_url->url(); +} + +bool AddScriptDialog::symLink() const +{ + return m_symlink->isChecked(); +} + +#include "addscriptdialog.moc" diff --git a/kcms/autostart/addscriptdialog.h b/kcms/autostart/addscriptdialog.h new file mode 100644 index 0000000000..fc1e608d6a --- /dev/null +++ b/kcms/autostart/addscriptdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2007 by Stephen Leaf * + * smileaf@gmail.com * + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#ifndef ADDSCRIPTDIALOG_H +#define ADDSCRIPTDIALOG_H + +#include +#include + +class QDialogButtonBox; +class KUrlRequester; +class QCheckBox; + +class AddScriptDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddScriptDialog(QWidget* parent=0); + ~AddScriptDialog(); + // Returns the Url of the script to be imported + QUrl importUrl() const; + bool symLink() const; + +public Q_SLOTS: + // reimplemented + virtual void accept(); + +protected: + virtual bool doBasicSanityCheck(); + +private Q_SLOTS: + void textChanged(const QString &text); + +private: + KUrlRequester *m_url; + QCheckBox* m_symlink; + + QDialogButtonBox* m_buttons; +}; + +#endif + diff --git a/kcms/autostart/advanceddialog.cpp b/kcms/autostart/advanceddialog.cpp new file mode 100644 index 0000000000..02b90f8670 --- /dev/null +++ b/kcms/autostart/advanceddialog.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#include "advanceddialog.h" + +#include +#include +#include + +#include + +AdvancedDialog::AdvancedDialog( QWidget *parent, bool status ) + : QDialog( parent ) +{ + QVBoxLayout *lay= new QVBoxLayout(this); + m_onlyInKde = new QCheckBox( i18n( "Autostart only in KDE" ), this ); + m_onlyInKde->setChecked( status ); + lay->addWidget( m_onlyInKde ); + + QDialogButtonBox* buttons = new QDialogButtonBox(this); + buttons->setStandardButtons( QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + setLayout( lay ); + + connect(buttons, SIGNAL(accepted()), SLOT(accept())); + connect(buttons, SIGNAL(rejected()), SLOT(reject())); +} + +AdvancedDialog::~AdvancedDialog() +{ +} + +bool AdvancedDialog::onlyInKde() const +{ + return m_onlyInKde->isChecked(); +} + + +#include "advanceddialog.moc" + diff --git a/kcms/autostart/advanceddialog.h b/kcms/autostart/advanceddialog.h new file mode 100644 index 0000000000..3f22924805 --- /dev/null +++ b/kcms/autostart/advanceddialog.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#ifndef ADVANCEDDIALOG_H +#define ADVANCEDDIALOG_H + +#include +class QCheckBox; + +class AdvancedDialog : public QDialog +{ + Q_OBJECT + +public: + AdvancedDialog(QWidget* parent, bool status); + ~AdvancedDialog(); + + bool onlyInKde() const; + +private: + QCheckBox *m_onlyInKde; +}; + +#endif diff --git a/kcms/autostart/autostart.cpp b/kcms/autostart/autostart.cpp new file mode 100644 index 0000000000..7a18a1775a --- /dev/null +++ b/kcms/autostart/autostart.cpp @@ -0,0 +1,438 @@ +/*************************************************************************** + * Copyright (C) 2006-2007 by Stephen Leaf * + * smileaf@gmail.com * + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#include "autostart.h" +#include "autostartitem.h" +#include "addscriptdialog.h" +#include "advanceddialog.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(AutostartFactory, registerPlugin();) +K_EXPORT_PLUGIN(AutostartFactory( "kcmautostart", "kcm_autostart" )) + +Autostart::Autostart( QWidget* parent, const QVariantList& ) + : KCModule(parent ) +{ + widget = new Ui_AutostartConfig(); + widget->setupUi(this); + + QStringList lstHeader; + lstHeader << i18n( "Name" ) + << i18n( "Command" ) + << i18n( "Status" ) + << i18nc("@title:column The name of the column that decides if the program is run on kde startup, on kde shutdown, etc", "Run On" ); + widget->listCMD->setHeaderLabels(lstHeader); + widget->listCMD->setFocus(); + + setButtons(Help); + + connect( widget->btnAddScript, SIGNAL(clicked()), SLOT(slotAddScript()) ); + connect( widget->btnAddProgram, SIGNAL(clicked()), SLOT(slotAddProgram()) ); + connect( widget->btnRemove, SIGNAL(clicked()), SLOT(slotRemoveCMD()) ); + connect( widget->btnAdvanced, SIGNAL(clicked()), SLOT(slotAdvanced()) ); + connect( widget->listCMD, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(slotEditCMD(QTreeWidgetItem*)) ); + connect( widget->listCMD, SIGNAL(itemClicked(QTreeWidgetItem*,int)),this,SLOT(slotItemClicked(QTreeWidgetItem*,int)) ); + connect( widget->btnProperties, SIGNAL(clicked()), SLOT(slotEditCMD()) ); + connect( widget->listCMD, SIGNAL(itemSelectionChanged()), SLOT(slotSelectionChanged()) ); + + + KAboutData* about = new KAboutData(QStringLiteral("Autostart"), QString(), + i18n("KDE Autostart Manager"), + QStringLiteral("1.0"), + i18n("KDE Autostart Manager Control Panel Module"), + KAboutData::License_GPL, + i18n("Copyright © 2006–2010 Autostart Manager team")); + about->addAuthor(i18n("Stephen Leaf"), QString(), QStringLiteral("smileaf@gmail.com")); + about->addAuthor(i18n("Montel Laurent"), i18n( "Maintainer" ), QStringLiteral("montel@kde.org")); + setAboutData( about ); + +} + +Autostart::~Autostart() +{ + delete widget; +} + + +void Autostart::slotItemClicked( QTreeWidgetItem *item, int col) +{ + if ( item && col == COL_STATUS ) { + DesktopStartItem *entry = dynamic_cast( item ); + if ( entry ) { + bool disable = ( item->checkState( col ) == Qt::Unchecked ); + KDesktopFile kc(entry->fileName().path()); + KConfigGroup grp = kc.desktopGroup(); + if ( grp.hasKey( "Hidden" ) && !disable) { + grp.deleteEntry( "Hidden" ); + } + else + grp.writeEntry("Hidden", disable); + + kc.sync(); + if ( disable ) + item->setText( COL_STATUS, i18nc( "The program won't be run", "Disabled" ) ); + else + item->setText( COL_STATUS, i18nc( "The program will be run", "Enabled" ) ); + } + } +} + +void Autostart::addItem( DesktopStartItem* item, const QString& name, const QString& run, const QString& command, bool disabled ) +{ + Q_ASSERT( item ); + item->setText( COL_NAME, name ); + item->setText( COL_RUN, run ); + item->setText( COL_COMMAND, command ); + item->setCheckState( COL_STATUS, disabled ? Qt::Unchecked : Qt::Checked ); + item->setText( COL_STATUS, disabled ? i18nc( "The program won't be run", "Disabled" ) : i18nc( "The program will be run", "Enabled" )); +} + +void Autostart::addItem(ScriptStartItem* item, const QString& name, const QString& command, ScriptStartItem::ENV type ) +{ + Q_ASSERT( item ); + item->setText( COL_NAME, name ); + item->setText( COL_COMMAND, command ); + item->changeStartup( type ); +} + + +void Autostart::load() +{ + // share/autostart may *only* contain .desktop files + // shutdown and env may *only* contain scripts, links or binaries + // autostart on the otherhand may contain all of the above. + // share/autostart is special as it overrides entries found in $KDEDIR/share/autostart + m_paths << QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/autostart/") + << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/shutdown/") + << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/env/") + << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/share/autostart/"); // For Importing purposes + + // share/autostart shouldn't be an option as this should be reserved for global autostart entries + m_pathName << i18n("Startup") + << i18n("Shutdown") + << i18n("Pre-KDE startup") + ; + widget->listCMD->clear(); + + m_programItem = new QTreeWidgetItem( widget->listCMD ); + m_programItem->setText( 0, i18n( "Desktop File" )); + m_programItem->setFlags(m_programItem->flags()^Qt::ItemIsSelectable ); + + QFont boldFont = m_programItem->font(0); + boldFont.setBold( true ); + m_programItem->setData ( 0, Qt::FontRole, boldFont ); + + m_scriptItem = new QTreeWidgetItem( widget->listCMD ); + m_scriptItem->setText( 0, i18n( "Script File" )); + m_scriptItem->setFlags(m_scriptItem->flags()^Qt::ItemIsSelectable); + m_scriptItem->setData ( 0, Qt::FontRole, boldFont); + + widget->listCMD->expandItem( m_programItem ); + widget->listCMD->expandItem( m_scriptItem ); + + foreach (const QString& path, m_paths) { + QDir d(path); + if (!d.exists()) + d.mkpath(path); + + QDir autostartdir( path ); + autostartdir.setFilter( QDir::Files ); + const QFileInfoList list = autostartdir.entryInfoList(); + for (int i = 0; i < list.size(); ++i) { + QFileInfo fi = list.at(i); + QString filename = fi.fileName(); + bool desktopFile = filename.endsWith(".desktop"); + if ( desktopFile ) + { + KDesktopFile config(fi.absoluteFilePath()); + //kDebug() << fi.absoluteFilePath() << "trying" << config.desktopGroup().readEntry("Exec"); + QStringList commandLine = KShell::splitArgs(config.desktopGroup().readEntry("Exec")); + if (commandLine.isEmpty()) { + continue; + } + + const QString exe = commandLine.first(); + if (exe.isEmpty() || QStandardPaths::findExecutable(exe).isEmpty()) { + continue; + } + + DesktopStartItem *item = new DesktopStartItem( fi.absoluteFilePath(), m_programItem, this ); + + const KConfigGroup grp = config.desktopGroup(); + const bool hidden = grp.readEntry("Hidden", false); + const QStringList notShowList = grp.readXdgListEntry("NotShowIn"); + const QStringList onlyShowList = grp.readXdgListEntry("OnlyShowIn"); + + const bool disabled = hidden || + notShowList.contains("KDE") || + (!onlyShowList.isEmpty() && !onlyShowList.contains("KDE")); + + int indexPath = m_paths.indexOf((item->fileName().adjusted(QUrl::RemoveFilename).toString() ) ); + if ( indexPath > 2 ) + indexPath = 0; //.kde/share/autostart and .config/autostart load destkop at startup + addItem(item, config.readName(), m_pathName.value(indexPath), grp.readEntry("Exec"), disabled ); + } + else + { + ScriptStartItem *item = new ScriptStartItem( fi.absoluteFilePath(), m_scriptItem,this ); + int typeOfStartup = m_paths.indexOf((item->fileName().adjusted(QUrl::RemoveFilename).toString()) ); + ScriptStartItem::ENV type = ScriptStartItem::START; + switch( typeOfStartup ) + { + case 0: + type =ScriptStartItem::START; + break; + case 1: + type = ScriptStartItem::SHUTDOWN; + break; + case 2: + type = ScriptStartItem::PRE_START; + break; + default: + qDebug()<<" type is not defined :"<listCMD->resizeColumnToContents(COL_NAME); + //widget->listCMD->resizeColumnToContents(COL_COMMAND); + widget->listCMD->resizeColumnToContents(COL_STATUS); + widget->listCMD->resizeColumnToContents(COL_RUN); +} + +void Autostart::slotAddProgram() +{ + KOpenWithDialog owdlg( this ); + if (owdlg.exec() != QDialog::Accepted) + return; + + KService::Ptr service = owdlg.service(); + + Q_ASSERT(service); + if (!service) { + return; // Don't crash if KOpenWith wasn't able to create service. + } + + // It is important to ensure that we make an exact copy of an existing + // desktop file (if selected) to enable users to override global autostarts. + // Also see + // https://bugs.launchpad.net/ubuntu/+source/kde-workspace/+bug/923360 + QString desktopPath; + QUrl desktopTemplate; + if ( service->desktopEntryName().isEmpty() ) { + // Build custom desktop file (e.g. when the user entered an executable + // name in the OpenWithDialog). + desktopPath = m_paths[4] + service->name() + ".desktop"; + desktopTemplate = QUrl( desktopPath ); + KConfig kc(desktopTemplate.path(), KConfig::SimpleConfig); + KConfigGroup kcg = kc.group("Desktop Entry"); + kcg.writeEntry("Exec",service->exec()); + kcg.writeEntry("Icon","system-run"); + kcg.writeEntry("Path",""); + kcg.writeEntry("Terminal",false); + kcg.writeEntry("Type","Application"); + kc.sync(); + + KPropertiesDialog dlg( desktopTemplate, this ); + if ( dlg.exec() != QDialog::Accepted ) + { + return; + } + } + else + { + // Use existing desktop file and use same file name to enable overrides. + desktopPath = m_paths[4] + service->desktopEntryName() + ".desktop"; + desktopTemplate = QUrl( QStandardPaths::locate(QStandardPaths::ApplicationsLocation, service->entryPath()) ); + + KPropertiesDialog dlg( desktopTemplate, QUrl(m_paths[4]), service->desktopEntryName() + ".desktop", this ); + if ( dlg.exec() != QDialog::Accepted ) + return; + } + DesktopStartItem * item = new DesktopStartItem( desktopPath, m_programItem,this ); + addItem( item, service->name(), m_pathName[0], service->exec() , false); +} + +void Autostart::slotAddScript() +{ + AddScriptDialog * addDialog = new AddScriptDialog(this); + int result = addDialog->exec(); + if (result == QDialog::Accepted) { + if (addDialog->symLink()) + KIO::link(addDialog->importUrl(), QUrl(m_paths[0])); + else + KIO::copy(addDialog->importUrl(), QUrl(m_paths[0])); + + ScriptStartItem * item = new ScriptStartItem( m_paths[0] + addDialog->importUrl().fileName(), m_scriptItem,this ); + addItem( item, addDialog->importUrl().fileName(), addDialog->importUrl().fileName(),ScriptStartItem::START ); + } + delete addDialog; +} + +void Autostart::slotRemoveCMD() +{ + QTreeWidgetItem* item = widget->listCMD->currentItem(); + if (!item) + return; + DesktopStartItem *startItem = dynamic_cast( item ); + if ( startItem ) + { + m_programItem->takeChild( m_programItem->indexOfChild( startItem ) ); + KIO::del(startItem->fileName()); + delete item; + } + else + { + ScriptStartItem * scriptItem = dynamic_cast( item ); + if ( scriptItem ) + { + m_scriptItem->takeChild( m_scriptItem->indexOfChild( scriptItem ) ); + KIO::del(scriptItem->fileName()); + delete item; + } + } +} + +void Autostart::slotEditCMD(QTreeWidgetItem* ent) +{ + if (!ent) return; + AutoStartItem *entry = dynamic_cast( ent ); + if ( entry ) + { + const KFileItem kfi = KFileItem( KFileItem::Unknown, KFileItem::Unknown, QUrl( entry->fileName() ), true ); + if (! slotEditCMD( kfi )) + return; + DesktopStartItem *desktopEntry = dynamic_cast( entry ); + if (desktopEntry) { + KService service(desktopEntry->fileName().path()); + addItem( desktopEntry, service.name(), m_pathName.value(m_paths.indexOf(desktopEntry->fileName().adjusted(QUrl::RemoveFilename).toString())), service.exec(),false ); + } + } +} + +bool Autostart::slotEditCMD( const KFileItem &item) +{ + KPropertiesDialog dlg( item, this ); + bool c = ( dlg.exec() == QDialog::Accepted ); + return c; +} + +void Autostart::slotEditCMD() +{ + if ( widget->listCMD->currentItem() == 0 ) + return; + slotEditCMD( (AutoStartItem*)widget->listCMD->currentItem() ); +} + +void Autostart::slotAdvanced() +{ + if ( widget->listCMD->currentItem() == 0 ) + return; + + DesktopStartItem *entry = static_cast( widget->listCMD->currentItem() ); + KDesktopFile kc(entry->fileName().path()); + KConfigGroup grp = kc.desktopGroup(); + bool status = false; + QStringList lstEntry; + if (grp.hasKey("OnlyShowIn")) + { + lstEntry = grp.readXdgListEntry("OnlyShowIn"); + status = lstEntry.contains("KDE"); + } + + AdvancedDialog *dlg = new AdvancedDialog( this,status ); + if ( dlg->exec() ) + { + status = dlg->onlyInKde(); + if ( lstEntry.contains( "KDE" ) && !status ) + { + lstEntry.removeAll( "KDE" ); + grp.writeXdgListEntry( "OnlyShowIn", lstEntry ); + } + else if ( !lstEntry.contains( "KDE" ) && status ) + { + lstEntry.append( "KDE" ); + grp.writeXdgListEntry( "OnlyShowIn", lstEntry ); + } + } + delete dlg; +} + +void Autostart::slotChangeStartup( ScriptStartItem* item, int index ) +{ + Q_ASSERT(item); + + if ( item ) + { + item->setPath(m_paths.value(index)); + widget->listCMD->setCurrentItem( item ); + if ( ( index == 2 ) && !item->fileName().path().endsWith( ".sh" )) + KMessageBox::information( this, i18n( "Only files with “.sh” extensions are allowed for setting up the environment." ) ); + + } +} + +void Autostart::slotSelectionChanged() +{ + const bool hasItems = ( dynamic_cast( widget->listCMD->currentItem() )!=0 ) ; + widget->btnRemove->setEnabled(hasItems); + + const bool isDesktopItem = (dynamic_cast(widget->listCMD->currentItem() ) != 0) ; + widget->btnProperties->setEnabled(isDesktopItem); + widget->btnAdvanced->setEnabled(isDesktopItem) ; +} + +void Autostart::defaults() +{ +} + +void Autostart::save() +{ +} + +#include "autostart.moc" diff --git a/kcms/autostart/autostart.desktop b/kcms/autostart/autostart.desktop new file mode 100644 index 0000000000..5a1048e724 --- /dev/null +++ b/kcms/autostart/autostart.desktop @@ -0,0 +1,190 @@ +[Desktop Entry] +Exec=kcmshell5 autostart +Icon=system-run +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/autostart/index.html + +X-KDE-Library=kcm_autostart +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=startup-and-shutdown + +Name=Autostart +Name[af]=Outomatiese begin +Name[ar]=البدء التلقائي +Name[ast]=Autoarranque +Name[be]=Аўтаматычны запуск +Name[be@latin]=Aŭtamatyčnaje ŭklučeńnie +Name[bg]=Автоматично стартиране +Name[bn]=অটো-স্টার্ট +Name[bn_IN]=স্বয়ং-আরম্ভ +Name[br]=Emloc'h +Name[bs]=Samopokretanje +Name[ca]=Inici automàtic +Name[ca@valencia]=Inici automàtic +Name[cs]=Automatické spuštění +Name[csb]=Aùtomatné zrëszanié +Name[cy]=Hunan-gychwyn +Name[da]=Autostart +Name[de]=Autostart +Name[el]=Αυτόματη εκκίνηση +Name[en_GB]=Autostart +Name[eo]=Aŭtomata lanĉo +Name[es]=Autoarranque +Name[et]=Autostart +Name[eu]=Hasiera automatikoa +Name[fa]=آغاز خودکار +Name[fi]=Automaattikäynnistys +Name[fr]=Démarrage automatique +Name[fy]=Auto-úteinsette +Name[ga]=Tosú uathoibríoch +Name[gl]=Inicio automático +Name[gu]=આપમેળેશરૂ +Name[he]=הפעלה אוטומטית +Name[hi]=स्वतःप्रारंभ +Name[hne]=खुद से चालू +Name[hr]=Automatsko pokretanje +Name[hsb]=Awtostart +Name[hu]=Automatikus indítás +Name[ia]=Auto starta +Name[id]=Start Otomatis +Name[is]=Sjálfræsing +Name[it]=Avvio automatico +Name[ja]=自動起動 +Name[ka]=ავტოგაშვება +Name[kk]=Автобастау +Name[km]=ចាប់ផ្ដើម​ស្វ័យប្រវត្តិ +Name[kn]=ಸ್ವಯಮಾರಂಭ +Name[ko]=자동 시작 +Name[ku]=Bixweber Bide Destpêkirin +Name[lt]=Autostartas +Name[lv]=Autopalaišana +Name[mai]=स्वतः चालू +Name[mk]=Автостарт +Name[ml]=ഓട്ടോസ്റ്റാര്‍ട്ട് +Name[mr]=स्वप्रारंभ +Name[ms]=Automula +Name[nb]=Autostart +Name[nds]=Autostart +Name[nl]=Autostart +Name[nn]=Autostart +Name[or]=ସ୍ୱୟଂଚାଳନ +Name[pa]=ਆਟੋ-ਸਟਾਰਟ +Name[pl]=Samoczynne uruchamianie +Name[pt]=Arranque +Name[pt_BR]=Iniciar automaticamente +Name[ro]=Pornire automată +Name[ru]=Автозапуск +Name[si]=ක්‍ෂණික ඇරඹුම +Name[sk]=Automatické spustenie +Name[sl]=Samodejni zagon +Name[sr]=Самопокретање +Name[sr@ijekavian]=Самопокретање +Name[sr@ijekavianlatin]=Samopokretanje +Name[sr@latin]=Samopokretanje +Name[sv]=Automatisk start +Name[ta]=சுயதுவக்கம் +Name[te]=స్వయంచాలకప్రారంభం +Name[tg]=Худоғозӣ +Name[th]=เริ่มอัตโนมัติ +Name[tr]=Otomatik Başlat +Name[ug]=ئۆزلۈكىدىن قوزغات +Name[uk]=Автозапуск +Name[uz]=Avto-boshlash +Name[uz@cyrillic]=Авто-бошлаш +Name[vi]=Tự khởi chạy +Name[wa]=Enondaedje tot seu +Name[xh]=Isiqalo esizenzekelayo +Name[x-test]=xxAutostartxx +Name[zh_CN]=自动启动 +Name[zh_TW]=自動啟動 + +Comment=Manage which programs start up automatically with KDE. +Comment[bs]=Upravljanje koji će se programi automatski pokretati pomoću KDE. +Comment[ca]=Gestiona quins programes s'iniciaran automàticament amb el KDE. +Comment[ca@valencia]=Gestiona quins programes s'iniciaran automàticament amb el KDE. +Comment[cs]=Spravujte automatické spouštění programů při startu KDE. +Comment[da]=Håndtér hvilke programmer der automatisk starter op med KDE. +Comment[de]=Verwaltung der automatisch mit KDE zu startenden Anwendungen. +Comment[el]=Διαχείριση των προγραμμάτων που θα ξεκινούν αυτόματα με το KDE. +Comment[es]=Gestionar qué programas se inician automáticamente con KDE. +Comment[et]=Määramine, millised programmid käivituvad automaatselt koos KDE-ga. +Comment[eu]=KDErekin automatikoki abiatuko diren programak kudeatu. +Comment[fi]=Aseta, mitkä ohjelmat käynnistetään automaattisesti KDE:n käynnistyessä. +Comment[fr]=Gère les programmes démarrant automatiquement avec KDE. +Comment[ga]=Bainistigh na ríomhchláir a thosaíonn nuair a thosaíonn KDE. +Comment[gl]=Xestiona os programas que se inician xunto con KDE. +Comment[he]=נהל אילו תוכניות מופעלות ביחד עם KDE. +Comment[hu]=Annak kezelése, hogy mely programok induljanak automatikusan a KDE-vel. +Comment[ia]=Gere qual programma debe startar automaticamente con KDE. +Comment[is]=Stjórntæki til að stilla hvaða forrit eigi að ræsast sjálfkrafa með KDE. +Comment[it]=Gestisci quali programmi avviare automaticamente assieme a KDE. +Comment[kk]=KDE бастағанда автожегілетін бағдарламаларды басқару. +Comment[ko]=KDE를 시작할 때 같이 시작될 프로그램을 관리합니다. +Comment[lt]=Konfigūravimo įrankis valdyti programas, kurios paleidžiamos automatiškai kartu su KDE. +Comment[mr]=केडीई सह कोणते कार्यक्रम स्वप्रारंभ होतील ते नियंत्रीत करा. +Comment[nb]=Styr hvilke program som skal starte automatisk med KDE. +Comment[nds]=Programmen fastleggen, de automaatsch mit KDE opropen warrt +Comment[nl]=Beheren van programma's die automatisch met KDE opstarten. +Comment[pa]=ਪਰਬੰਧ ਕਰਦੀ ਹੈ ਕਿ ਕਿਹੜੇ ਪਰੋਗਰਾਮ ਕੇਡੀਈ (KDE) ਨਾਲ ਆਪਣੇ-ਆਪਣ ਚੱਲਣ। +Comment[pl]=Zdecyduj, które programy mają się uruchamiać wraz ze startem KDE. +Comment[pt]=Gerir os programas que são arrancados automaticamente com o KDE. +Comment[pt_BR]=Gerencia os programas que são carregados automaticamente com o KDE. +Comment[ro]=Gestionează ce programe se pornesc automat împreună cu KDE. +Comment[ru]=Управление программами, запускаемыми автоматически в начале сеанса KDE +Comment[sk]=Spravovať, ktoré programy spustiť automaticky s KDE. +Comment[sl]=Upravljanje programov, ki se samodejno zaženejo ob zagonu KDE. +Comment[sr]=Одредите који се програми покрећу са КДЕ‑ом по пријављивању. +Comment[sr@ijekavian]=Одредите који се програми покрећу са КДЕ‑ом по пријављивању. +Comment[sr@ijekavianlatin]=Odredite koji se programi pokreću sa KDE‑om po prijavljivanju. +Comment[sr@latin]=Odredite koji se programi pokreću sa KDE‑om po prijavljivanju. +Comment[sv]=Hantera vilka program som startas automatiskt när KDE startas. +Comment[tr]=Hangi programların KDE açılırken otomatik olarak başlatılacağını yönetin. +Comment[uk]=Керування програмами, які будуть запускатися разом з графічним середовищем KDE автоматично. +Comment[x-test]=xxManage which programs start up automatically with KDE.xx +Comment[zh_CN]=管理随 KDE 自动启动的程序 +Comment[zh_TW]=管理 KDE 要自動啟動哪些程式。 + +X-KDE-Keywords=Autostart Manager,autostart,startup,system startup,kde start,cron +X-KDE-Keywords[bs]=automatsko pokretanje upravitelja,automatsko pokretanje,pokretanje,sustav za pokretanje,početak kde,kompjuter izvodi zadatak za određeno vrijeme. +X-KDE-Keywords[ca]=Gestor d'inici automàtic,inici automàtic,inici,inici del sistema,inici del kde,cron +X-KDE-Keywords[ca@valencia]=Gestor d'inici automàtic,inici automàtic,inici,inici del sistema,inici del kde,cron +X-KDE-Keywords[da]=autostart,opstart,systemstart,kde start,cron +X-KDE-Keywords[de]=Autostartverwaltung,Autostart,Startvorgang,Systemstart,KDE-Start,Cron +X-KDE-Keywords[el]=διαχειριστής αυτόματης εκκίνησης,αυτόματη εκκίνηση,έναρξη,έναρξη συστήματος,έναρξη kde,cron +X-KDE-Keywords[es]=Gestor de inicio automático,inicio automático,inicio,inicio del sistema,inicio de kde,cron +X-KDE-Keywords[et]=Autostardi haldur,autostart,käivitamine,süsteemi käivitamine,kde käivitamine,cron +X-KDE-Keywords[eu]=Hasiera automatikoaren kudeatzailea,hasiera automatikoa,abiarazi,sistema abiarazi,kde hasi,cron +X-KDE-Keywords[fi]=automaattikäynnistys,hallinta,käynnistys,järjestelmän käynnistys, järjestelmän käynnistyminen,kde:n käynnistys,kde:n käynnistyminen,cron +X-KDE-Keywords[fr]=Gestionnaire de démarrage, démarrage automatique, démarrage, démarrage du système, démarrage de kde, cron +X-KDE-Keywords[gl]=Xestor de inicio, inicio automático, inicio, sistema de arranque, inicio de kde, cron +X-KDE-Keywords[he]=Autostart Manager,autostart,startup,system startup,kde start,cron,הפעלה,התחלה +X-KDE-Keywords[hu]=Automatikus indítás kezelő,automatikus indítás,indulás,rendszerindulás,kde indulás,cron +X-KDE-Keywords[ia]=Gestor de autoinitio,autoinitio,initio,initio de systema.initio de kde,crom +X-KDE-Keywords[it]=Gestore avvio,avvio automatico,avvio,avvio del sistema,avvio di kde,cron +X-KDE-Keywords[kk]=Autostart Manager,autostart,startup,system startup,kde start,cron +X-KDE-Keywords[km]=ចាប់ផ្ដើម​កម្មវិធី​គ្រប់គ្រង​ដោយ​ស្វ័យ​ប្រវត្តិ ចាប់ផ្ដើម​ស្វ័យ​ប្រវត្តិ ចាប់ផ្ដើម ចាប់ផ្ដើម​ប្រព័ន្ធ ចាប់ផ្ដើម kde cron +X-KDE-Keywords[ko]=Autostart Manager,autostart,startup,system startup,kde start,cron,자동 시작,자동시작,시작프로그램,시스템 시작 +X-KDE-Keywords[lt]=Automatinio paleidimo tvarkyklė,automatinis paleidimas, sistemos paleidimas,kde paleidimas,cron +X-KDE-Keywords[mr]=स्वप्रारंभ व्यवस्थापक, स्वप्रारंभ, सुरुवात, सुरुवात प्रणाली, kde सुरुवात, क्रॉन +X-KDE-Keywords[nb]=Autostartbehandler,autostart,oppstart,systemstart,kde-start,cron +X-KDE-Keywords[nds]=Autostartpleger,Autostart,Starten,Hoochfohren,KDE-Start,Cron +X-KDE-Keywords[nl]=Autostartbeheerder,autostarten,opstarten,opstarten systeem,opstarten kde,cron +X-KDE-Keywords[pl]=Menadżer autostartu,autostart,uruchamianie,uruchamianie systemu,kde start,cron +X-KDE-Keywords[pt]=Gestor de arranque,arranque,início,arranque do sistema,início do kde,cron +X-KDE-Keywords[pt_BR]=Gerenciado de inicialização,inicialização,início,inicialização do sistema,início do kde,cron +X-KDE-Keywords[ru]=Autostart Manager,autostart,startup,system startup,kde start,cron,диспетчер автоматического запуска,автоматический запуск,автозапуск,загрузка системы,запуск kde +X-KDE-Keywords[sk]=Správca automatického spustenia,autostart,spustenie,spustenie systému,štart kde,cron +X-KDE-Keywords[sl]=upravljalnik samodejnega zagona,samodejni zagon,zagon,sistemski zagon,zagon kde,zagon sistema,cron +X-KDE-Keywords[sr]=Autostart Manager,autostart,startup,system startup,kde start,cron,самопокретање,менаџер самопокретања,покретање система +X-KDE-Keywords[sr@ijekavian]=Autostart Manager,autostart,startup,system startup,kde start,cron,самопокретање,менаџер самопокретања,покретање система +X-KDE-Keywords[sr@ijekavianlatin]=Autostart Manager,autostart,startup,system startup,kde start,cron,samopokretanje,menadžer samopokretanja,pokretanje sistema +X-KDE-Keywords[sr@latin]=Autostart Manager,autostart,startup,system startup,kde start,cron,samopokretanje,menadžer samopokretanja,pokretanje sistema +X-KDE-Keywords[sv]=Hantering av autostart,autostart,start,systemstart,kde-start,cron +X-KDE-Keywords[tr]=Otomatik Başlatma Yöneticisi, otomatik,başlatma,sistem başlangıcı,kde başlangıcı,görevler,cron +X-KDE-Keywords[uk]=Autostart Manager,autostart,startup,system startup,kde start,cron,керування автозапуском,автозапуск,запуск,запуск системи,запуск kde +X-KDE-Keywords[vi]=trình quản lý tự động chạy,tự động chạy,khởi động,khởi động hệ thống,chạy kde,cron +X-KDE-Keywords[x-test]=xxAutostart Manager,autostart,startup,system startup,kde start,cronxx +X-KDE-Keywords[zh_CN]=Autostart Manager,autostart,startup,system startup,kde start,cron,自动启动管理器,自动启动,启动,系统启动,kde 启动,任务调度 +X-KDE-Keywords[zh_TW]=Autostart Manager,autostart,startup,system startup,kde start,cron diff --git a/kcms/autostart/autostart.h b/kcms/autostart/autostart.h new file mode 100644 index 0000000000..11675e1eaf --- /dev/null +++ b/kcms/autostart/autostart.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2006-2007 by Stephen Leaf * + * smileaf@gmail.com * + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + + +#ifndef AUTOSTART_H +#define AUTOSTART_H + +#include +#include + +#include +#include + +#include "ui_autostartconfig.h" +#include "autostartitem.h" + +class Autostart: public KCModule +{ + Q_OBJECT + +public: + Autostart( QWidget* parent, const QVariantList& ); + ~Autostart(); + enum COL_TYPE { COL_NAME = 0, COL_COMMAND=1, COL_STATUS=2,COL_RUN=3 }; + void load(); + void save(); + void defaults(); + + QStringList listPathName() const { return m_pathName;} + +public Q_SLOTS: + void slotChangeStartup( ScriptStartItem* item, int index ); + +protected: + void addItem(DesktopStartItem *item, const QString& name, const QString& run, const QString& command, bool disabled ); + void addItem(ScriptStartItem *item, const QString& name, const QString& command, ScriptStartItem::ENV type ); + +private Q_SLOTS: + void slotAddProgram(); + void slotAddScript(); + void slotRemoveCMD(); + void slotEditCMD(QTreeWidgetItem*); + bool slotEditCMD(const KFileItem&); + void slotEditCMD(); + void slotSelectionChanged(); + void slotItemClicked( QTreeWidgetItem *, int); + void slotAdvanced(); + +private: + QTreeWidgetItem *m_programItem, *m_scriptItem; + QStringList m_paths; + QStringList m_pathName; + + Ui_AutostartConfig *widget; +}; + +#endif diff --git a/kcms/autostart/autostartconfig.ui b/kcms/autostart/autostartconfig.ui new file mode 100644 index 0000000000..6dfdb8b87c --- /dev/null +++ b/kcms/autostart/autostartconfig.ui @@ -0,0 +1,82 @@ + + AutostartConfig + + + + 0 + 0 + 553 + 384 + + + + + + + false + + + false + + + + + + + Add Program... + + + + + + + Add Script... + + + + + + + &Remove + + + + + + + &Properties... + + + + + + + Qt::Vertical + + + + 79 + 141 + + + + + + + + Qt::Horizontal + + + + + + + Advanced... + + + + + + + + diff --git a/kcms/autostart/autostartitem.cpp b/kcms/autostart/autostartitem.cpp new file mode 100644 index 0000000000..4607408325 --- /dev/null +++ b/kcms/autostart/autostartitem.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#include "autostartitem.h" +#include "autostart.h" + +#include +#include +#include +#include +#include + +#include +#include + +AutoStartItem::AutoStartItem( const QString &service, QTreeWidgetItem *parent, Autostart* ) + : QTreeWidgetItem( parent ) + , m_fileName(service) +{ +} + +AutoStartItem::~AutoStartItem() +{ + +} + +QUrl AutoStartItem::fileName() const +{ + return m_fileName; +} + +void AutoStartItem::setPath(const QString &path) +{ + Q_ASSERT( path.endsWith(QDir::separator()) ); + + if (QUrl(path) == m_fileName.adjusted(QUrl::RemoveFilename)) + return; + + const QString& newFileName = path + m_fileName.fileName(); + KIO::move(m_fileName, QUrl(newFileName)); + + m_fileName = QUrl(newFileName); +} + +DesktopStartItem::DesktopStartItem( const QString &service, QTreeWidgetItem *parent, Autostart*autostart ) + : AutoStartItem( service, parent,autostart ) +{ + setCheckState ( Autostart::COL_STATUS,Qt::Checked ); +} + +DesktopStartItem::~DesktopStartItem() +{ +} + +ScriptStartItem::ScriptStartItem( const QString &service, QTreeWidgetItem *parent, Autostart* autostart ) + : AutoStartItem( service, parent,autostart ) +{ + m_comboBoxStartup = new QComboBox; + m_comboBoxStartup->addItems( autostart->listPathName() ); + + setText( 2, i18nc( "The program will be run", "Enabled" ) ); + QObject::connect( m_comboBoxStartup,SIGNAL(activated(int)),this,SLOT(slotStartupChanged(int)) ); + QObject::connect( this,SIGNAL(askChangeStartup(ScriptStartItem*,int)),autostart,SLOT(slotChangeStartup(ScriptStartItem*,int)) ); + treeWidget()->setItemWidget ( this, Autostart::COL_RUN, m_comboBoxStartup ); +} + +ScriptStartItem::~ScriptStartItem() +{ +} + +void ScriptStartItem::slotStartupChanged(int index) +{ + emit askChangeStartup(this, index); +} + +void ScriptStartItem::changeStartup(ScriptStartItem::ENV type ) +{ + switch( type ) + { + case ScriptStartItem::START: + m_comboBoxStartup->setCurrentIndex( 0 ); + break; + case ScriptStartItem::SHUTDOWN: + m_comboBoxStartup->setCurrentIndex( 1 ); + break; + case ScriptStartItem::PRE_START: + m_comboBoxStartup->setCurrentIndex( 2 ); + break; + default: + qWarning() << " startup type is not defined :" << type; + break; + } +} + +#include "autostartitem.moc" diff --git a/kcms/autostart/autostartitem.h b/kcms/autostart/autostartitem.h new file mode 100644 index 0000000000..2e305bb393 --- /dev/null +++ b/kcms/autostart/autostartitem.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#ifndef AUTOSTARTITEM_H +#define AUTOSTARTITEM_H + +#include +#include +#include + +class QComboBox; +class QTreeWidget; +class Autostart; + +class AutoStartItem : public QTreeWidgetItem, public QObject +{ +public: + AutoStartItem( const QString &service, QTreeWidgetItem *parent, Autostart* ); + ~AutoStartItem(); + + QUrl fileName() const; + + void setPath(const QString &path); + +private: + QUrl m_fileName; +}; + +class DesktopStartItem : public AutoStartItem +{ +public: + DesktopStartItem( const QString &service, QTreeWidgetItem *parent, Autostart* ); + ~DesktopStartItem(); +}; + + +class ScriptStartItem : public AutoStartItem +{ + Q_OBJECT + +public: + enum ENV { START=0, SHUTDOWN=1, PRE_START=2}; //rename + ScriptStartItem( const QString &service, QTreeWidgetItem *parent, Autostart* ); + ~ScriptStartItem(); + + void changeStartup( ScriptStartItem::ENV type ); + +Q_SIGNALS: + void askChangeStartup(ScriptStartItem* item, int index); + +private Q_SLOTS: + + void slotStartupChanged(int index); + +private: + QComboBox *m_comboBoxStartup; +}; + +#endif diff --git a/kcms/bell/CMakeLists.txt b/kcms/bell/CMakeLists.txt new file mode 100644 index 0000000000..772b65c15c --- /dev/null +++ b/kcms/bell/CMakeLists.txt @@ -0,0 +1,28 @@ + + + +########### next target ############### + +set(kcm_bell_PART_SRCS bell.cpp ) + + +add_library(kcm_bell MODULE ${kcm_bell_PART_SRCS}) + + +target_link_libraries(kcm_bell + Qt5::X11Extras + KF5::KCMUtils + KF5::I18n + KF5::Notifications + KF5::WindowSystem + KF5::KDE4Support + ${X11_X11_LIB}) + +install(TARGETS kcm_bell DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES bell.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +ecm_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/kcms/bell/Messages.sh b/kcms/bell/Messages.sh new file mode 100644 index 0000000000..cb6a47821d --- /dev/null +++ b/kcms/bell/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kcmbell.pot diff --git a/kcms/bell/bell.cpp b/kcms/bell/bell.cpp new file mode 100644 index 0000000000..2bf9423d1e --- /dev/null +++ b/kcms/bell/bell.cpp @@ -0,0 +1,257 @@ +/* + Copyright (c) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at) + 1998 Bernd Wuebben + 2000 Matthias Elter + 2001 Carsten PFeiffer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include + + +//Added by qt3to4: +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bell.h" + +#include +#include +#include +#include +#include +#include + + + +K_PLUGIN_FACTORY(KBellConfigFactory, registerPlugin();) +K_EXPORT_PLUGIN(KBellConfigFactory("kcmbell")) + + + +extern "C" +{ + Q_DECL_EXPORT void kcminit_bell() + { + XKeyboardState kbd; + XKeyboardControl kbdc; + + XGetKeyboardControl(QX11Info::display(), &kbd); + + KConfig _config( "kcmbellrc", KConfig::NoGlobals ); + KConfigGroup config(&_config, "General"); + + kbdc.bell_percent = config.readEntry("Volume", kbd.bell_percent); + kbdc.bell_pitch = config.readEntry("Pitch", kbd.bell_pitch); + kbdc.bell_duration = config.readEntry("Duration", kbd.bell_duration); + XChangeKeyboardControl(QX11Info::display(), + KBBellPercent | KBBellPitch | KBBellDuration, + &kbdc); + } +} + +KBellConfig::KBellConfig(QWidget *parent, const QVariantList &args): + KCModule(parent, args) +{ + QBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + QGroupBox *box = new QGroupBox(i18n("Bell Settings"), this ); + QFormLayout *form = new QFormLayout(); + form->setVerticalSpacing(0); + box->setLayout(form); + layout->addWidget(box); + + m_useBell = new QCheckBox( i18n("&Use system bell instead of system notification" ), box ); + m_useBell->setWhatsThis( i18n("You can use the standard system bell (PC speaker) or a " + "more sophisticated system notification, see the " + "\"System Notifications\" control module for the " + "\"Something Special Happened in the Program\" event.")); + connect(m_useBell, SIGNAL(toggled(bool)), SLOT(useBell(bool))); + form->addRow(m_useBell); + + setQuickHelp( i18n("

    System Bell

    Here you can customize the sound of the standard system bell," + " i.e. the \"beep\" you always hear when there is something wrong. Note that you can further" + " customize this sound using the \"Accessibility\" control module; for example, you can choose" + " a sound file to be played instead of the standard bell.")); + + m_volume = new KIntNumInput(50, box); + m_volume->setRange(0, 100, 5); + m_volume->setSuffix("%"); + m_volume->setSteps(5,25); + form->addRow(i18n("&Volume:"), m_volume); + m_volume->setWhatsThis( i18n("Here you can customize the volume of the system bell. For further" + " customization of the bell, see the \"Accessibility\" control module.") ); + + m_pitch = new KIntNumInput(800, box); + m_pitch->setRange(20, 2000, 20); + m_pitch->setSuffix(i18n(" Hz")); + m_pitch->setSteps(40,200); + form->addRow(i18n("&Pitch:"), m_pitch); + m_pitch->setWhatsThis( i18n("Here you can customize the pitch of the system bell. For further" + " customization of the bell, see the \"Accessibility\" control module.") ); + + m_duration = new KIntNumInput(100, box); + m_duration->setRange(1, 1000, 50); + m_duration->setSuffix(i18n(" msec")); + m_duration->setSteps(20,100); + form->addRow(i18n("&Duration:"), m_duration); + m_duration->setWhatsThis( i18n("Here you can customize the duration of the system bell. For further" + " customization of the bell, see the \"Accessibility\" control module.") ); + + m_testButton = new QPushButton(i18n("&Test"), box); + m_testButton->setObjectName("test"); + form->addRow(QString(), m_testButton); + connect( m_testButton, SIGNAL(clicked()), SLOT(ringBell())); + m_testButton->setWhatsThis( i18n("Click \"Test\" to hear how the system bell will sound using your changed settings.") ); + + // watch for changes + connect(m_volume, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(m_pitch, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(m_duration, SIGNAL(valueChanged(int)), SLOT(changed())); + + KAboutData *about = + new KAboutData(I18N_NOOP("kcmbell"), QString(), i18n("KDE Bell Control Module"), + QString(), QString(), KAboutData::License_GPL, + i18n("(c) 1997 - 2001 Christian Czezatke, Matthias Elter")); + + about->addAuthor(i18n("Christian Czezatke"), i18n("Original author"), QStringLiteral("e9025461@student.tuwien.ac.at")); + about->addAuthor(i18n("Bernd Wuebben"), QString(), QStringLiteral("wuebben@kde.org")); + about->addAuthor(i18n("Matthias Elter"), i18n("Current maintainer"), QStringLiteral("elter@kde.org")); + about->addAuthor(i18n("Carsten Pfeiffer"), QString(), QStringLiteral("pfeiffer@kde.org")); + setAboutData(about); +} + +void KBellConfig::load() +{ + XKeyboardState kbd; + XGetKeyboardControl(QX11Info::display(), &kbd); + + m_volume->setValue(kbd.bell_percent); + m_pitch->setValue(kbd.bell_pitch); + m_duration->setValue(kbd.bell_duration); + + KConfig _cfg("kdeglobals", KConfig::NoGlobals); + KConfigGroup cfg(&_cfg, "General"); + m_useBell->setChecked(cfg.readEntry("UseSystemBell", false)); + useBell(m_useBell->isChecked()); + emit changed(false); +} + +void KBellConfig::save() +{ + XKeyboardControl kbd; + + int bellVolume = m_volume->value(); + int bellPitch = m_pitch->value(); + int bellDuration = m_duration->value(); + + kbd.bell_percent = bellVolume; + kbd.bell_pitch = bellPitch; + kbd.bell_duration = bellDuration; + XChangeKeyboardControl(QX11Info::display(), + KBBellPercent | KBBellPitch | KBBellDuration, + &kbd); + + KConfig _config("kcmbellrc", KConfig::NoGlobals); + KConfigGroup config(&_config, "General"); + config.writeEntry("Volume",bellVolume); + config.writeEntry("Pitch",bellPitch); + config.writeEntry("Duration",bellDuration); + + config.sync(); + + KConfig _cfg("kdeglobals", KConfig::NoGlobals); + KConfigGroup cfg(&_cfg, "General"); + cfg.writeEntry("UseSystemBell", m_useBell->isChecked()); + cfg.sync(); + + if (!m_useBell->isChecked()) + { + KConfig config("kaccessrc"); + + KConfigGroup group = config.group("Bell"); + group.writeEntry("SystemBell", false); + group.writeEntry("ArtsBell", false); + group.writeEntry("VisibleBell", false); + } +} + +void KBellConfig::ringBell() +{ + if (!m_useBell->isChecked()) { + KNotification::beep(QString(), this); + return; + } + + // store the old state + XKeyboardState old_state; + XGetKeyboardControl(QX11Info::display(), &old_state); + + // switch to the test state + XKeyboardControl kbd; + kbd.bell_percent = m_volume->value(); + kbd.bell_pitch = m_pitch->value(); + if (m_volume->value() > 0) + kbd.bell_duration = m_duration->value(); + else + kbd.bell_duration = 0; + XChangeKeyboardControl(QX11Info::display(), + KBBellPercent | KBBellPitch | KBBellDuration, + &kbd); + // ring bell + XBell(QX11Info::display(),0); + + // restore old state + kbd.bell_percent = old_state.bell_percent; + kbd.bell_pitch = old_state.bell_pitch; + kbd.bell_duration = old_state.bell_duration; + XChangeKeyboardControl(QX11Info::display(), + KBBellPercent | KBBellPitch | KBBellDuration, + &kbd); +} + +void KBellConfig::defaults() +{ + m_volume->setValue(100); + m_pitch->setValue(800); + m_duration->setValue(100); + m_useBell->setChecked( false ); + useBell( false ); +} + +void KBellConfig::useBell( bool on ) +{ + m_volume->setEnabled( on ); + m_pitch->setEnabled( on ); + m_duration->setEnabled( on ); + m_testButton->setEnabled( on ); + changed(); +} + +#include "bell.moc" diff --git a/kcms/bell/bell.desktop b/kcms/bell/bell.desktop new file mode 100644 index 0000000000..04575958f6 --- /dev/null +++ b/kcms/bell/bell.desktop @@ -0,0 +1,242 @@ +[Desktop Entry] +Exec=kcmshell5 bell +Icon=preferences-desktop-notification-bell +Type=Service +X-KDE-ServiceTypes=KCModule,KCModuleInit +X-DocPath=kcontrol/bell/index.html +Categories=Qt;KDE;X-KDE-settings-sound; +X-KDE-OnlyShowOnQtPlatforms=xcb; + +X-KDE-Library=kcm_bell +X-KDE-Init-Symbol=bell +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=application-and-system-notifications +X-KDE-Weight=60 + +Name=System Bell +Name[af]=Stelsel Klok +Name[ar]=جرس النظام +Name[ast]=Pitíu del sistema +Name[be]=Сістэмны званок +Name[be@latin]=Systemny hukavy syhnał +Name[bg]=Системен звук +Name[bn]=সিস্টেম ঘণ্টা +Name[bn_IN]=সিস্টেমের ঘন্টা +Name[br]=Kloc'h ar reizhiad +Name[bs]=Sistemsko zvono +Name[ca]=Timbre del sistema +Name[ca@valencia]=Timbre del sistema +Name[cs]=Systémový zvonek +Name[csb]=Systemowi brzãczk +Name[cy]=Cloch Cysawd +Name[da]=Systemklokke +Name[de]=Signalton +Name[el]=Μεγαφωνάκι συστήματος +Name[en_GB]=System Bell +Name[eo]=Sistempepo +Name[es]=Timbre del sistema +Name[et]=Süsteemne signaal +Name[eu]=Sistemaren soinua +Name[fa]=زنگ سیستم +Name[fi]=Järjestelmän varoitus +Name[fr]=Cloche du système +Name[fy]=Systeembel +Name[ga]=Cloigín an Chórais +Name[gl]=Badalada do sistema +Name[gu]=સિસ્ટમ ઘંટડી +Name[he]=פעמון המערכת +Name[hi]=तंत्र घंटी +Name[hne]=तंत्र घंटी +Name[hr]=Sistemsko zvono +Name[hsb]=Systemowy zwónčk +Name[hu]=Rendszercsengő +Name[ia]=Campana de systema +Name[id]=Bel Sistem +Name[is]=Kerfisbjalla +Name[it]=Campanella di sistema +Name[ja]=システムベル +Name[ka]=სისტემის ხმოვანი სიგნალი +Name[kk]=Жүйелік қоңырау +Name[km]=កណ្ដឹង​ប្រព័ន្ធ +Name[kn]=ವ್ಯವಸ್ಥಾ ಗಂಟೆ +Name[ko]=시스템 종소리 +Name[ku]=Zingila Pergalê +Name[lt]=Sistemos skambutis +Name[lv]=Sistēmas zvans +Name[mai]=तंत्र घंटी +Name[mk]=Системско ѕвонче +Name[ml]=സിസ്റ്റം മണി +Name[mr]=प्रणाली घंटी +Name[ms]=Loceng Sistem +Name[nb]=Systemlyd +Name[nds]=Systeempingel +Name[ne]=प्रणाली बेल +Name[nl]=Systeembel +Name[nn]=Systemlyd +Name[or]=ତନ୍ତ୍ର ଘଣ୍ଟି +Name[pa]=ਸਿਸਟਮ ਘੰਟੀ +Name[pl]=Brzęczyk systemowy +Name[pt]=Campainha do Sistema +Name[pt_BR]=Campainha do sistema +Name[ro]=Clopoțel de sistem +Name[ru]=Системный звуковой сигнал +Name[se]=Vuogádatjietna +Name[si]=පද්ධති සීනුව +Name[sk]=Zvonček +Name[sl]=Sistemski zvonec +Name[sr]=Системско звоно +Name[sr@ijekavian]=Системско звоно +Name[sr@ijekavianlatin]=Sistemsko zvono +Name[sr@latin]=Sistemsko zvono +Name[sv]=Systemsummer +Name[ta]=அமைப்பு மணி +Name[te]=వ్యవస్థ గంట +Name[tg]=Системаи занг +Name[th]=ออดระบบ +Name[tr]=Sistem Zili +Name[ug]=سىستېما قوڭغۇراق ئاۋازى +Name[uk]=Системний дзвінок +Name[uz]=Tizim tovush signali +Name[uz@cyrillic]=Тизим товуш сигнали +Name[vi]=Chuông hệ thống +Name[wa]=Xhuflet do sistinme +Name[xh]=Indlela yokusebenza Yentsimbi +Name[x-test]=xxSystem Bellxx +Name[zh_CN]=系统铃声 +Name[zh_TW]=系統鈴聲 + +Comment=System Bell Configuration +Comment[af]=Stelsel Klok Opstelling +Comment[ar]=إعداد جرس النظام +Comment[ast]=Configuración del pitíu del sistema +Comment[be]=Настаўленне сістэмнага званка +Comment[be@latin]=Nałady systemnaha hukavoha syhnału +Comment[bg]=Настройване звуковия сигнал на системата +Comment[bn]=সিস্টেম ঘণ্টা কনফিগারেশন +Comment[bn_IN]=সিস্টেমের ঘন্টা সংক্রান্ত কনফিগারেশন +Comment[br]=Kefluniañ kloc'h ar reizhiad +Comment[bs]=Postava sistemskog zvona +Comment[ca]=Configuració del timbre del sistema +Comment[ca@valencia]=Configuració del timbre del sistema +Comment[cs]=Nastavení systémového zvonku +Comment[csb]=Kònfigùracëjô systemòwégò brzãczka +Comment[cy]=Gosodiadau'r Cloch Cysawd +Comment[da]=Systemklokkeindstilling +Comment[de]=Einstellungen zum Signalton +Comment[el]=Διαμόρφωση για το μεγαφωνάκι του συστήματός σας +Comment[en_GB]=System Bell Configuration +Comment[eo]=Agordo de la sistempepo +Comment[es]=Configuración del timbre del sistema +Comment[et]=Süsteemse signaali seadistamine +Comment[eu]=Sistemaren soinuaren konfigurazioa +Comment[fa]=پیکربندی زنگ سیستم +Comment[fi]=Järjestelmän varoitusäänen asetus +Comment[fr]=Configuration de la cloche du système +Comment[fy]=Systeemlûden ynstelle +Comment[ga]=Cumraíocht Chloigín an Chórais +Comment[gl]=Configuración da badalada do sistema +Comment[gu]=સિસ્ટમ ઘંટડી રૂપરેખાંકન +Comment[he]=הגדרות פעמון המערכת +Comment[hi]=तंत्र घंटी कॉन्फ़िगरेशन +Comment[hne]=तंत्र घंटी कान्फिगरेसन +Comment[hr]=Konfiguracija sistemskog zvona +Comment[hsb]=Připrawjenje systemoweho zwónčka +Comment[hu]=A rendszercsengő beállításai +Comment[ia]=Configuration del campana de systema +Comment[id]=Konfigurasi Bel Sistem +Comment[is]=Stillingar kerfishljóða +Comment[it]=Configurazione della campanella di sistema +Comment[ja]=システムベルの設定 +Comment[ka]=სისტემის ხმოვანი სიგნალის კონფიგურაცია +Comment[kk]=Жүйелік қоңырауды баптау +Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​កណ្ដឹង​ប្រព័ន្ធ +Comment[kn]=ವ್ಯವಸ್ಥಾ ಗಂಟೆ ಸಂರಚನೆ +Comment[ko]=시스템 종소리 설정 +Comment[ku]=Veavakirina Zingila Pergalê +Comment[lt]=Sistemos skambučio derinimas +Comment[lv]=Sistēmas zvana konfigurācija +Comment[mai]=तंत्र घंटी कान्फिगरेशन +Comment[mk]=Конфигурација на системското ѕвонче +Comment[ml]=സിസ്റ്റത്തിന്റെ മണിയ്ക്കുള്ള ക്രമീകരണം +Comment[mr]=प्रणाली घंटी संयोजना +Comment[ms]=Konfigurasi Loceng Sistem +Comment[nb]=Oppsett av systemlyd +Comment[nds]=Systeempingel instellen +Comment[ne]=प्रणाली बेल कन्फिगरेसन +Comment[nl]=Systeemgeluiden instellen +Comment[nn]=Systemlydoppsett +Comment[or]=ତନ୍ତ୍ର ଘଣ୍ଟି ସଂରଚନା +Comment[pa]=ਸਿਸਟਮ ਘੰਟੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja brzęczka systemowego +Comment[pt]=Configuração da Campainha do Sistema +Comment[pt_BR]=Configuração da campainha do sistema +Comment[ro]=Configurare Clopoțel de sistem +Comment[ru]=Настройка системного звукового сигнала +Comment[se]=Vuogádatjietnaheivehusat +Comment[si]=පද්ධති සීනු සැකසුම් +Comment[sk]=Nastavenie zvončeka +Comment[sl]=Nastavitve sistemskega zvonca +Comment[sr]=Постава системског звона +Comment[sr@ijekavian]=Постава системског звона +Comment[sr@ijekavianlatin]=Postava sistemskog zvona +Comment[sr@latin]=Postava sistemskog zvona +Comment[sv]=Anpassa systemets summer +Comment[ta]=அமைப்பின் மணி வடிவமைப்பு +Comment[te]=సిస్టమ్ బెల్ సాధనం +Comment[tg]=Танзимоти системаи занг +Comment[th]=ปรับแต่งออดระบบ +Comment[tr]=Sistem Zili Yapılandırması +Comment[ug]=سىستېما قوڭغۇرىقى سەپلىمىسى +Comment[uk]=Налаштування системного дзвінка +Comment[uz]=Tizim tovush signalini moslash +Comment[uz@cyrillic]=Тизим товуш сигналини мослаш +Comment[vi]=Cấu hình Chuông Hệ thống +Comment[wa]=Apontiaedje do xhuflet do sistinme +Comment[xh]=Uqwalaselo Lwendlela Yentsimbi +Comment[x-test]=xxSystem Bell Configurationxx +Comment[zh_CN]=系统铃声配置 +Comment[zh_TW]=系統鈴聲組態 + +X-KDE-Keywords=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker +X-KDE-Keywords[bs]=zvono,zvučni,zvuk,glasnoća,igralište,trajanje,visina tona,trajanje,PC zvučnici,unutarnji zvučnik,zvučnik,sustav zvona,sustav zvučnika,unutarnji PC zvučnik +X-KDE-Keywords[ca]=Campana,Àudio,So,Volum,To,Durada,Altaveu del PC,Altaveu intern,Altaveu,Campana del sistema,Altaveu del sistema,Altaveu intern del PC +X-KDE-Keywords[ca@valencia]=Campana,Àudio,So,Volum,To,Durada,Altaveu del PC,Altaveu intern,Altaveu,Campana del sistema,Altaveu del sistema,Altaveu intern del PC +X-KDE-Keywords[da]=Klokke,lyd,lydstyrke,volume,tone,varighed,pc-højtaler,intern højtaler,højtaler,systemklokke,systemhøjtaler,intern pc-højtaler +X-KDE-Keywords[de]=Signalton,Systemton,Klänge,Audio,Sound,Dauer,Lautstärke,Höhen,PC-Lautsprecher,Interner Lautsprecher,Lautsprecher,Systemsignalton,System-Lautsprecher,Interner PC-Lautsprecher +X-KDE-Keywords[el]=μεγαφωνάκι,ήχος,ήχος,ένταση,τόνος,διάρκεια,ηχείο PC,εσωτερικό ηχείο,ηχείο,κουδούνι συστήματος,ηχείο συστήματος,εσωτερικό ηχείο PC +X-KDE-Keywords[es]=Campana,Audio,Sonido,Volumen,Pitch,Duración,Altavoz del PC,Altavoz interno,Altavoz,Campana del sistema,Altavoz del sistema,Altavoz interno del PC +X-KDE-Keywords[et]=Kell,audio,heli,helitugevus,toon,kestus,arvutikõlar,PC kõlar,kõlar,sisemine kõlar,süsteemne piiks,süsteemi piiks,süsteemi kõlar,PC sisemine kõlar +X-KDE-Keywords[eu]=kanpai,audio,soinu,bolumen,tonu,iraupen,PC bozgorailu,barneko bozgorailu,bozgorailu,sistemaren soinu,sistemaren bozgorailu, PC barneko bozgorailu +X-KDE-Keywords[fi]=varoitus,äänimerkki,ääni,äänenvoimakkuus,äänenkorkeus,kesto,PC-kaiutin,sisäinen kaiutin,kaiutin,järjestelmän äänimerkki,järjestelmäkaiutin,sisäinen PC-kaiutin +X-KDE-Keywords[fr]=Cloche, Audio, Son, Volume, Pas, Durée, Haut-parleur du PC, Haut-parleur interne, Haut parleur, Cloche du système, Haut-parleur du système, Haut-parleur interne du PC +X-KDE-Keywords[gl]=Badalada, campá, son, volume, altofalante, badalada do sistema +X-KDE-Keywords[he]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,ספיקר, שמע, משך, פעמון, גובה +X-KDE-Keywords[hu]=Csengő,Audio,Hang,Hangerő,Hangmagasság,Időtartam,PC hangszóró,Belső hangszóró,Hangszóró,Rendszercsengő,Rendszer hangszóró,Belső PC hangszóró +X-KDE-Keywords[ia]=Campana,Audio,Sono,Volumine,Accordatura,Duration,altoparlator de PC,altoparlator interne, Altoparlator,Campana de systema,altoparlator de systema,Altoparlator de PC interne +X-KDE-Keywords[it]=Campanella,Audio,Suono,Volume,Altezza,Durata,Altoparlanti PC,Altoparlanti interni,Altoparlante,Campanella di sistema,Altoparlanti di sistema,Altoparlanti interni pc +X-KDE-Keywords[kk]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker +X-KDE-Keywords[km]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker +X-KDE-Keywords[ko]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,오디오,사운드,음량,시간,스피커 +X-KDE-Keywords[mr]=घंटी, श्राव्य, ध्वनी, आवाज, आवाजाचा स्तर, अवधी, पी.सी.स्पीकर , इंटर्नल स्पीकर, स्पीकर प्रणाली घंटी, प्रणाली स्पीकर, अंतर्गत पी.सी. स्पीकर +X-KDE-Keywords[nb]=Bjelle, Lyd,Lydstyrke,Tone,Varighet,PC-høyttaler,Intern høyttaler,Høyttaler,Systembjelle,Systemhøyttaler, Intern PC-høyttaler +X-KDE-Keywords[nds]=Pingel,Kläng,Klang,Luutstärk,Toonhööchd,Duer,PC-Luutspreker,intern Luutspreker,Luutspreker,Systempingel,System-Luutspreker,intern PC-Luutspreker +X-KDE-Keywords[nl]=Bel,audio,geluid,volume,toonhoogte,duur,PC-speaker,interne speaker,speaker,systeembel,systeemspeaker,interne PC-speaker +X-KDE-Keywords[pl]=Dzwonek,Audio,Dźwięk,Głośność,Ton,Czas trwania +X-KDE-Keywords[pt]=Campainha,Áudio,Som,Volume,Frequência,Duração,Altifalante do PC,Altifalante Interno,Altifalante,Campainha do Sistema,Altifalante do Sistema,Altifalante Interno do PC +X-KDE-Keywords[pt_BR]=Campainha,Áudio,Som,Volume,Frequência,Duração,Alto-falante do PC,Alto-falante interno,Alto-falante,Campainha do sistema,Alto-falante do sistema,Alto-falante interno do PC +X-KDE-Keywords[ru]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,сигнал,аудио,звук,громкость,высота тона,длительность,колонки,динамики,встроенный динамик,сигнал системы,акустическая система,встроенная акустическая система +X-KDE-Keywords[sk]=Zvonček,zvuk,audio,hlasitosť,výška tónu,trvanie,PC speaker,vnútorný reproduktor,speaker,systémový zvonček,systémový reproduktor,vnútorný PC speaker +X-KDE-Keywords[sl]=zvonec,audio,avdio,zvok,glasnost,ton,višina tona,trajanje,zvočnik,notranji zvočnik,sistemski zvonec,sistemski zvočnik,piskanje,notranji PC zvočnik +X-KDE-Keywords[sr]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,звоно,аудио,звук,јачина,висина,трајање,системско звоно,системски звучник,унутрашњи звучник +X-KDE-Keywords[sr@ijekavian]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,звоно,аудио,звук,јачина,висина,трајање,системско звоно,системски звучник,унутрашњи звучник +X-KDE-Keywords[sr@ijekavianlatin]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,zvono,audio,zvuk,jačina,visina,trajanje,sistemsko zvono,sistemski zvučnik,unutrašnji zvučnik +X-KDE-Keywords[sr@latin]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,zvono,audio,zvuk,jačina,visina,trajanje,sistemsko zvono,sistemski zvučnik,unutrašnji zvučnik +X-KDE-Keywords[sv]=Summer,Ljud,Volym,Tonhöjd,Längd,PC-högtalare,Intern högtalare,Högtalare,Systemsummer,Systemhögtalare,Intern PC-högtalare +X-KDE-Keywords[tr]=Zil,Ses,Ses Düzeyi,Akort,Süre,PC Hoparlörü,İç Hoparlör,Hoparlör,Sistem Zili,Sistem Hoparlörü,Dahili PC Hoparlörü +X-KDE-Keywords[uk]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,дзвінок,гудок,звук,гучність,тон,тривалість,гучномовець,колонки,вбудовані колонки,системний дзвінок,вбудований гучномовець +X-KDE-Keywords[vi]=Chuông,âm thanh,âm lượng,thời lượng,loa PC,loa trong,loa,chuông hệ thống,loa hệ thống, loa PC trong +X-KDE-Keywords[x-test]=xxBell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speakerxx +X-KDE-Keywords[zh_CN]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker,铃声,声音,音量,音调,持续时间,PC 扬声器,内部扬声器,扬声器,PC 喇叭,内部喇叭,喇叭,系统铃声,系统扬声器,系统喇叭,内部 PC 扬声器,内部 PC 喇叭 +X-KDE-Keywords[zh_TW]=Bell,Audio,Sound,Volume,Pitch,Duration,PC Speaker,Internal Speaker,Speaker,System Bell,System Speaker,Internal PC Speaker + diff --git a/kcms/bell/bell.h b/kcms/bell/bell.h new file mode 100644 index 0000000000..fda09b5556 --- /dev/null +++ b/kcms/bell/bell.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at) + 1998 Bernd Wuebben + 2000 Matthias Elter + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __bell_h__ +#define __bell_h__ + +#include "kcmodule.h" + +class QCheckBox; +class KIntNumInput; +class QPushButton; + +class KBellConfig : public KCModule +{ + Q_OBJECT + + public: + KBellConfig(QWidget *parent, const QVariantList &args); + + void load(); + void save(); + void defaults(); + + protected Q_SLOTS: + void ringBell(); + void useBell( bool ); + + private: + QPushButton *m_testButton; + KIntNumInput *m_volume; + KIntNumInput *m_pitch; + KIntNumInput *m_duration; + QCheckBox *m_useBell; +}; + +#endif diff --git a/kcms/colors/CMakeLists.txt b/kcms/colors/CMakeLists.txt new file mode 100644 index 0000000000..ff0b537cfc --- /dev/null +++ b/kcms/colors/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(kcm_colors_SRCS ../krdb/krdb.cpp colorscm.cpp previewwidget.cpp setpreviewwidget.cpp) + +set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) +qt5_add_dbus_interface(kcm_colors_SRCS ${klauncher_xml} klauncher_iface) + +kde4_add_ui_files(kcm_colors_SRCS colorsettings.ui preview.ui setpreview.ui) + +add_library(kcm_colors MODULE ${kcm_colors_SRCS}) +target_link_libraries(kcm_colors + KF5::KCMUtils + KF5::GuiAddons + KF5::I18n + Qt5::DBus + KF5::NewStuff + KF5::KDE4Support + ${X11_LIBRARIES}) + +if(X11_FOUND) + set_target_properties(kcm_colors PROPERTIES COMPILE_FLAGS -DHAVE_X11=1) + target_link_libraries(kcm_colors Qt5::X11Extras) +endif() + +install(TARGETS kcm_colors DESTINATION ${PLUGIN_INSTALL_DIR}) +install( FILES colors.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES colorschemes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) + +# built-in color schemes +file(GLOB schemefiles schemes/*.colors) +install( FILES ${schemefiles} DESTINATION ${DATA_INSTALL_DIR}/color-schemes ) diff --git a/kcms/colors/Messages.sh b/kcms/colors/Messages.sh new file mode 100644 index 0000000000..71fb50becd --- /dev/null +++ b/kcms/colors/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcmcolors.pot +rm -f rc.cpp diff --git a/kcms/colors/README.i18n b/kcms/colors/README.i18n new file mode 100644 index 0000000000..1033fcb9b4 --- /dev/null +++ b/kcms/colors/README.i18n @@ -0,0 +1,54 @@ +There are two sets of preview strings in the color kcm, with special translation needs. In general, length of the translations, and "gist"-level clarity is more important than precise translation. It should be easy to identify what color role a string represents, but there are size considerations that should be taken into account. The tooltips should be translated normally. + + + +When translating the color kcm, it is helpful to understand the intended meaning of the various color roles in order to chose the best possible translation if more than one would be possible. Hopefully the sets (view, window, button, selection, tooltip) are obvious, as well as normal background/foreground. The others are as follows: + +Alternate [Background]: used in lists when every other row uses a different color; "different". +Inactive: comments, something which is old, unimportant/uninteresting/secondary text +Active: e.g. something new, something active (currently engaged in activity), something requesting attention, a hovered hyperlink +Link: hyperlink, somewhere the user can go, new history +Visited: visited hyperlinks, somewhere the user has been, old history +Negative: "bad", untrusted, unreliable, a mistake, an error the user made, an error that occurred +Neutral: warning, encrypted, anything "between" negative and positive +Positive: success, completion messages, trusted content + +For Link, although other uses are possible, translate as referring to hyperlinks to minimize user confusion. + + + +The "generic" preview uses these strings (all with disambiguation "color-kcm-preview"): +- Window Text +- Push Button +- Normal Text +- Selected Text +- link +- visited +- a +- i +- ! +- = +- + + +The two-word strings may be translated normally, though it is suggested to omit the translation of "Text" if the strings would be excessively (around 20+ characters) long and doing so does not cause confusion (i.e. substantially change the conveyed meaning). The "link" and "visited" strings should be translated according to the notes above, ideally keeping the length to four to eight characters; abbreviation (to not less than four characters if possible) is acceptable as the tooltips will provide more complete descriptions. + +The remaining one-character strings represent (respectively) the text roles active (a), inactive (i), negative (!), neutral (=) and positive (+). These should be translated as one- or two-character strings which uniquely correspond to the aforementioned text roles, ideally taking the first letter of the corresponding translation, or maintaining the symbols (!, =, +) as-is if culturally appropriate. + +Ideographic languages (e.g. Japanese, Mandarin, etc.) where the single-character strings can be replaced with one or two characters representing the full word should do so. These should also, if needed, consider a phonetic representation or relevant phrase for the longer elements, to keep the translations close to the same number of characters as the English. + + + +The "set" preview uses these strings (all with disambiguation "color-kcm-set-preview"): +- normal +- link +- visited +- active +- inactive +- alternate +- negative +- neutral +- positive +- hover +- focus + +These live in a preview widget that is two rows tall by nine items wide, and is often the determining factor in the minimum width of the dialog. For this reason, it is important to keep the strings SHORT. To ensure each text block is of sufficient size to provide an adequate preview, each should be at least a few characters long, but please try to limit them to no more than eight or ten characters on average. Abbreviation is fine and encouraged if necessary to achieve this average. In particular, PLEASE avoid adding the translated equivalent of " text" to these strings if at all possible; the tooltips will disambiguate if required. If these objectives cannot be achieved via direct translation, semantic translation (e.g. "negative" -> i18n("oops!"), "neutral" -> i18n("meh")) would be better. (Ideographic languages, which may need to ADD text to achieve a minimum length, should ignore the previous two sentences.) Note also that "hover" and "focus" are actually "decoration" roles, so e.g. "hover text" is incorrect (should be "hover decoration"). diff --git a/kcms/colors/TODO b/kcms/colors/TODO new file mode 100644 index 0000000000..a0695b9ffd --- /dev/null +++ b/kcms/colors/TODO @@ -0,0 +1,3 @@ +query kwin for what WM colors should be shown for configuring(?) +options (i.e. "things on the first tab") not saved in schemes (in kdeglobals only) +sort schemes list, 'current' at 0, 'default' at 1, everything else sorted (possibly kde3-style, user schemes on top?) diff --git a/kcms/colors/colors.desktop b/kcms/colors/colors.desktop new file mode 100644 index 0000000000..adc2d4e61d --- /dev/null +++ b/kcms/colors/colors.desktop @@ -0,0 +1,194 @@ +[Desktop Entry] +Exec=kcmshell5 colors +Icon=preferences-desktop-color +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/colors/index.html + +X-KDE-Library=kcm_colors +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=application-appearance +X-KDE-Weight=60 + +Name=Colors +Name[af]=Kleure +Name[ar]=الألوان +Name[ast]=Colores +Name[be]=Колеры +Name[be@latin]=Kolery +Name[bg]=Цветове +Name[bn]=রং +Name[bn_IN]=রং +Name[br]=Livioù +Name[bs]=Boje +Name[ca]=Colors +Name[ca@valencia]=Colors +Name[cs]=Barvy +Name[csb]=Farwë +Name[cy]=Lliwiau +Name[da]=Farver +Name[de]=Farben +Name[el]=Χρώματα +Name[en_GB]=Colours +Name[eo]=Koloroj +Name[es]=Colores +Name[et]=Värvid +Name[eu]=Koloreak +Name[fa]=رنگها +Name[fi]=Värit +Name[fr]=Couleurs +Name[fy]=Kleuren +Name[ga]=Dathanna +Name[gl]=Cores +Name[gu]=રંગો +Name[he]=צבעים +Name[hi]=रंग +Name[hne]=रंग +Name[hr]=Boje +Name[hsb]=Barby +Name[hu]=Színek +Name[ia]=Colores +Name[id]=Warna +Name[is]=Litir +Name[it]=Colori +Name[ja]=色 +Name[ka]=ცვეტები +Name[kk]=Түстер +Name[km]=ពណ៌ +Name[kn]=ಬಣ್ಣಗಳು +Name[ko]=색 +Name[ku]=Reng +Name[lt]=Spalvos +Name[lv]=Krāsas +Name[mai]=रँग +Name[mk]=Бои +Name[ml]=നിറങ്ങള്‍ +Name[mr]=रंग +Name[ms]=Warna +Name[nb]=Farger +Name[nds]=Klören +Name[ne]=रङ +Name[nl]=Kleuren +Name[nn]=Fargar +Name[oc]=Colors +Name[or]=ରଙ୍ଗ +Name[pa]=ਰੰਗ +Name[pl]=Kolory +Name[pt]=Cores +Name[pt_BR]=Cores +Name[ro]=Culori +Name[ru]=Цвета +Name[se]=Ivnnit +Name[si]=වර්‍ණ +Name[sk]=Farby +Name[sl]=Barve +Name[sr]=Боје +Name[sr@ijekavian]=Боје +Name[sr@ijekavianlatin]=Boje +Name[sr@latin]=Boje +Name[sv]=Färger +Name[ta]=வண்ணங்கள் +Name[te]=రంగులు +Name[tg]=Рангҳо +Name[th]=สี +Name[tr]=Renkler +Name[ug]=رەڭلەر +Name[uk]=Кольори +Name[uz]=Ranglar +Name[uz@cyrillic]=Ранглар +Name[vi]=Màu sắc +Name[wa]=Coleurs +Name[xh]=Imibala +Name[x-test]=xxColorsxx +Name[zh_CN]=颜色 +Name[zh_TW]=顏色 + +Comment=Change the colors used in applications +Comment[bs]=Promjeni boje koristene u aplikaciji +Comment[ca]=Canvia els colors utilitzats en les aplicacions +Comment[ca@valencia]=Canvia els colors utilitzats en les aplicacions +Comment[cs]=Změňte barvy použité v aplikacích +Comment[da]=Ændr de farver der bruges i programmer +Comment[de]=Änderung der von Programmen verwendeten Farben +Comment[el]=Αλλαγή των χρωμάτων που χρησιμοποιούνται στις εφαρμογές +Comment[es]=Cambiar los colores que usan las aplicaciones +Comment[et]=Rakendustes kasutatavate värvide muutmine +Comment[eu]=Aldatu aplikazioetan erabiltzen diren koloreak +Comment[fi]=Sovellusten värien asetukset +Comment[fr]=Modifier les couleurs utilisées dans les applications +Comment[gl]=Cambia as cores usadas nos programas +Comment[he]=שנה את הצבעים בשימוש ביישומים +Comment[hu]=Az alkalmazásokban használt színek megváltoztatása +Comment[ia]=Modifica le colores usate in applicationes +Comment[it]=Cambia il colore usato nelle applicazioni +Comment[kk]=Қолданбада пайдаланатын түстерін өзгерту +Comment[km]=ផ្លាស់ប្ដូរ​ពណ៌​ដែល​បាន​ប្រើ​ក្នុង​កម្មវិធី +Comment[ko]=프로그램에 사용되는 색을 변경합니다 +Comment[lt]=Keisti spalvas naudojamas programose +Comment[mr]=अनुप्रयोगांत वापरण्यात आलेले रंग बदला +Comment[nb]=Endre fargene som brukes i programmer +Comment[nds]=De Programmklören ännern +Comment[nl]=De in toepassingen gebruikte kleuren wijzigen +Comment[pa]=ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਵਰਤੇ ਜਾਂਦੇ ਰੰਗ ਬਦਲੋ +Comment[pl]=Zmiana kolorów używanych w programach +Comment[pt]=Mudar as cores usadas nas aplicações +Comment[pt_BR]=Alterar as cores usadas nos aplicativos +Comment[ro]=Schimbă culorile folosite în aplicații +Comment[ru]=Изменение цветов, используемых в приложениях +Comment[sk]=Zmeniť farby použité v aplikáciách +Comment[sl]=Spremenite barve, ki se uporabljajo v programih +Comment[sr]=Промените боје коришћене у програмима +Comment[sr@ijekavian]=Промените боје коришћене у програмима +Comment[sr@ijekavianlatin]=Promenite boje korišćene u programima +Comment[sr@latin]=Promenite boje korišćene u programima +Comment[sv]=Ändra färgerna som används av program +Comment[tr]=Uygulamalarda kullanılacak renkleri yapılandır +Comment[uk]=Зміна кольорів, що використовуються у інтерфейсах програм +Comment[vi]=Thay đổi màu sắc được sử dụng trong các ứng dụng +Comment[x-test]=xxChange the colors used in applicationsxx +Comment[zh_CN]=更改应用程序所使用的颜色 +Comment[zh_TW]=變更用於應用程式的顏色 + +X-KDE-Keywords=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme +X-KDE-Keywords[bs]=boje,boje,tema,kontrast,dodatak bojama,tema u boji,stil boja,tema boja +X-KDE-Keywords[ca]=colors,esquema,contrast,colors d'estris,Esquema de color,estil de color, tema de color +X-KDE-Keywords[ca@valencia]=colors,esquema,contrast,colors d'estris,Esquema de color,estil de color, tema de color +X-KDE-Keywords[da]=farver,tema,kontrast,widget-farver,farvetema,farvestil,farveskema +X-KDE-Keywords[de]=Farben,Schema,Kontrast,Farbschema,Elemente +X-KDE-Keywords[el]=χρώματα,χρώματα,σχήμα,αντίθεση,χρώματα γραφικών συστατικών,χρωματικό σχήμα,χρωματικό στιλ,θέμα χρώματος +X-KDE-Keywords[es]=colores,esquema,contraste,colores de elementos gráficos,Esquema de color,estilo de color,tema de color +X-KDE-Keywords[et]=värv,värvid,skeem,kontrast,vidina värvid,värviskeem,värvistiil,värviteema +X-KDE-Keywords[eu]=kolore,koloreak,eskema,kontraste,trepetaren koloreak,kolore-eskema,kolore-estilo, kolorearen gai +X-KDE-Keywords[fi]=värit,teema,kontrasti,käyttöliittymäelementtien värit,elementtien värit,väriteema,värityyli +X-KDE-Keywords[fr]=couleurs, couleurs, schéma, contraste, couleur des composants graphiques, schéma de couleur, style de couleur, thème de couleur +X-KDE-Keywords[ga]=dathanna,scéim,codarsnacht,dathanna Giuirléidí,Scéim Datha,téama datha +X-KDE-Keywords[gl]=cores,esquema,contraste,cores do widget,esquema de cores, tema de cores +X-KDE-Keywords[hu]=színek,színek,séma,kontraszt,Grafikai elemek színei,Színséma,színstílus,színtéma +X-KDE-Keywords[ia]=colores,colores,schema,contrasto,colores de Widget,Schema de Color,stilo de color, thema de color +X-KDE-Keywords[it]=colori,schema,contrasto,colore degli oggetti,schema di colore,stile colore,tema colore +X-KDE-Keywords[kk]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme +X-KDE-Keywords[km]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme +X-KDE-Keywords[ko]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,색,색 배열,고대비 +X-KDE-Keywords[mr]=रंग, रंग, योजना, कॉन्ट्रास्ट, विजेट रंग, रंगयोजना, रंगप्रकार, रंगशैली +X-KDE-Keywords[nb]=farger,oppsett,kontrast,elementfarger,fargeoppsett,fargestil,fargetema +X-KDE-Keywords[nds]=Klöör,Klören,Schema,Kontrast,Lüttprogramm-Klören,Klöörschema,Klöörstil,Klöörmuster +X-KDE-Keywords[nl]=colors,colours, kleuren,scheme,schema,contrast,Widget colors,Widgetkleuren,Color Scheme,kleurschema,kleurstijl,kleurthema +X-KDE-Keywords[pl]=kolory,schemat,kontrast,kolory elementów interfejsu,zestaw kolorów,styl kolorów,motyw kolorów +X-KDE-Keywords[pt]=cores,esquema,contraste,cores dos elementos,esquema de cores,estilo de cores,tema de cores +X-KDE-Keywords[pt_BR]=cor,cores,esquema,contraste,Cores do widget,Esquema de cores,estilo de cores,tema de cores +X-KDE-Keywords[ru]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,цвет,цвета,схема,контраст,цвета виджета,цветовая схема,цветовой стиль,цветовая тема +X-KDE-Keywords[sk]=farba,farby,schéma,kontrast,farby widgetov,Farebná schéma,štýl farieb,téma farieb +X-KDE-Keywords[sl]=barve,shema,tema,kontrast,barve gradnikov,barvna shema,barvna tema,barvni slog +X-KDE-Keywords[sr]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,боје,шема,контраст,боје виџета,шема боја,стил боја,тема боја +X-KDE-Keywords[sr@ijekavian]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,боје,шема,контраст,боје виџета,шема боја,стил боја,тема боја +X-KDE-Keywords[sr@ijekavianlatin]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,boje,šema,kontrast,boje vidžeta,šema boja,stil boja,tema boja +X-KDE-Keywords[sr@latin]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,boje,šema,kontrast,boje vidžeta,šema boja,stil boja,tema boja +X-KDE-Keywords[sv]=färger,schema,kontrast,Komponentfärger,Färgschema,färgstil,färgtema +X-KDE-Keywords[tr]=renkler,renk,şema,karşıtlık,kontrast,Parçacık renkleri,RenkŞeması,renk biçimi,renk teması +X-KDE-Keywords[uk]=кольори,кольори,схема,контраст,кольори віджетів,схема кольорів,стиль кольорів,тема кольорів,colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme +X-KDE-Keywords[x-test]=xxcolors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color themexx +X-KDE-Keywords[zh_CN]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,颜色,配色方案,对比度,部件颜色,颜色方案,颜色风格,颜色主题 +X-KDE-Keywords[zh_TW]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme + +Categories=Qt;KDE;X-KDE-settings-looknfeel; diff --git a/kcms/colors/colorschemes.knsrc b/kcms/colors/colorschemes.knsrc new file mode 100644 index 0000000000..cc4852d641 --- /dev/null +++ b/kcms/colors/colorschemes.knsrc @@ -0,0 +1,6 @@ +[KNewStuff3] +ProvidersUrl=http://download.kde.org/ocs/providers.xml +TargetDir=color-schemes +Uncompress=archive +Categories=KDE Color Scheme KDE4 +UploadCategories=KDE Color Scheme KDE4 diff --git a/kcms/colors/colorscm.cpp b/kcms/colors/colorscm.cpp new file mode 100644 index 0000000000..c38811bc78 --- /dev/null +++ b/kcms/colors/colorscm.cpp @@ -0,0 +1,1470 @@ +/* KDE Display color scheme setup module + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Jeremy Whiting + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "colorscm.h" + +#include "../krdb/krdb.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY( KolorFactory, registerPlugin(); ) +K_EXPORT_PLUGIN( KolorFactory("kcmcolors") ) + +//BEGIN WindecoColors +KColorCm::WindecoColors::WindecoColors(const KSharedConfigPtr &config) +{ + load(config); +} + +void KColorCm::WindecoColors::load(const KSharedConfigPtr &config) +{ + // NOTE: keep this in sync with kdelibs/kdeui/kernel/kglobalsettings.cpp + KConfigGroup group(config, "WM"); + m_colors[ActiveBackground] = group.readEntry("activeBackground", QColor(48, 174, 232)); + m_colors[ActiveForeground] = group.readEntry("activeForeground", QColor(255, 255, 255)); + m_colors[InactiveBackground] = group.readEntry("inactiveBackground", QColor(224, 223, 222)); + m_colors[InactiveForeground] = group.readEntry("inactiveForeground", QColor(75, 71, 67)); + m_colors[ActiveBlend] = group.readEntry("activeBlend", m_colors[ActiveForeground]); + m_colors[InactiveBlend] = group.readEntry("inactiveBlend", m_colors[InactiveForeground]); +} + +QColor KColorCm::WindecoColors::color(WindecoColors::Role role) const +{ + return m_colors[role]; +} +//END WindecoColors + +KColorCm::KColorCm(QWidget *parent, const QVariantList &) + : KCModule( parent ), m_disableUpdates(false), + m_loadedSchemeHasUnsavedChanges(false), m_dontLoadSelectedScheme(false), + m_previousSchemeItem(0) +{ + KAboutData* about = new KAboutData( + QStringLiteral("kcmcolors"), QString(), i18n("Colors"), QString(), QString(), + KAboutData::License_GPL, + i18n("(c) 2007 Matthew Woehlke") + ); + about->addAuthor( i18n("Matthew Woehlke"), QString(), + QStringLiteral("mw_triad@users.sourceforge.net") ); + about->addAuthor( i18n("Jeremy Whiting"), QString(), QStringLiteral("jpwhiting@kde.org")); + setAboutData( about ); + + m_config = KSharedConfig::openConfig("kdeglobals"); + + setupUi(this); + schemeKnsButton->setIcon( QIcon::fromTheme("get-hot-new-stuff") ); + schemeKnsUploadButton->setIcon( QIcon::fromTheme("get-hot-new-stuff") ); + connect(colorSet, SIGNAL(currentIndexChanged(int)), this, SLOT(updateColorTable())); + connect(schemeList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(loadScheme(QListWidgetItem*,QListWidgetItem*))); + connect(applyToAlien, SIGNAL(toggled(bool)), this, SLOT(emitChanged())); + + // only needs to be called once + setupColorTable(); +} + +KColorCm::~KColorCm() +{ + m_config->markAsClean(); +} + +void KColorCm::populateSchemeList() +{ + // clear the list in case this is being called from reset button click + schemeList->clear(); + + // add entries + QIcon icon; + const QStringList schemeFiles = KGlobal::dirs()->findAllResources("data", "color-schemes/*.colors", KStandardDirs::NoDuplicates); + for (int i = 0; i < schemeFiles.size(); ++i) + { + // get the file name + const QString filename = schemeFiles.at(i); + const QFileInfo info(filename); + + // add the entry + KSharedConfigPtr config = KSharedConfig::openConfig(filename); + icon = createSchemePreviewIcon(config); + KConfigGroup group(config, "General"); + const QString name = group.readEntry("Name", info.baseName()); + QListWidgetItem * newItem = new QListWidgetItem(icon, name); + // stash the file basename for use later + newItem->setData(Qt::UserRole, info.baseName()); + schemeList->addItem(newItem); + } + schemeList->sortItems(); + + // add default entry (do this here so that the current and default entry appear at the top) + m_config->setReadDefaults(true); + icon = createSchemePreviewIcon(m_config); + schemeList->insertItem(0, new QListWidgetItem(icon, i18nc("Default color scheme", "Default"))); + m_config->setReadDefaults(false); + + // add current scheme entry + icon = createSchemePreviewIcon(m_config); + QListWidgetItem *currentitem = new QListWidgetItem(icon, i18nc("Current color scheme", "Current")); + schemeList->insertItem(0, currentitem); + schemeList->blockSignals(true); // don't emit changed signals + schemeList->setCurrentItem(currentitem); + schemeList->blockSignals(false); +} + +void KColorCm::updatePreviews() +{ + schemePreview->setPalette(m_config); + colorPreview->setPalette(m_config); + setPreview->setPalette(m_config, (KColorScheme::ColorSet)(colorSet->currentIndex() - 1)); + inactivePreview->setPalette(m_config, QPalette::Inactive); + disabledPreview->setPalette(m_config, QPalette::Disabled); +} + +void KColorCm::updateEffectsPage() +{ + m_disableUpdates = true; + + // NOTE: keep this in sync with kdelibs/kdeui/colors/kcolorscheme.cpp + KConfigGroup groupI(m_config, "ColorEffects:Inactive"); + inactiveIntensityBox->setCurrentIndex(abs(groupI.readEntry("IntensityEffect", 0))); + inactiveIntensitySlider->setValue(int(groupI.readEntry("IntensityAmount", 0.0) * 20.0) + 20); + inactiveColorBox->setCurrentIndex(abs(groupI.readEntry("ColorEffect", 2))); + if (inactiveColorBox->currentIndex() > 1) + { + inactiveColorSlider->setValue(int(groupI.readEntry("ColorAmount", 0.025) * 40.0)); + } + else + { + inactiveColorSlider->setValue(int(groupI.readEntry("ColorAmount", 0.05) * 20.0) + 20); + } + inactiveColorButton->setColor(groupI.readEntry("Color", QColor(112, 111, 110))); + inactiveContrastBox->setCurrentIndex(abs(groupI.readEntry("ContrastEffect", 2))); + inactiveContrastSlider->setValue(int(groupI.readEntry("ContrastAmount", 0.1) * 20.0)); + + // NOTE: keep this in sync with kdelibs/kdeui/colors/kcolorscheme.cpp + KConfigGroup groupD(m_config, "ColorEffects:Disabled"); + disabledIntensityBox->setCurrentIndex(groupD.readEntry("IntensityEffect", 2)); + disabledIntensitySlider->setValue(int(groupD.readEntry("IntensityAmount", 0.1) * 20.0) + 20); + disabledColorBox->setCurrentIndex(groupD.readEntry("ColorEffect", 0)); + if (disabledColorBox->currentIndex() > 1) + { + disabledColorSlider->setValue(int(groupD.readEntry("ColorAmount", 0.0) * 40.0)); + } + else + { + disabledColorSlider->setValue(int(groupD.readEntry("ColorAmount", 0.0) * 20.0) + 20); + } + disabledColorButton->setColor(groupD.readEntry("Color", QColor(56, 56, 56))); + disabledContrastBox->setCurrentIndex(groupD.readEntry("ContrastEffect", 1)); + disabledContrastSlider->setValue(int(groupD.readEntry("ContrastAmount", 0.65) * 20.0)); + + m_disableUpdates = false; + + // enable/disable controls + inactiveIntensitySlider->setDisabled(inactiveIntensityBox->currentIndex() == 0); + disabledIntensitySlider->setDisabled(disabledIntensityBox->currentIndex() == 0); + inactiveColorSlider->setDisabled(inactiveColorBox->currentIndex() == 0); + disabledColorSlider->setDisabled(disabledColorBox->currentIndex() == 0); + inactiveColorButton->setDisabled(inactiveColorBox->currentIndex() < 2); + disabledColorButton->setDisabled(disabledColorBox->currentIndex() < 2); + inactiveContrastSlider->setDisabled(inactiveContrastBox->currentIndex() == 0); + disabledContrastSlider->setDisabled(disabledContrastBox->currentIndex() == 0); +} + +void KColorCm::loadScheme(KSharedConfigPtr config) // const QString &path) +{ + KSharedConfigPtr temp = m_config; + m_config = config; + + updateColorSchemes(); + updateEffectsPage(); // intentionally before swapping back m_config + updatePreviews(); + + m_config = temp; + updateFromColorSchemes(); + updateFromEffectsPage(); + updateFromOptions(); + updateColorTable(); + + m_loadedSchemeHasUnsavedChanges = false; + //m_changed = false; +} + +void KColorCm::selectPreviousSchemeAgain() +{ + m_dontLoadSelectedScheme = true; + schemeList->setCurrentItem(m_previousSchemeItem); + m_dontLoadSelectedScheme = false; +} + +void KColorCm::loadScheme(QListWidgetItem *currentItem, QListWidgetItem *previousItem) +{ + m_previousSchemeItem = previousItem; + + if (m_dontLoadSelectedScheme) + { + return; + } + + if (currentItem != NULL) + { + if (m_loadedSchemeHasUnsavedChanges) // if changes made to loaded scheme + { + if (KMessageBox::Continue != KMessageBox::warningContinueCancel(this, + i18n("Selecting another scheme will discard any changes you have made"), + i18n("Are you sure?"), + KStandardGuiItem::cont(), + KStandardGuiItem::cancel(), + "noDiscardWarning")) // if the user decides to cancel + { + QTimer::singleShot(0, this, SLOT(selectPreviousSchemeAgain())); + return; + } + } + + // load it + + const QString name = currentItem->text(); + m_currentColorScheme = name; + const QString fileBaseName = currentItem->data(Qt::UserRole).toString(); + if (name == i18nc("Default color scheme", "Default")) + { + schemeRemoveButton->setEnabled(false); + schemeKnsUploadButton->setEnabled(false); + + KSharedConfigPtr config = m_config; + config->setReadDefaults(true); + loadScheme(config); + config->setReadDefaults(false); + // load the default scheme + emit changed(true); + } + else if (name == i18nc("Current color scheme", "Current")) + { + schemeRemoveButton->setEnabled(false); + schemeKnsUploadButton->setEnabled(false); + loadInternal(false); + } + else + { + const QString path = KGlobal::dirs()->findResource("data", + "color-schemes/" + fileBaseName + ".colors"); + + const int permissions = QFile(path).permissions(); + const bool canWrite = (permissions & QFile::WriteUser); + kDebug() << "checking permissions of " << path; + schemeRemoveButton->setEnabled(canWrite); + schemeKnsUploadButton->setEnabled(true); + + KSharedConfigPtr config = KSharedConfig::openConfig(path); + loadScheme(config); + + emit changed(true); + } + } +} + +void KColorCm::on_schemeRemoveButton_clicked() +{ + if (schemeList->currentItem() != NULL) + { + const QString path = KGlobal::dirs()->findResource("data", + "color-schemes/" + schemeList->currentItem()->data(Qt::UserRole).toString() + + ".colors"); + if (KIO::NetAccess::del(QUrl::fromLocalFile(path), this)) + { + delete schemeList->takeItem(schemeList->currentRow()); + } + else + { + // deletion failed, so show an error message + KMessageBox::error(this, i18n("You do not have permission to delete that scheme"), i18n("Error")); + } + } +} + +void KColorCm::on_schemeImportButton_clicked() +{ + // get the path to the scheme to import + QUrl url = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, i18n("Import Color Scheme"))); + + if(url.isValid()) + { + // TODO: possibly untar or uncompress it + // open it + + // load the scheme + KSharedConfigPtr config = KSharedConfig::openConfig(url.path()); + + if (config->groupList().contains("Color Scheme")) + { + if (KMessageBox::Continue != KMessageBox::warningContinueCancel(this, + i18n("The scheme you have selected appears to be a KDE3 scheme.\n\n" + "KDE will attempt to import this scheme, however many color roles have been added since KDE3. " + "Some manual work will likely be required.\n\n" + "This scheme will not be saved automatically."), + i18n("Notice"))) + { + return; + } + + // convert KDE3 scheme to KDE4 scheme + KConfigGroup g(config, "Color Scheme"); + + colorSet->setCurrentIndex(0); + contrastSlider->setValue(g.readEntry("contrast", KGlobalSettings::contrast())); + shadeSortedColumn->setChecked(g.readEntry("shadeSortColumn", KGlobalSettings::shadeSortColumn())); + + m_commonColorButtons[0]->setColor(g.readEntry("windowBackground", m_colorSchemes[KColorScheme::View].background().color())); + m_commonColorButtons[1]->setColor(g.readEntry("windowForeground", m_colorSchemes[KColorScheme::View].foreground().color())); + m_commonColorButtons[2]->setColor(g.readEntry("background", m_colorSchemes[KColorScheme::Window].background().color())); + m_commonColorButtons[3]->setColor(g.readEntry("foreground", m_colorSchemes[KColorScheme::Window].foreground().color())); + m_commonColorButtons[4]->setColor(g.readEntry("buttonBackground", m_colorSchemes[KColorScheme::Button].background().color())); + m_commonColorButtons[5]->setColor(g.readEntry("buttonForeground", m_colorSchemes[KColorScheme::Button].foreground().color())); + m_commonColorButtons[6]->setColor(g.readEntry("selectBackground", m_colorSchemes[KColorScheme::Selection].background().color())); + m_commonColorButtons[7]->setColor(g.readEntry("selectForeground", m_colorSchemes[KColorScheme::Selection].foreground().color())); + m_commonColorButtons[8]->setColor(KColorUtils::mix(m_colorSchemes[KColorScheme::Selection].foreground().color(), + m_colorSchemes[KColorScheme::Selection].background().color())); + m_commonColorButtons[9]->setColor(KColorUtils::mix(m_colorSchemes[KColorScheme::View].foreground().color(), + m_colorSchemes[KColorScheme::View].background().color())); + // doesn't exist in KDE3: 10 ActiveText + m_commonColorButtons[11]->setColor(g.readEntry("linkColor", m_colorSchemes[KColorScheme::View].foreground(KColorScheme::LinkText).color())); + m_commonColorButtons[12]->setColor(g.readEntry("visitedLinkColor", m_colorSchemes[KColorScheme::View].foreground(KColorScheme::VisitedText).color())); + // doesn't exist in KDE3: 13-15 PositiveText, NeutralText, NegativeText + m_commonColorButtons[16]->setColor(g.readEntry("windowForeground", m_colorSchemes[KColorScheme::View].decoration(KColorScheme::FocusColor).color())); + m_commonColorButtons[17]->setColor(g.readEntry("selectBackground", m_colorSchemes[KColorScheme::View].decoration(KColorScheme::HoverColor).color())); + m_commonColorButtons[18]->setColor(g.readEntry("windowBackground", m_colorSchemes[KColorScheme::Tooltip].background().color())); + m_commonColorButtons[19]->setColor(g.readEntry("windowForeground", m_colorSchemes[KColorScheme::Tooltip].foreground().color())); + m_commonColorButtons[20]->setColor(g.readEntry("activeBackground", m_wmColors.color(WindecoColors::ActiveBackground))); + m_commonColorButtons[21]->setColor(g.readEntry("activeForeground", m_wmColors.color(WindecoColors::ActiveForeground))); + m_commonColorButtons[22]->setColor(g.readEntry("activeBlend", m_wmColors.color(WindecoColors::ActiveBlend))); + m_commonColorButtons[23]->setColor(g.readEntry("inactiveBackground", m_wmColors.color(WindecoColors::InactiveBackground))); + m_commonColorButtons[24]->setColor(g.readEntry("inactiveForeground", m_wmColors.color(WindecoColors::InactiveForeground))); + m_commonColorButtons[25]->setColor(g.readEntry("inactiveBlend", m_wmColors.color(WindecoColors::InactiveBlend))); + + colorSet->setCurrentIndex(1); + m_backgroundButtons[KColorScheme::AlternateBackground]->setColor(g.readEntry("alternateBackground", + m_colorSchemes[KColorScheme::View].background(KColorScheme::AlternateBackground).color())); + colorSet->setCurrentIndex(0); + } + else + { + // load KDE4 scheme + loadScheme(config); + + // save it + saveScheme(url.fileName()); + } + } +} + +void KColorCm::on_schemeKnsButton_clicked() +{ + KNS3::DownloadDialog dialog("colorschemes.knsrc", this); + dialog.exec(); + if ( ! dialog.changedEntries().isEmpty() ) + { + populateSchemeList(); + } +} + +void KColorCm::on_schemeKnsUploadButton_clicked() +{ + if (schemeList->currentItem() != NULL) + { + // check if the currently loaded scheme has unsaved changes + if (m_loadedSchemeHasUnsavedChanges) + { + KMessageBox::sorry(this, i18n("Please save the color scheme before uploading it."), + i18n("Please save")); + return; + } + + // find path + const QString basename = schemeList->currentItem()->data(Qt::UserRole).toString(); + const QString path = KGlobal::dirs()->findResource("data", + "color-schemes/" + basename + ".colors"); + if (path.isEmpty() ) // if the color scheme file wasn't found + { + kDebug() << "path for color scheme " << basename << " couldn't be found"; + return; + } + + // upload + KNS3::UploadDialog dialog("colorschemes.knsrc", this); + dialog.setUploadFile(QUrl::fromLocalFile(path) ); + dialog.exec(); + } +} + +void KColorCm::on_schemeSaveButton_clicked() +{ + QString previousName; + if (schemeList->currentItem() != NULL && schemeList->currentRow() > 1) + { + previousName = schemeList->currentItem()->data(Qt::DisplayRole).toString(); + } + // prompt for the name to save as + bool ok; + QString name = KInputDialog::getText(i18n("Save Color Scheme"), + i18n("&Enter a name for the color scheme:"), previousName, &ok, this); + if (ok) + { + saveScheme(name); + } +} + +void KColorCm::saveScheme(const QString &name) +{ + QString filename = name; + filename.remove('\''); // So Foo's does not become FooS + QRegExp fixer("[\\W,.-]+(.?)"); + int offset; + while ((offset = fixer.indexIn(filename)) >= 0) + filename.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); + filename.replace(0, 1, filename.at(0).toUpper()); + + // check if that name is already in the list + const QString path = KGlobal::dirs()->saveLocation("data", "color-schemes/") + + filename + ".colors"; + + QFile file(path); + const int permissions = file.permissions(); + const bool canWrite = (permissions & QFile::WriteUser); + // or if we can overwrite it if it exists + if (path.isEmpty() || !file.exists() || canWrite) + { + if(canWrite){ + int ret = KMessageBox::questionYesNo(this, + i18n("A color scheme with that name already exists.\nDo you want to overwrite it?"), + i18n("Save Color Scheme"), + KStandardGuiItem::overwrite(), + KStandardGuiItem::cancel()); + //on don't overwrite, select the already existing name. + if(ret == KMessageBox::No){ + QList foundItems = schemeList->findItems(name, Qt::MatchExactly); + if (foundItems.size() == 1) + schemeList->setCurrentRow(schemeList->row(foundItems[0])); + return; + } + } + + // go ahead and save it + QString newpath = KGlobal::dirs()->saveLocation("data", "color-schemes/"); + newpath += filename + ".colors"; + KSharedConfigPtr temp = m_config; + m_config = KSharedConfig::openConfig(newpath); + // then copy current colors into new config + updateFromColorSchemes(); + updateFromEffectsPage(); + KConfigGroup group(m_config, "General"); + group.writeEntry("Name", name); + // sync it + m_config->sync(); + + m_loadedSchemeHasUnsavedChanges = false; + + QList foundItems = schemeList->findItems(name, Qt::MatchExactly); + QIcon icon = createSchemePreviewIcon(m_config); + if (foundItems.size() < 1) + { + // add it to the list since it's not in there already + populateSchemeList(); + + // then select the new item + schemeList->setCurrentItem(schemeList->findItems(name, Qt::MatchExactly).at(0)); + } + else + { + // update the icon of the one that's in the list + foundItems[0]->setIcon(icon); + schemeList->setCurrentRow(schemeList->row(foundItems[0])); + } + + // set m_config back to the system one + m_config = temp; + + // store colorscheme name in global settings + m_currentColorScheme = name; + group = KConfigGroup(m_config, "General"); + group.writeEntry("ColorScheme", m_currentColorScheme); + + emit changed(true); + } + else if (!canWrite && file.exists()) + { + // give error message if !canWrite && file.exists() + KMessageBox::error(this, i18n("You do not have permission to overwrite that scheme"), i18n("Error")); + } +} + +void KColorCm::createColorEntry(const QString &text, const QString &key, QList &list, int index) +{ + KColorButton *button = new KColorButton(this); + button->setObjectName(QString::number(index)); + connect(button, SIGNAL(changed(QColor)), this, SLOT(colorChanged(QColor))); + list.append(button); + + m_colorKeys.insert(index, key); + + QTableWidgetItem *label = new QTableWidgetItem(text); + colorTable->setItem(index, 0, label); + colorTable->setCellWidget(index, 1, button); + colorTable->setRowHeight(index, button->sizeHint().height()); +} + +void KColorCm::variesClicked() +{ + // find which button was changed + const int row = sender()->objectName().toInt(); + + QColor color; + if(KColorDialog::getColor(color, this ) != QDialog::Rejected ) + { + changeColor(row, color); + m_stackedWidgets[row - 9]->setCurrentIndex(0); + } +} + +QPixmap KColorCm::createSchemePreviewIcon(const KSharedConfigPtr &config) +{ + const WindecoColors wm(config); + const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b }; + const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff }; + const QSize bitsSize(24,2); + const QBitmap b1 = QBitmap::fromData(bitsSize, bits1); + const QBitmap b2 = QBitmap::fromData(bitsSize, bits2); + + QPixmap pixmap(23, 16); + pixmap.fill(Qt::black); // ### use some color other than black for borders? + + QPainter p(&pixmap); + + KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config); + p.fillRect( 1, 1, 7, 7, windowScheme.background()); + p.fillRect( 2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1)); + + KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config); + p.fillRect( 8, 1, 7, 7, buttonScheme.background()); + p.fillRect( 9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1)); + + p.fillRect(15, 1, 7, 7, wm.color(WindecoColors::ActiveBackground)); + p.fillRect(16, 2, 5, 2, QBrush(wm.color(WindecoColors::ActiveForeground), b1)); + + KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config); + p.fillRect( 1, 8, 7, 7, viewScheme.background()); + p.fillRect( 2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2)); + + KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config); + p.fillRect( 8, 8, 7, 7, selectionScheme.background()); + p.fillRect( 9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2)); + + p.fillRect(15, 8, 7, 7, wm.color(WindecoColors::InactiveBackground)); + p.fillRect(16, 12, 5, 2, QBrush(wm.color(WindecoColors::InactiveForeground), b2)); + + p.end(); + + return pixmap; +} + +void KColorCm::updateColorSchemes() +{ + m_colorSchemes.clear(); + + m_colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::View, m_config)); + m_colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Window, m_config)); + m_colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Button, m_config)); + m_colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Selection, m_config)); + m_colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Tooltip, m_config)); + + m_wmColors.load(m_config); +} + +void KColorCm::updateFromColorSchemes() +{ + // store colorscheme name in global settings + KConfigGroup group(m_config, "General"); + group.writeEntry("ColorScheme", m_currentColorScheme); + + for (int i = KColorScheme::View; i <= KColorScheme::Tooltip; ++i) + { + KConfigGroup group(m_config, colorSetGroupKey(i)); + group.writeEntry("BackgroundNormal", m_colorSchemes[i].background(KColorScheme::NormalBackground).color()); + group.writeEntry("BackgroundAlternate", m_colorSchemes[i].background(KColorScheme::AlternateBackground).color()); + group.writeEntry("ForegroundNormal", m_colorSchemes[i].foreground(KColorScheme::NormalText).color()); + group.writeEntry("ForegroundInactive", m_colorSchemes[i].foreground(KColorScheme::InactiveText).color()); + group.writeEntry("ForegroundActive", m_colorSchemes[i].foreground(KColorScheme::ActiveText).color()); + group.writeEntry("ForegroundLink", m_colorSchemes[i].foreground(KColorScheme::LinkText).color()); + group.writeEntry("ForegroundVisited", m_colorSchemes[i].foreground(KColorScheme::VisitedText).color()); + group.writeEntry("ForegroundNegative", m_colorSchemes[i].foreground(KColorScheme::NegativeText).color()); + group.writeEntry("ForegroundNeutral", m_colorSchemes[i].foreground(KColorScheme::NeutralText).color()); + group.writeEntry("ForegroundPositive", m_colorSchemes[i].foreground(KColorScheme::PositiveText).color()); + group.writeEntry("DecorationFocus", m_colorSchemes[i].decoration(KColorScheme::FocusColor).color()); + group.writeEntry("DecorationHover", m_colorSchemes[i].decoration(KColorScheme::HoverColor).color()); + } + + KConfigGroup WMGroup(m_config, "WM"); + WMGroup.writeEntry("activeBackground", m_wmColors.color(WindecoColors::ActiveBackground)); + WMGroup.writeEntry("activeForeground", m_wmColors.color(WindecoColors::ActiveForeground)); + WMGroup.writeEntry("inactiveBackground", m_wmColors.color(WindecoColors::InactiveBackground)); + WMGroup.writeEntry("inactiveForeground", m_wmColors.color(WindecoColors::InactiveForeground)); + WMGroup.writeEntry("activeBlend", m_wmColors.color(WindecoColors::ActiveBlend)); + WMGroup.writeEntry("inactiveBlend", m_wmColors.color(WindecoColors::InactiveBlend)); +} + +void KColorCm::updateFromOptions() +{ + KConfigGroup groupK(m_config, "KDE"); + groupK.writeEntry("contrast", contrastSlider->value()); + + KConfigGroup groupG(m_config, "General"); + groupG.writeEntry("shadeSortColumn", shadeSortedColumn->isChecked()); + + KConfigGroup groupI(m_config, "ColorEffects:Inactive"); + groupI.writeEntry("Enable", useInactiveEffects->isChecked()); + // only write this setting if it is not the default; this way we can change the default more easily in later KDE + // the setting will still written by explicitly checking/unchecking the box + if (inactiveSelectionEffect->isChecked()) + { + groupI.writeEntry("ChangeSelectionColor", true); + } + else + { + groupI.deleteEntry("ChangeSelectionColor"); + } +} + +void KColorCm::updateFromEffectsPage() +{ + if (m_disableUpdates) + { + // don't write the config as we are reading it! + return; + } + + KConfigGroup groupI(m_config, "ColorEffects:Inactive"); + KConfigGroup groupD(m_config, "ColorEffects:Disabled"); + + // intensity + groupI.writeEntry("IntensityEffect", inactiveIntensityBox->currentIndex()); + groupD.writeEntry("IntensityEffect", disabledIntensityBox->currentIndex()); + groupI.writeEntry("IntensityAmount", qreal(inactiveIntensitySlider->value() - 20) * 0.05); + groupD.writeEntry("IntensityAmount", qreal(disabledIntensitySlider->value() - 20) * 0.05); + + // color + groupI.writeEntry("ColorEffect", inactiveColorBox->currentIndex()); + groupD.writeEntry("ColorEffect", disabledColorBox->currentIndex()); + if (inactiveColorBox->currentIndex() > 1) + { + groupI.writeEntry("ColorAmount", qreal(inactiveColorSlider->value()) * 0.025); + } + else + { + groupI.writeEntry("ColorAmount", qreal(inactiveColorSlider->value() - 20) * 0.05); + } + if (disabledColorBox->currentIndex() > 1) + { + groupD.writeEntry("ColorAmount", qreal(disabledColorSlider->value()) * 0.025); + } + else + { + groupD.writeEntry("ColorAmount", qreal(disabledColorSlider->value() - 20) * 0.05); + } + groupI.writeEntry("Color", inactiveColorButton->color()); + groupD.writeEntry("Color", disabledColorButton->color()); + + // contrast + groupI.writeEntry("ContrastEffect", inactiveContrastBox->currentIndex()); + groupD.writeEntry("ContrastEffect", disabledContrastBox->currentIndex()); + groupI.writeEntry("ContrastAmount", qreal(inactiveContrastSlider->value()) * 0.05); + groupD.writeEntry("ContrastAmount", qreal(disabledContrastSlider->value()) * 0.05); + + // enable/disable controls + inactiveIntensitySlider->setDisabled(inactiveIntensityBox->currentIndex() == 0); + disabledIntensitySlider->setDisabled(disabledIntensityBox->currentIndex() == 0); + inactiveColorSlider->setDisabled(inactiveColorBox->currentIndex() == 0); + disabledColorSlider->setDisabled(disabledColorBox->currentIndex() == 0); + inactiveColorButton->setDisabled(inactiveColorBox->currentIndex() < 2); + disabledColorButton->setDisabled(disabledColorBox->currentIndex() < 2); + inactiveContrastSlider->setDisabled(inactiveContrastBox->currentIndex() == 0); + disabledContrastSlider->setDisabled(disabledContrastBox->currentIndex() == 0); +} + +void KColorCm::setupColorTable() +{ + // first setup the common colors table + commonColorTable->verticalHeader()->hide(); + commonColorTable->horizontalHeader()->hide(); + commonColorTable->setShowGrid(false); + commonColorTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); + int minWidth = QPushButton(i18n("Varies")).minimumSizeHint().width(); + commonColorTable->horizontalHeader()->setMinimumSectionSize(minWidth); + commonColorTable->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents); + + for (int i = 0; i < 26; ++i) + { + KColorButton * button = new KColorButton(this); + commonColorTable->setRowHeight(i, button->sizeHint().height()); + button->setObjectName(QString::number(i)); + connect(button, SIGNAL(changed(QColor)), this, SLOT(colorChanged(QColor))); + m_commonColorButtons << button; + + if (i > 8 && i < 18) + { + // Inactive Text row through Positive Text role all need a varies button + KPushButton * variesButton = new KPushButton(NULL); + variesButton->setText(i18n("Varies")); + variesButton->setObjectName(QString::number(i)); + connect(variesButton, SIGNAL(clicked()), this, SLOT(variesClicked())); + + QStackedWidget * widget = new QStackedWidget(this); + widget->addWidget(button); + widget->addWidget(variesButton); + m_stackedWidgets.append(widget); + + commonColorTable->setCellWidget(i, 1, widget); + } + else + { + commonColorTable->setCellWidget(i, 1, button); + } + } + + // then the colorTable that the colorSets will use + colorTable->verticalHeader()->hide(); + colorTable->horizontalHeader()->hide(); + colorTable->setShowGrid(false); + colorTable->setRowCount(12); + colorTable->horizontalHeader()->setMinimumSectionSize(minWidth); + colorTable->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents); + + createColorEntry(i18n("Normal Background"), "BackgroundNormal", m_backgroundButtons, 0); + createColorEntry(i18n("Alternate Background"), "BackgroundAlternate", m_backgroundButtons, 1); + createColorEntry(i18n("Normal Text"), "ForegroundNormal", m_foregroundButtons, 2); + createColorEntry(i18n("Inactive Text"), "ForegroundInactive", m_foregroundButtons, 3); + createColorEntry(i18n("Active Text"), "ForegroundActive", m_foregroundButtons, 4); + createColorEntry(i18n("Link Text"), "ForegroundLink", m_foregroundButtons, 5); + createColorEntry(i18n("Visited Text"), "ForegroundVisited", m_foregroundButtons, 6); + createColorEntry(i18n("Negative Text"), "ForegroundNegative", m_foregroundButtons, 7); + createColorEntry(i18n("Neutral Text"), "ForegroundNeutral", m_foregroundButtons, 8); + createColorEntry(i18n("Positive Text"), "ForegroundPositive", m_foregroundButtons, 9); + createColorEntry(i18n("Focus Decoration"), "DecorationFocus", m_decorationButtons, 10); + createColorEntry(i18n("Hover Decoration"), "DecorationHover", m_decorationButtons, 11); + + colorTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); + colorTable->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents); + + updateColorSchemes(); + updateColorTable(); +} + +void KColorCm::setCommonForeground(KColorScheme::ForegroundRole role, int stackIndex, + int buttonIndex) +{ + QColor color = m_colorSchemes[KColorScheme::View].foreground(role).color(); + for (int i = KColorScheme::Window; i < KColorScheme::Tooltip; ++i) + { + if (i == KColorScheme::Selection && role == KColorScheme::InactiveText) + break; + + if (m_colorSchemes[i].foreground(role).color() != color) + { + m_stackedWidgets[stackIndex]->setCurrentIndex(1); + return; + } + } + + m_stackedWidgets[stackIndex]->setCurrentIndex(0); + m_commonColorButtons[buttonIndex]->setColor(color); + m_loadedSchemeHasUnsavedChanges = true; +} + +void KColorCm::setCommonDecoration(KColorScheme::DecorationRole role, int stackIndex, + int buttonIndex) +{ + QColor color = m_colorSchemes[KColorScheme::View].decoration(role).color(); + for (int i = KColorScheme::Window; i < KColorScheme::Tooltip; ++i) + { + if (m_colorSchemes[i].decoration(role).color() != color) + { + m_stackedWidgets[stackIndex]->setCurrentIndex(1); + return; + } + } + + m_stackedWidgets[stackIndex]->setCurrentIndex(0); + m_commonColorButtons[buttonIndex]->setColor(color); + m_loadedSchemeHasUnsavedChanges = true; +} + +void KColorCm::updateColorTable() +{ + // subtract one here since the 0 item is "Common Colors" + const int currentSet = colorSet->currentIndex() - 1; + + if (currentSet == -1) + { + // common colors is selected + stackColors->setCurrentIndex(0); + stackPreview->setCurrentIndex(0); + + KColorButton * button; + foreach (button, m_commonColorButtons) + { + button->blockSignals(true); + } + + m_commonColorButtons[0]->setColor(m_colorSchemes[KColorScheme::View].background(KColorScheme::NormalBackground).color()); + m_commonColorButtons[1]->setColor(m_colorSchemes[KColorScheme::View].foreground(KColorScheme::NormalText).color()); + m_commonColorButtons[2]->setColor(m_colorSchemes[KColorScheme::Window].background(KColorScheme::NormalBackground).color()); + m_commonColorButtons[3]->setColor(m_colorSchemes[KColorScheme::Window].foreground(KColorScheme::NormalText).color()); + m_commonColorButtons[4]->setColor(m_colorSchemes[KColorScheme::Button].background(KColorScheme::NormalBackground).color()); + m_commonColorButtons[5]->setColor(m_colorSchemes[KColorScheme::Button].foreground(KColorScheme::NormalText).color()); + m_commonColorButtons[6]->setColor(m_colorSchemes[KColorScheme::Selection].background(KColorScheme::NormalBackground).color()); + m_commonColorButtons[7]->setColor(m_colorSchemes[KColorScheme::Selection].foreground(KColorScheme::NormalText).color()); + m_commonColorButtons[8]->setColor(m_colorSchemes[KColorScheme::Selection].foreground(KColorScheme::InactiveText).color()); + + setCommonForeground(KColorScheme::InactiveText, 0, 9); + setCommonForeground(KColorScheme::ActiveText, 1, 10); + setCommonForeground(KColorScheme::LinkText, 2, 11); + setCommonForeground(KColorScheme::VisitedText, 3, 12); + setCommonForeground(KColorScheme::NegativeText, 4, 13); + setCommonForeground(KColorScheme::NeutralText, 5, 14); + setCommonForeground(KColorScheme::PositiveText, 6, 15); + + setCommonDecoration(KColorScheme::FocusColor, 7, 16); + setCommonDecoration(KColorScheme::HoverColor, 8, 17); + + m_commonColorButtons[18]->setColor(m_colorSchemes[KColorScheme::Tooltip].background(KColorScheme::NormalBackground).color()); + m_commonColorButtons[19]->setColor(m_colorSchemes[KColorScheme::Tooltip].foreground(KColorScheme::NormalText).color()); + + m_commonColorButtons[20]->setColor(m_wmColors.color(WindecoColors::ActiveBackground)); + m_commonColorButtons[21]->setColor(m_wmColors.color(WindecoColors::ActiveForeground)); + m_commonColorButtons[22]->setColor(m_wmColors.color(WindecoColors::ActiveBlend)); + m_commonColorButtons[23]->setColor(m_wmColors.color(WindecoColors::InactiveBackground)); + m_commonColorButtons[24]->setColor(m_wmColors.color(WindecoColors::InactiveForeground)); + m_commonColorButtons[25]->setColor(m_wmColors.color(WindecoColors::InactiveBlend)); + + foreach (button, m_commonColorButtons) + { + button->blockSignals(false); + } + } + else + { + // a real color set is selected + setPreview->setPalette(m_config, (KColorScheme::ColorSet)currentSet); + stackColors->setCurrentIndex(1); + stackPreview->setCurrentIndex(1); + + for (int i = KColorScheme::NormalBackground; i <= KColorScheme::AlternateBackground; ++i) + { + m_backgroundButtons[i]->blockSignals(true); + m_backgroundButtons[i]->setColor(m_colorSchemes[currentSet].background(KColorScheme::BackgroundRole(i)).color()); + m_backgroundButtons[i]->blockSignals(false); + } + + for (int i = KColorScheme::NormalText; i <= KColorScheme::PositiveText; ++i) + { + m_foregroundButtons[i]->blockSignals(true); + m_foregroundButtons[i]->setColor(m_colorSchemes[currentSet].foreground(KColorScheme::ForegroundRole(i)).color()); + m_foregroundButtons[i]->blockSignals(false); + } + + for (int i = KColorScheme::FocusColor; i <= KColorScheme::HoverColor; ++i) + { + m_decorationButtons[i]->blockSignals(true); + m_decorationButtons[i]->setColor(m_colorSchemes[currentSet].decoration(KColorScheme::DecorationRole(i)).color()); + m_decorationButtons[i]->blockSignals(false); + } + } +} + +void KColorCm::colorChanged( const QColor &newColor ) +{ + // find which button was changed + const int row = sender()->objectName().toInt(); + changeColor(row, newColor); +} + +void KColorCm::changeColor(int row, const QColor &newColor) +{ + // update the m_colorSchemes for the selected colorSet + const int currentSet = colorSet->currentIndex() - 1; + + if (currentSet == -1) + { + // common colors is selected + switch (row) + { + case 0: + // View Background button + KConfigGroup(m_config, "Colors:View").writeEntry("BackgroundNormal", newColor); + break; + case 1: + // View Text button + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundNormal", newColor); + break; + case 2: + // Window Background Button + KConfigGroup(m_config, "Colors:Window").writeEntry("BackgroundNormal", newColor); + break; + case 3: + // Window Text Button + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundNormal", newColor); + break; + case 4: + // Button Background button + KConfigGroup(m_config, "Colors:Button").writeEntry("BackgroundNormal", newColor); + break; + case 5: + // Button Text button + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundNormal", newColor); + break; + case 6: + // Selection Background Button + KConfigGroup(m_config, "Colors:Selection").writeEntry("BackgroundNormal", newColor); + break; + case 7: + // Selection Text Button + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundNormal", newColor); + break; + case 8: + // Selection Inactive Text Button + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundInactive", newColor); + break; + + // buttons that could have varies in their place + case 9: + // Inactive Text Button (set all but Selection Inactive Text color) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundInactive", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundInactive", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundInactive", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundInactive", newColor); + break; + case 10: + // Active Text Button (set all active text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundActive", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundActive", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundActive", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundActive", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundActive", newColor); + break; + case 11: + // Link Text Button (set all link text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundLink", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundLink", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundLink", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundLink", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundLink", newColor); + break; + case 12: + // Visited Text Button (set all visited text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundVisited", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundVisited", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundVisited", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundVisited", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundVisited", newColor); + break; + case 13: + // Negative Text Button (set all negative text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundNegative", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundNegative", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundNegative", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundNegative", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundNegative", newColor); + break; + case 14: + // Neutral Text Button (set all neutral text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundNeutral", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundNeutral", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundNeutral", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundNeutral", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundNeutral", newColor); + break; + case 15: + // Positive Text Button (set all positive text colors) + KConfigGroup(m_config, "Colors:View").writeEntry("ForegroundPositive", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("ForegroundPositive", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("ForegroundPositive", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("ForegroundPositive", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundPositive", newColor); + break; + + case 16: + // Focus Decoration Button (set all focus decoration colors) + KConfigGroup(m_config, "Colors:View").writeEntry("DecorationFocus", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("DecorationFocus", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("DecorationFocus", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("DecorationFocus", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("DecorationFocus", newColor); + break; + case 17: + // Hover Decoration Button (set all hover decoration colors) + KConfigGroup(m_config, "Colors:View").writeEntry("DecorationHover", newColor); + KConfigGroup(m_config, "Colors:Window").writeEntry("DecorationHover", newColor); + KConfigGroup(m_config, "Colors:Selection").writeEntry("DecorationHover", newColor); + KConfigGroup(m_config, "Colors:Button").writeEntry("DecorationHover", newColor); + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("DecorationHover", newColor); + break; + + case 18: + // Tooltip Background button + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("BackgroundNormal", newColor); + break; + case 19: + // Tooltip Text button + KConfigGroup(m_config, "Colors:Tooltip").writeEntry("ForegroundNormal", newColor); + break; + case 20: + // Active Title Background + KConfigGroup(m_config, "WM").writeEntry("activeBackground", newColor); + break; + case 21: + // Active Title Text + KConfigGroup(m_config, "WM").writeEntry("activeForeground", newColor); + break; + case 22: + // Active Title Secondary + KConfigGroup(m_config, "WM").writeEntry("activeBlend", newColor); + break; + case 23: + // Inactive Title Background + KConfigGroup(m_config, "WM").writeEntry("inactiveBackground", newColor); + break; + case 24: + // Inactive Title Text + KConfigGroup(m_config, "WM").writeEntry("inactiveForeground", newColor); + break; + case 25: + // Inactive Title Secondary + KConfigGroup(m_config, "WM").writeEntry("inactiveBlend", newColor); + break; + } + m_commonColorButtons[row]->blockSignals(true); + m_commonColorButtons[row]->setColor(newColor); + m_commonColorButtons[row]->blockSignals(false); + } + else + { + QString group = colorSetGroupKey(currentSet); + KConfigGroup(m_config, group).writeEntry(m_colorKeys[row], newColor); + } + + QIcon icon = createSchemePreviewIcon(m_config); + schemeList->item(0)->setIcon(icon); + updateColorSchemes(); + updatePreviews(); + + m_loadedSchemeHasUnsavedChanges = true; + m_currentColorScheme = i18nc("Current color scheme", "Current"); + KConfigGroup group(m_config, "General"); + group.writeEntry("ColorScheme", m_currentColorScheme); + schemeRemoveButton->setEnabled(false); + schemeKnsUploadButton->setEnabled(false); + schemeList->blockSignals(true); // don't emit changed signals + schemeList->setCurrentRow(0); + schemeList->blockSignals(false); + + emit changed(true); +} + +QString KColorCm::colorSetGroupKey(int colorSet) +{ + QString group; + switch (colorSet) { + case KColorScheme::Window: + group = "Colors:Window"; + break; + case KColorScheme::Button: + group = "Colors:Button"; + break; + case KColorScheme::Selection: + group = "Colors:Selection"; + break; + case KColorScheme::Tooltip: + group = "Colors:Tooltip"; + break; + default: + group = "Colors:View"; + } + return group; +} + +void KColorCm::on_contrastSlider_valueChanged(int value) +{ + KConfigGroup group(m_config, "KDE"); + group.writeEntry("contrast", value); + + updatePreviews(); + + emit changed(true); +} + +void KColorCm::on_shadeSortedColumn_stateChanged(int state) +{ + KConfigGroup group(m_config, "General"); + group.writeEntry("shadeSortColumn", bool(state != Qt::Unchecked)); + + emit changed(true); +} + +void KColorCm::on_useInactiveEffects_stateChanged(int state) +{ + KConfigGroup group(m_config, "ColorEffects:Inactive"); + group.writeEntry("Enable", bool(state != Qt::Unchecked)); + + m_disableUpdates = true; + printf("re-init\n"); + inactiveSelectionEffect->setChecked(group.readEntry("ChangeSelectionColor", bool(state != Qt::Unchecked))); + m_disableUpdates = false; + + emit changed(true); +} + +void KColorCm::on_inactiveSelectionEffect_stateChanged(int state) +{ + if (m_disableUpdates) + { + // don't write the config as we are reading it! + return; + } + + KConfigGroup group(m_config, "ColorEffects:Inactive"); + group.writeEntry("ChangeSelectionColor", bool(state != Qt::Unchecked)); + + emit changed(true); +} + +void KColorCm::load() +{ + loadInternal(true); + + // get colorscheme name from global settings + KConfigGroup group(m_config, "General"); + m_currentColorScheme = group.readEntry("ColorScheme"); + if (m_currentColorScheme == i18nc("Current color scheme", "Current")) + { + m_loadedSchemeHasUnsavedChanges = true; + } + QList itemList = schemeList->findItems(m_currentColorScheme, Qt::MatchExactly); + if(!itemList.isEmpty()) // "Current" is already selected, so don't handle the case that itemList is empty + schemeList->setCurrentItem(itemList.at(0)); + + KConfig cfg("kcmdisplayrc", KConfig::NoGlobals); + group = KConfigGroup(&cfg, "X11"); + + applyToAlien->blockSignals(true); // don't emit SIGNAL(toggled(bool)) which would call SLOT(emitChanged()) + applyToAlien->setChecked(group.readEntry("exportKDEColors", true)); + applyToAlien->blockSignals(false); +} + +void KColorCm::loadOptions() +{ + contrastSlider->setValue(KGlobalSettings::contrast()); + shadeSortedColumn->setChecked(KGlobalSettings::shadeSortColumn()); + + KConfigGroup group(m_config, "ColorEffects:Inactive"); + useInactiveEffects->setChecked(group.readEntry("Enable", false)); + // NOTE: keep this in sync with kdelibs/kdeui/colors/kcolorscheme.cpp + // NOTE: remove extra logic from updateFromOptions and on_useInactiveEffects_stateChanged when this changes! + inactiveSelectionEffect->setChecked(group.readEntry("ChangeSelectionColor", group.readEntry("Enable", true))); +} + +void KColorCm::loadInternal(bool loadOptions_) +{ + // clean the config, in case we have changed the in-memory kconfig + m_config->markAsClean(); + m_config->reparseConfiguration(); + + // update the color table + updateColorSchemes(); + updateColorTable(); + + // fill in the color scheme list + populateSchemeList(); + + if (loadOptions_) + loadOptions(); + + updateEffectsPage(); + + updatePreviews(); + + m_loadedSchemeHasUnsavedChanges = false; + + emit changed(false); +} + +void KColorCm::save() +{ + QIcon icon = createSchemePreviewIcon(m_config); + schemeList->item(0)->setIcon(icon); + + KConfigGroup groupI(m_config, "ColorEffects:Inactive"); + + groupI.writeEntry("Enable", useInactiveEffects->isChecked()); + groupI.writeEntry("IntensityEffect", inactiveIntensityBox->currentIndex()); + groupI.writeEntry("ColorEffect", inactiveColorBox->currentIndex()); + groupI.writeEntry("ContrastEffect", inactiveContrastBox->currentIndex()); + + m_config->sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::PaletteChanged); + if (qApp->platformName() == QStringLiteral("xcb")) { + // Send signal to all kwin instances + QDBusMessage message = + QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); + QDBusConnection::sessionBus().send(message); + } + + KConfig cfg("kcmdisplayrc", KConfig::NoGlobals); + KConfigGroup displayGroup(&cfg, "X11"); + + displayGroup.writeEntry("exportKDEColors", applyToAlien->isChecked()); + cfg.sync(); + + runRdb(KRdbExportQtColors | KRdbExportGtkTheme | ( applyToAlien->isChecked() ? KRdbExportColors : 0 ) ); + + emit changed(false); +} + +void KColorCm::defaults() +{ + // Switch to default scheme + for(int i = 0; i < schemeList->count(); ++i) { + QListWidgetItem *item = schemeList->item(i); + if(item->text() == i18nc("Default color scheme", "Default")) { + // If editing the default scheme, force a reload, else select the default scheme + if(schemeList->currentItem() == item) + loadScheme(item, item); + else + schemeList->setCurrentItem(item); + m_currentColorScheme = item->text(); + break; + } + } + + // Reset options (not part of scheme) + m_config->setReadDefaults(true); + loadOptions(); + m_config->setReadDefaults(false); + applyToAlien->setChecked(Qt::Checked); + + KCModule::defaults(); + emit changed(true); +} + +void KColorCm::emitChanged() +{ + emit changed(true); +} + +// inactive effects slots +void KColorCm::on_inactiveIntensityBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveIntensitySlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveColorBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveColorSlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveColorButton_changed(const QColor& color) +{ + Q_UNUSED( color ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveContrastBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_inactiveContrastSlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + inactivePreview->setPalette(m_config, QPalette::Inactive); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +// disabled effects slots +void KColorCm::on_disabledIntensityBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledIntensitySlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledColorBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledColorSlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledColorButton_changed(const QColor& color) +{ + Q_UNUSED( color ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledContrastBox_currentIndexChanged(int index) +{ + Q_UNUSED( index ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +void KColorCm::on_disabledContrastSlider_valueChanged(int value) +{ + Q_UNUSED( value ); + + updateFromEffectsPage(); + disabledPreview->setPalette(m_config, QPalette::Disabled); + + m_loadedSchemeHasUnsavedChanges = true; + + emit changed(true); +} + +#include "colorscm.moc" diff --git a/kcms/colors/colorscm.h b/kcms/colors/colorscm.h new file mode 100644 index 0000000000..376a112be2 --- /dev/null +++ b/kcms/colors/colorscm.h @@ -0,0 +1,216 @@ +/* KDE Display color scheme setup module + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Jeremy Whiting + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __COLORSCM_H__ +#define __COLORSCM_H__ + +#include +#include +#include + +#include "ui_colorsettings.h" + +class QStackedWidget; +class QListWidgetItem; + +/** + * The Desktop/Colors tab in kcontrol. + */ +class KColorCm : public KCModule, public Ui::colorSettings +{ + Q_OBJECT + +public: + KColorCm(QWidget *parent, const QVariantList &); + ~KColorCm(); + +public Q_SLOTS: + + /// load the settings from the config + virtual void load(); + + /// save the current settings + virtual void save(); + + /// sets the configuration to sensible default values. + virtual void defaults(); + +private Q_SLOTS: + + /** set the colortable color buttons up according to the current colorset */ + void updateColorTable(); + + /** slot called when color on a KColorButton changes */ + void colorChanged( const QColor &newColor ); + + /** slot called when any varies button is clicked */ + void variesClicked(); + + /** slot called when the schemeList selection changes */ + void loadScheme(QListWidgetItem *currentItem, QListWidgetItem *previousItem); + + /** reselect the previously selected scheme in schemeList without loading it */ + void selectPreviousSchemeAgain(); + + /** slot called when the remove button is clicked*/ + void on_schemeRemoveButton_clicked(); + + /** slot called when the save button is clicked */ + void on_schemeSaveButton_clicked(); + + /** slot called when the import button is clicked */ + void on_schemeImportButton_clicked(); + + /** slot called when the get new schemes button is clicked */ + void on_schemeKnsButton_clicked(); + + /** slot called when the upload scheme button is clicked */ + void on_schemeKnsUploadButton_clicked(); + + /** null slot to emit changed(true) */ + void emitChanged(); + + // options slots + void on_contrastSlider_valueChanged(int value); + void on_shadeSortedColumn_stateChanged(int state); + void on_inactiveSelectionEffect_stateChanged(int state); + void on_useInactiveEffects_stateChanged(int state); + + // effects page slots + void on_inactiveIntensityBox_currentIndexChanged(int index); + void on_inactiveIntensitySlider_valueChanged(int value); + void on_inactiveColorBox_currentIndexChanged(int index); + void on_inactiveColorSlider_valueChanged(int value); + void on_inactiveColorButton_changed(const QColor & color); + void on_inactiveContrastBox_currentIndexChanged(int index); + void on_inactiveContrastSlider_valueChanged(int value); + + void on_disabledIntensityBox_currentIndexChanged(int index); + void on_disabledIntensitySlider_valueChanged(int value); + void on_disabledColorBox_currentIndexChanged(int index); + void on_disabledColorSlider_valueChanged(int value); + void on_disabledColorButton_changed(const QColor & color); + void on_disabledContrastBox_currentIndexChanged(int index); + void on_disabledContrastSlider_valueChanged(int value); + +private: + class WindecoColors { + public: + enum Role { + ActiveForeground = 0, + ActiveBackground = 1, + InactiveForeground = 2, + InactiveBackground = 3, + ActiveBlend = 4, + InactiveBlend = 5 + }; + + WindecoColors() {} + WindecoColors(const KSharedConfigPtr&); + virtual ~WindecoColors() {} + + void load(const KSharedConfigPtr&); + QColor color(Role) const; + private: + QColor m_colors[6]; + }; + + /** create a preview of a color scheme */ + static QPixmap createSchemePreviewIcon(const KSharedConfigPtr &config); + + /** load options from global */ + void loadOptions(); + + /** load from global */ + void loadInternal(bool loadOptions); + + /** load a scheme from a config file at a given path */ + void loadScheme(KSharedConfigPtr config); + + /** populate the schemeList with color schemes found on the system */ + void populateSchemeList(); + + /** update m_colorSchemes contents from the values in m_config */ + void updateColorSchemes(); + + /** update the effects page from the values in m_config */ + void updateEffectsPage(); + + /** update all preview panes from the values in m_config */ + void updatePreviews(); + + /** setup the colortable with its buttons and labels */ + void setupColorTable(); + + /** helper to create color entries */ + void createColorEntry(const QString &text, + const QString &key, + QList &list, + int index); + + /** copy color entries from color schemes into m_config */ + void updateFromColorSchemes(); + + /** copy effects page entries from controls into m_config */ + void updateFromEffectsPage(); + + /** copy options from controls into m_config */ + void updateFromOptions(); + + void changeColor(int row, const QColor &newColor); + + /** get the groupKey for the given colorSet */ + static QString colorSetGroupKey(int colorSet); + + /** save the current scheme with the given name + @param name name to save the scheme as + */ + void saveScheme(const QString &name); + + void setCommonForeground(KColorScheme::ForegroundRole role, + int stackIndex, + int buttonIndex); + void setCommonDecoration(KColorScheme::DecorationRole role, + int stackIndex, + int buttonIndex); + + QList m_backgroundButtons; + QList m_foregroundButtons; + QList m_decorationButtons; + QList m_commonColorButtons; + QList m_stackedWidgets; + QStringList m_colorKeys; + + QList m_colorSchemes; + WindecoColors m_wmColors; + QString m_currentColorScheme; + + KSharedConfigPtr m_config; + + bool m_disableUpdates; + bool m_loadedSchemeHasUnsavedChanges; + // don't (re)load the scheme, only select it in schemeList + bool m_dontLoadSelectedScheme; + + // the item previously selected in schemeList + QListWidgetItem *m_previousSchemeItem; +}; + +#endif diff --git a/kcms/colors/colorsettings.ui b/kcms/colors/colorsettings.ui new file mode 100644 index 0000000000..cbc2e84d36 --- /dev/null +++ b/kcms/colors/colorsettings.ui @@ -0,0 +1,1298 @@ + + + colorSettings + + + + 0 + 0 + 477 + 422 + + + + + 0 + + + + + 0 + + + + &Scheme + + + + + + + 0 + 0 + + + + + 23 + 16 + + + + + + + + + + Get new color schemes from the Internet + + + Get &New Schemes... + + + + + + + false + + + Share the selected scheme on the Internet + + + &Upload Scheme... + + + + + + + Import a color scheme from a file + + + Import Scheme... + + + + + + + Save the current color scheme + + + Save Scheme... + + + + + + + false + + + Remove the selected scheme + + + Remove Scheme + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + Preview + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + + + Options + + + + + + Apply inactive window color &effects + + + + + + + In&active selection changes color + + + + + + + Shade sorted column &in lists + + + + + + + true + + + Apply colors to &non-KDE4 applications + + + + + + + Shading of frames and lighting ("3D") effects + + + Shading + + + + 12 + + + 0 + + + + + Minimum + + + + + + + Qt::Horizontal + + + + 198 + 20 + + + + + + + + Maximum + + + + + + + Contrast + + + + + + + 10 + + + 5 + + + Qt::Horizontal + + + + + + + + + + Qt::Vertical + + + + 20 + 141 + + + + + + + + + Colors + + + + + + + 0 + 0 + + + + Color set: + + + + + + + + 0 + 0 + + + + Colorset to view/modify + + + + Common Colors + + + + + View + + + + + Window + + + + + Button + + + + + Selection + + + + + Tooltip + + + + + + + + 0 + + + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + 2 + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + New Row + + + + + 0 + + + + + 1 + + + + + View Background + + + + + View Text + + + + + Window Background + + + + + Window Text + + + + + Button Background + + + + + Button Text + + + + + Selection Background + + + + + Selection Text + + + + + Selection Inactive Text + + + + + Inactive Text + + + + + Active Text + + + + + Link Text + + + + + Visited Text + + + + + Negative Text + + + + + Neutral Text + + + + + Positive Text + + + + + Focus Decoration + + + + + Hover Decoration + + + + + Tooltip Background + + + + + Tooltip Text + + + + + Active Titlebar + + + + + Active Titlebar Text + + + + + Active Titlebar Secondary + + + + + Inactive Titlebar + + + + + Inactive Titlebar Text + + + + + Inactive Titlebar Secondary + + + + + + + + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + 2 + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Preview + + + + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + + + + + + + Inactive + + + + + + Intensity: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Inactive intensity effect type + + + + None + + + + + Shade + + + + + Darken + + + + + Lighten + + + + + + + + false + + + Inactive intensity effect amount + + + 40 + + + 10 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 69 + 22 + + + + + + + + Color: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Inactive color effect type + + + + None + + + + + Desaturate + + + + + Fade + + + + + Tint + + + + + + + + false + + + Inactive color amount + + + 40 + + + 10 + + + Qt::Horizontal + + + + + + + false + + + Inactive color + + + + + + + Contrast: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Inactive contrast effect type + + + + None + + + + + Fade + + + + + Tint + + + + + + + + false + + + Inactive contrast effect amount + + + 20 + + + 5 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 69 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Preview + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + + + Disabled + + + + + + Intensity: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Disabled intensity effect type + + + + None + + + + + Shade + + + + + Darken + + + + + Lighten + + + + + + + + false + + + Disabled intensity effect amount + + + 40 + + + 10 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 69 + 22 + + + + + + + + Color: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Disabled color effect type + + + + None + + + + + Desaturate + + + + + Fade + + + + + Tint + + + + + + + + false + + + Disabled color effect amount + + + 40 + + + 10 + + + Qt::Horizontal + + + + + + + false + + + Disabled color + + + + + + + Contrast: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Disabled contrast type + + + + None + + + + + Fade + + + + + Tint + + + + + + + + false + + + Disabled contrast amount + + + 20 + + + 10 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 69 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Preview + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + + + + + + + KListWidget + QListWidget +
    klistwidget.h
    +
    + + KPushButton + QPushButton +
    kpushbutton.h
    +
    + + KColorButton + QPushButton +
    kcolorbutton.h
    +
    + + PreviewWidget + QWidget +
    previewwidget.h
    + 1 +
    + + SetPreviewWidget + QWidget +
    setpreviewwidget.h
    + 1 +
    +
    + + tabWidget + schemeList + schemeKnsButton + schemeImportButton + schemeSaveButton + schemeRemoveButton + useInactiveEffects + inactiveSelectionEffect + shadeSortedColumn + applyToAlien + contrastSlider + colorSet + commonColorTable + inactiveIntensityBox + inactiveIntensitySlider + inactiveColorBox + inactiveColorSlider + inactiveContrastBox + inactiveContrastSlider + inactiveColorButton + disabledIntensityBox + disabledIntensitySlider + disabledColorBox + disabledColorSlider + disabledContrastBox + disabledContrastSlider + disabledColorButton + colorTable + + + +
    diff --git a/kcms/colors/preview.ui b/kcms/colors/preview.ui new file mode 100644 index 0000000000..8dde5c139c --- /dev/null +++ b/kcms/colors/preview.ui @@ -0,0 +1,336 @@ + + + preview + + + + 0 + 0 + 467 + 100 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Window text on Window Background + + + Window text + + + + + + + true + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 0 + + + 0 + + + + + + 2 + + + + + View Normal Text against View Normal Background + + + Normal text + + + + + + + + true + + + + View Link Text against View Normal Background + + + link + + + + + + + + true + + + + View Visited Text against View Normal Background + + + visited + + + + + + + View Active Text against View Normal Background + + + a + + + + + + + View Inactive Text against View Normal Background + + + i + + + + + + + View Negative Text against View Normal Background + + + ! + + + + + + + View Neutral Text against View Normal Background + + + = + + + + + + + View Positive Text against View Normal Background + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 4 + 51 + + + + + + + + true + + + + 2 + + + + + Selection Normal Text against Selection Normal Background + + + Selected text + + + + + + + + true + + + + Selection Link Text against Selection Normal Background + + + link + + + + + + + + true + + + + Selection Visited Text against Selection Normal Background + + + visited + + + + + + + Selection Active Text against Selection Normal Background + + + a + + + + + + + Selection Inactive Text against Selection Normal Background + + + i + + + + + + + Selection Negative Text against Selection Normal Background + + + ! + + + + + + + Selection Neutral Text against Selection Normal Background + + + = + + + + + + + Selection Positive Text against Selection Normal Background + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 141 + 0 + + + + + + + + + + + Button text on Button Background + + + Push Button + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 0 + + + + + + + + + diff --git a/kcms/colors/previewwidget.cpp b/kcms/colors/previewwidget.cpp new file mode 100644 index 0000000000..5be7567a6c --- /dev/null +++ b/kcms/colors/previewwidget.cpp @@ -0,0 +1,168 @@ +/* Preview widget for KDE Display color scheme setup module + * Copyright (C) 2007 Matthew Woehlke + * eventFilter code Copyright (C) 2007 Urs Wolfer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "previewwidget.h" + +#include +#include + +#include + +PreviewWidget::PreviewWidget(QWidget *parent) : QFrame(parent) +{ + setupUi(this); + + // set correct colors on... lots of things + setAutoFillBackground(true); + frame->setBackgroundRole(QPalette::Base); + viewWidget->setBackgroundRole(QPalette::Base); + labelView0->setBackgroundRole(QPalette::Base); + labelView3->setBackgroundRole(QPalette::Base); + labelView4->setBackgroundRole(QPalette::Base); + labelView2->setBackgroundRole(QPalette::Base); + labelView1->setBackgroundRole(QPalette::Base); + labelView5->setBackgroundRole(QPalette::Base); + labelView6->setBackgroundRole(QPalette::Base); + labelView7->setBackgroundRole(QPalette::Base); + selectionWidget->setBackgroundRole(QPalette::Highlight); + labelSelection0->setBackgroundRole(QPalette::Highlight); + labelSelection3->setBackgroundRole(QPalette::Highlight); + labelSelection4->setBackgroundRole(QPalette::Highlight); + labelSelection2->setBackgroundRole(QPalette::Highlight); + labelSelection1->setBackgroundRole(QPalette::Highlight); + labelSelection5->setBackgroundRole(QPalette::Highlight); + labelSelection6->setBackgroundRole(QPalette::Highlight); + labelSelection7->setBackgroundRole(QPalette::Highlight); + + QList widgets = findChildren(); + foreach (QWidget* widget, widgets) + { + widget->installEventFilter(this); + widget->setFocusPolicy(Qt::NoFocus); + } +} + +PreviewWidget::~PreviewWidget() +{ +} + +bool PreviewWidget::eventFilter(QObject *, QEvent *ev) +{ + switch (ev->type()) + { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::Enter: + case QEvent::Leave: + case QEvent::Wheel: + case QEvent::ContextMenu: + return true; // ignore + default: + break; + } + return false; +} + +inline void copyPaletteBrush(QPalette &palette, QPalette::ColorGroup state, + QPalette::ColorRole role) +{ + palette.setBrush(QPalette::Active, role, palette.brush(state, role)); + if (state == QPalette::Disabled) + // ### hack, while Qt has no inactive+disabled state + // TODO copy from Inactive+Disabled to Inactive instead + palette.setBrush(QPalette::Inactive, role, + palette.brush(QPalette::Disabled, role)); +} + +void PreviewWidget::setPaletteRecursive(QWidget *widget, + const QPalette &palette) +{ + widget->setPalette(palette); + + const QObjectList children = widget->children(); + foreach (QObject *child, children) { + if (child->isWidgetType()) + setPaletteRecursive((QWidget*)child, palette); + } +} + +inline void adjustWidgetForeground(QWidget *widget, QPalette::ColorGroup state, + const KSharedConfigPtr &config, + QPalette::ColorRole color, + KColorScheme::ColorSet set, + KColorScheme::ForegroundRole role) +{ + QPalette palette = widget->palette(); + KColorScheme::adjustForeground(palette, role, color, set, config); + copyPaletteBrush(palette, state, color); + widget->setPalette(palette); +} + +void PreviewWidget::setPalette(const KSharedConfigPtr &config, + QPalette::ColorGroup state) +{ + QPalette palette = KGlobalSettings::createNewApplicationPalette(config); + + if (state != QPalette::Active) { + copyPaletteBrush(palette, state, QPalette::Base); + copyPaletteBrush(palette, state, QPalette::Text); + copyPaletteBrush(palette, state, QPalette::Window); + copyPaletteBrush(palette, state, QPalette::WindowText); + copyPaletteBrush(palette, state, QPalette::Button); + copyPaletteBrush(palette, state, QPalette::ButtonText); + copyPaletteBrush(palette, state, QPalette::Highlight); + copyPaletteBrush(palette, state, QPalette::HighlightedText); + copyPaletteBrush(palette, state, QPalette::AlternateBase); + copyPaletteBrush(palette, state, QPalette::Link); + copyPaletteBrush(palette, state, QPalette::LinkVisited); + copyPaletteBrush(palette, state, QPalette::Light); + copyPaletteBrush(palette, state, QPalette::Midlight); + copyPaletteBrush(palette, state, QPalette::Mid); + copyPaletteBrush(palette, state, QPalette::Dark); + copyPaletteBrush(palette, state, QPalette::Shadow); + } + + setPaletteRecursive(this, palette); + +#define ADJUST_WIDGET_FOREGROUND(w,c,s,r) \ + adjustWidgetForeground(w, state, config, QPalette::c, KColorScheme::s, KColorScheme::r); + + ADJUST_WIDGET_FOREGROUND(labelView1, Text, View, InactiveText); + ADJUST_WIDGET_FOREGROUND(labelView2, Text, View, ActiveText); + ADJUST_WIDGET_FOREGROUND(labelView3, Text, View, LinkText); + ADJUST_WIDGET_FOREGROUND(labelView4, Text, View, VisitedText); + ADJUST_WIDGET_FOREGROUND(labelView5, Text, View, NegativeText); + ADJUST_WIDGET_FOREGROUND(labelView6, Text, View, NeutralText); + ADJUST_WIDGET_FOREGROUND(labelView7, Text, View, PositiveText); + + ADJUST_WIDGET_FOREGROUND(labelSelection1, HighlightedText, Selection, InactiveText); + ADJUST_WIDGET_FOREGROUND(labelSelection2, HighlightedText, Selection, ActiveText); + ADJUST_WIDGET_FOREGROUND(labelSelection3, HighlightedText, Selection, LinkText); + ADJUST_WIDGET_FOREGROUND(labelSelection4, HighlightedText, Selection, VisitedText); + ADJUST_WIDGET_FOREGROUND(labelSelection5, HighlightedText, Selection, NegativeText); + ADJUST_WIDGET_FOREGROUND(labelSelection6, HighlightedText, Selection, NeutralText); + ADJUST_WIDGET_FOREGROUND(labelSelection7, HighlightedText, Selection, PositiveText); +} + +#include "previewwidget.moc" diff --git a/kcms/colors/previewwidget.h b/kcms/colors/previewwidget.h new file mode 100644 index 0000000000..44b1d541cf --- /dev/null +++ b/kcms/colors/previewwidget.h @@ -0,0 +1,49 @@ +/* Preview widget for KDE Display color scheme setup module + * Copyright (C) 2007 Matthew Woehlke + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SCHEMEPREVIEW_H__ +#define __SCHEMEPREVIEW_H__ + +#include +#include + +#include + +#include "ui_preview.h" + +/** + * The Desktop/Colors tab in kcontrol. + */ +class PreviewWidget : public QFrame, Ui::preview +{ + Q_OBJECT + +public: + PreviewWidget(QWidget *parent); + virtual ~PreviewWidget(); + + void setPalette(const KSharedConfigPtr &config, + QPalette::ColorGroup state = QPalette::Active); + +protected: + void setPaletteRecursive(QWidget*, const QPalette&); + bool eventFilter(QObject *, QEvent *); +}; + +#endif diff --git a/kcms/colors/schemes/Honeycomb.colors b/kcms/colors/schemes/Honeycomb.colors new file mode 100644 index 0000000000..2db8c570bb --- /dev/null +++ b/kcms/colors/schemes/Honeycomb.colors @@ -0,0 +1,96 @@ +[ColorEffects:Disabled] +Color=48,43,30 +ColorAmount=0.125 +ColorEffect=2 +ContrastAmount=0.5 +ContrastEffect=1 +IntensityAmount=0.05 +IntensityEffect=0 + +[ColorEffects:Inactive] +Color=227,170,0 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.25 +ContrastEffect=1 +IntensityAmount=0.05 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=136,138,133 +BackgroundNormal=186,189,183 +DecorationFocus=85,85,85 +DecorationHover=243,195,0 +ForegroundActive=191,92,0 +ForegroundInactive=117,80,25 +ForegroundLink=232,82,144 +ForegroundNegative=191,3,3 +ForegroundNeutral=43,116,199 +ForegroundNormal=0,0,0 +ForegroundPositive=0,137,43 +ForegroundVisited=100,74,155 + +[Colors:Selection] +BackgroundAlternate=243,195,0 +BackgroundNormal=227,170,0 +DecorationFocus=85,85,85 +DecorationHover=243,195,0 +ForegroundActive=191,92,0 +ForegroundInactive=255,235,85 +ForegroundLink=232,82,144 +ForegroundNegative=191,3,3 +ForegroundNeutral=43,116,199 +ForegroundNormal=255,255,255 +ForegroundPositive=0,137,43 +ForegroundVisited=100,74,155 + +[Colors:Tooltip] +BackgroundAlternate=255,235,85 +BackgroundNormal=255,242,153 +DecorationFocus=85,85,85 +DecorationHover=243,195,0 +ForegroundActive=191,92,0 +ForegroundInactive=117,80,25 +ForegroundLink=232,82,144 +ForegroundNegative=191,3,3 +ForegroundNeutral=43,116,199 +ForegroundNormal=64,48,0 +ForegroundPositive=0,137,43 +ForegroundVisited=100,74,155 + +[Colors:View] +BackgroundAlternate=255,251,231 +BackgroundNormal=255,255,255 +DecorationFocus=85,85,85 +DecorationHover=243,195,0 +ForegroundActive=191,92,0 +ForegroundInactive=117,80,25 +ForegroundLink=232,82,144 +ForegroundNegative=191,3,3 +ForegroundNeutral=43,116,199 +ForegroundNormal=0,0,0 +ForegroundPositive=0,137,43 +ForegroundVisited=100,74,155 + +[Colors:Window] +BackgroundAlternate=186,189,183 +BackgroundNormal=212,215,208 +DecorationFocus=85,85,85 +DecorationHover=243,195,0 +ForegroundActive=191,92,0 +ForegroundInactive=117,80,25 +ForegroundLink=232,82,144 +ForegroundNegative=191,3,3 +ForegroundNeutral=43,116,199 +ForegroundNormal=0,0,0 +ForegroundPositive=0,137,43 +ForegroundVisited=100,74,155 + +[General] +Name=Honeycomb + +[WM] +activeBackground=227,170,0 +activeForeground=255,255,255 +inactiveBackground=136,138,133 +inactiveForeground=46,52,54 diff --git a/kcms/colors/schemes/Norway.colors b/kcms/colors/schemes/Norway.colors new file mode 100644 index 0000000000..abf78bcd84 --- /dev/null +++ b/kcms/colors/schemes/Norway.colors @@ -0,0 +1,96 @@ +[ColorEffects:Disabled] +Color=197,179,153 +ColorAmount=0.5 +ColorEffect=2 +ContrastAmount=0.25 +ContrastEffect=1 +IntensityAmount=0.25 +IntensityEffect=0 + +[ColorEffects:Inactive] +Color=213,198,176 +ColorAmount=0.1 +ColorEffect=2 +ContrastAmount=0.25 +ContrastEffect=1 +IntensityAmount=-0.05 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=246,240,227 +BackgroundNormal=247,242,232 +DecorationFocus=128,112,96 +DecorationHover=29,135,205 +ForegroundActive=0,197,204 +ForegroundInactive=197,179,153 +ForegroundLink=0,87,174 +ForegroundNegative=232,87,82 +ForegroundNeutral=227,170,0 +ForegroundNormal=0,0,0 +ForegroundPositive=120,183,83 +ForegroundVisited=100,74,155 + +[Colors:Selection] +BackgroundAlternate=27,131,196 +BackgroundNormal=29,132,205 +DecorationFocus=128,112,96 +DecorationHover=29,135,205 +ForegroundActive=0,197,204 +ForegroundInactive=144,192,232 +ForegroundLink=0,87,174 +ForegroundNegative=232,87,82 +ForegroundNeutral=227,170,0 +ForegroundNormal=255,255,255 +ForegroundPositive=120,183,83 +ForegroundVisited=100,74,155 + +[Colors:Tooltip] +BackgroundAlternate=250,248,241 +BackgroundNormal=253,252,251 +DecorationFocus=128,112,96 +DecorationHover=29,135,205 +ForegroundActive=0,197,204 +ForegroundInactive=197,179,153 +ForegroundLink=0,87,174 +ForegroundNegative=232,87,82 +ForegroundNeutral=227,170,0 +ForegroundNormal=0,0,0 +ForegroundPositive=120,183,83 +ForegroundVisited=100,74,155 + +[Colors:View] +BackgroundAlternate=250,246,239 +BackgroundNormal=253,252,250 +DecorationFocus=128,112,96 +DecorationHover=29,135,205 +ForegroundActive=0,197,204 +ForegroundInactive=197,179,153 +ForegroundLink=0,87,174 +ForegroundNegative=232,87,82 +ForegroundNeutral=227,170,0 +ForegroundNormal=0,0,0 +ForegroundPositive=120,183,83 +ForegroundVisited=100,74,155 + +[Colors:Window] +BackgroundAlternate=233,223,206 +BackgroundNormal=235,226,210 +DecorationFocus=128,112,96 +DecorationHover=29,135,205 +ForegroundActive=0,197,204 +ForegroundInactive=197,179,153 +ForegroundLink=0,87,174 +ForegroundNegative=232,87,82 +ForegroundNeutral=227,170,0 +ForegroundNormal=0,0,0 +ForegroundPositive=120,183,83 +ForegroundVisited=100,74,155 + +[General] +Name=Norway + +[WM] +activeBackground=29,135,205 +activeForeground=255,255,255 +inactiveBackground=128,112,96 +inactiveForeground=253,252,251 diff --git a/kcms/colors/schemes/ObsidianCoast.colors b/kcms/colors/schemes/ObsidianCoast.colors new file mode 100644 index 0000000000..0245ee1805 --- /dev/null +++ b/kcms/colors/schemes/ObsidianCoast.colors @@ -0,0 +1,95 @@ +[ColorEffects:Disabled] +ColorAmount=-0.8 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.25 +IntensityEffect=2 + +[ColorEffects:Inactive] +Color=0,0,0 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.4 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=66,65,64 +BackgroundNormal=64,63,62 +DecorationFocus=39,94,160 +DecorationHover=87,129,176 +ForegroundActive=150,191,240 +ForegroundInactive=120,119,117 +ForegroundLink=80,142,216 +ForegroundNegative=232,88,72 +ForegroundNeutral=192,162,95 +ForegroundNormal=232,230,227 +ForegroundPositive=120,183,83 +ForegroundVisited=142,121,165 + +[Colors:Selection] +BackgroundAlternate=22,68,120 +BackgroundNormal=24,72,128 +DecorationFocus=39,94,160 +DecorationHover=87,129,176 +ForegroundActive=150,191,240 +ForegroundInactive=81,119,166 +ForegroundLink=80,142,216 +ForegroundNegative=232,88,72 +ForegroundNeutral=192,162,95 +ForegroundNormal=255,255,255 +ForegroundPositive=120,183,83 +ForegroundVisited=142,121,165 + +[Colors:Tooltip] +BackgroundAlternate=17,51,86 +BackgroundNormal=16,48,80 +DecorationFocus=39,94,160 +DecorationHover=87,129,176 +ForegroundActive=150,191,240 +ForegroundInactive=120,119,117 +ForegroundLink=80,142,216 +ForegroundNegative=232,88,72 +ForegroundNeutral=192,162,95 +ForegroundNormal=196,209,224 +ForegroundPositive=120,183,83 +ForegroundVisited=142,121,165 + +[Colors:View] +BackgroundAlternate=36,35,35 +BackgroundNormal=32,31,31 +DecorationFocus=39,94,160 +DecorationHover=87,129,176 +ForegroundActive=150,191,240 +ForegroundInactive=120,119,117 +ForegroundLink=80,142,216 +ForegroundNegative=232,88,72 +ForegroundNeutral=192,162,95 +ForegroundNormal=212,210,207 +ForegroundPositive=120,183,83 +ForegroundVisited=142,121,165 + +[Colors:Window] +BackgroundAlternate=52,51,50 +BackgroundNormal=48,47,47 +DecorationFocus=39,94,160 +DecorationHover=87,129,176 +ForegroundActive=150,191,240 +ForegroundInactive=120,119,117 +ForegroundLink=80,142,216 +ForegroundNegative=232,88,72 +ForegroundNeutral=192,162,95 +ForegroundNormal=224,222,219 +ForegroundPositive=120,183,83 +ForegroundVisited=142,121,165 + +[General] +Name=Obsidian Coast + +[WM] +activeBackground=19,47,80 +activeForeground=255,255,255 +inactiveBackground=64,63,62 +inactiveForeground=128,127,125 diff --git a/kcms/colors/schemes/Oxygen.colors b/kcms/colors/schemes/Oxygen.colors new file mode 100644 index 0000000000..da9116b39a --- /dev/null +++ b/kcms/colors/schemes/Oxygen.colors @@ -0,0 +1,103 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=-0.9 +ColorEffect=1 +ContrastAmount=0.25 +ContrastEffect=2 +Enable=true +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=224,223,222 +BackgroundNormal=223,220,217 +DecorationFocus=58,167,221 +DecorationHover=110,214,255 +ForegroundActive=146,76,157 +ForegroundInactive=137,136,135 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=34,31,30 +ForegroundPositive=0,110,40 +ForegroundVisited=100,74,155 + +[Colors:Selection] +BackgroundAlternate=62,138,204 +BackgroundNormal=67,172,232 +DecorationFocus=58,167,221 +DecorationHover=110,214,255 +ForegroundActive=108,36,119 +ForegroundInactive=199,226,248 +ForegroundLink=0,49,110 +ForegroundNegative=156,14,14 +ForegroundNeutral=255,221,0 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=69,40,134 + +[Colors:Tooltip] +BackgroundAlternate=196,224,255 +BackgroundNormal=24,21,19 +DecorationFocus=58,167,221 +DecorationHover=110,214,255 +ForegroundActive=255,128,224 +ForegroundInactive=137,136,135 +ForegroundLink=88,172,255 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=231,253,255 +ForegroundPositive=0,110,40 +ForegroundVisited=150,111,232 + +[Colors:View] +BackgroundAlternate=248,247,246 +BackgroundNormal=255,255,255 +DecorationFocus=58,167,221 +DecorationHover=110,214,255 +ForegroundActive=146,76,157 +ForegroundInactive=137,136,135 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=31,28,27 +ForegroundPositive=0,110,40 +ForegroundVisited=100,74,155 + +[Colors:Window] +BackgroundAlternate=218,217,216 +BackgroundNormal=214,210,208 +DecorationFocus=58,167,221 +DecorationHover=110,214,255 +ForegroundActive=146,76,157 +ForegroundInactive=137,136,135 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=34,31,30 +ForegroundPositive=0,110,40 +ForegroundVisited=100,74,155 + +[General] +ColorScheme=Oxygen +Name=Oxygen +shadeSortColumn=true + +[KDE] +contrast=7 + +[WM] +activeBackground=48,174,232 +activeForeground=255,255,255 +inactiveBackground=224,223,222 +inactiveForeground=75,71,67 diff --git a/kcms/colors/schemes/OxygenCold.colors b/kcms/colors/schemes/OxygenCold.colors new file mode 100644 index 0000000000..53d7777bb8 --- /dev/null +++ b/kcms/colors/schemes/OxygenCold.colors @@ -0,0 +1,95 @@ +[ColorEffects:Disabled] +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.1 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=224,223,222 +BackgroundNormal=232,231,230 +DecorationFocus=43,116,199 +DecorationHover=119,183,255 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=20,19,18 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:Selection] +BackgroundAlternate=62,138,204 +BackgroundNormal=65,139,212 +DecorationFocus=43,116,199 +DecorationHover=119,183,255 +ForegroundActive=255,128,224 +ForegroundInactive=165,193,228 +ForegroundLink=0,49,110 +ForegroundNegative=156,14,14 +ForegroundNeutral=255,221,0 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=69,40,134 + +[Colors:Tooltip] +BackgroundAlternate=196,224,255 +BackgroundNormal=192,218,255 +DecorationFocus=43,116,199 +DecorationHover=119,183,255 +ForegroundActive=255,128,224 +ForegroundInactive=96,112,128 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=20,19,18 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:View] +BackgroundAlternate=248,247,246 +BackgroundNormal=255,255,255 +DecorationFocus=43,116,199 +DecorationHover=119,183,255 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=20,19,18 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:Window] +BackgroundAlternate=218,217,216 +BackgroundNormal=224,223,222 +DecorationFocus=43,116,199 +DecorationHover=119,183,255 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=20,19,18 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[General] +Name=Oxygen Cold + +[WM] +activeBackground=96,148,207 +activeForeground=255,255,255 +inactiveBackground=224,223,222 +inactiveForeground=20,19,18 diff --git a/kcms/colors/schemes/Steel.colors b/kcms/colors/schemes/Steel.colors new file mode 100644 index 0000000000..3ebd8b4c32 --- /dev/null +++ b/kcms/colors/schemes/Steel.colors @@ -0,0 +1,95 @@ +[ColorEffects:Disabled] +ColorAmount=-0.9 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.25 +IntensityEffect=2 + +[ColorEffects:Inactive] +Color=135,133,129 +ColorAmount=0.05 +ColorEffect=2 +ContrastAmount=0.25 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=224,223,216 +BackgroundNormal=232,231,223 +DecorationFocus=69,98,112 +DecorationHover=74,139,163 +ForegroundActive=232,87,82 +ForegroundInactive=148,133,111 +ForegroundLink=41,111,190 +ForegroundNegative=139,83,127 +ForegroundNeutral=191,165,103 +ForegroundNormal=0,0,0 +ForegroundPositive=67,102,46 +ForegroundVisited=100,74,155 + +[Colors:Selection] +BackgroundAlternate=118,154,165 +BackgroundNormal=123,160,173 +DecorationFocus=69,98,112 +DecorationHover=74,139,163 +ForegroundActive=232,87,82 +ForegroundInactive=178,197,204 +ForegroundLink=41,111,190 +ForegroundNegative=139,83,127 +ForegroundNeutral=191,165,103 +ForegroundNormal=255,255,255 +ForegroundPositive=67,102,46 +ForegroundVisited=100,74,155 + +[Colors:Tooltip] +BackgroundAlternate=216,228,231 +BackgroundNormal=219,231,235 +DecorationFocus=69,98,112 +DecorationHover=74,139,163 +ForegroundActive=232,87,82 +ForegroundInactive=148,133,111 +ForegroundLink=41,111,190 +ForegroundNegative=139,83,127 +ForegroundNeutral=191,165,103 +ForegroundNormal=37,34,28 +ForegroundPositive=67,102,46 +ForegroundVisited=100,74,155 + +[Colors:View] +BackgroundAlternate=250,250,248 +BackgroundNormal=255,255,255 +DecorationFocus=69,98,112 +DecorationHover=74,139,163 +ForegroundActive=232,87,82 +ForegroundInactive=148,133,111 +ForegroundLink=41,111,190 +ForegroundNegative=139,83,127 +ForegroundNeutral=191,165,103 +ForegroundNormal=0,0,0 +ForegroundPositive=67,102,46 +ForegroundVisited=100,74,155 + +[Colors:Window] +BackgroundAlternate=212,211,204 +BackgroundNormal=224,223,216 +DecorationFocus=69,98,112 +DecorationHover=74,139,163 +ForegroundActive=232,87,82 +ForegroundInactive=148,133,111 +ForegroundLink=41,111,190 +ForegroundNegative=139,83,127 +ForegroundNeutral=191,165,103 +ForegroundNormal=0,0,0 +ForegroundPositive=67,102,46 +ForegroundVisited=100,74,155 + +[General] +Name=Steel + +[WM] +activeBackground=74,139,163 +activeForeground=255,255,255 +inactiveBackground=208,206,192 +inactiveForeground=100,92,72 diff --git a/kcms/colors/schemes/WontonSoup.colors b/kcms/colors/schemes/WontonSoup.colors new file mode 100644 index 0000000000..34ebaa9913 --- /dev/null +++ b/kcms/colors/schemes/WontonSoup.colors @@ -0,0 +1,94 @@ +[ColorEffects:Disabled] +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.25 +IntensityEffect=2 + +[ColorEffects:Inactive] +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.25 +ContrastEffect=2 +IntensityAmount=0.05 +IntensityEffect=2 + +[Colors:Button] +BackgroundAlternate=90,98,109 +BackgroundNormal=82,88,99 +DecorationFocus=125,141,153 +DecorationHover=119,149,179 +ForegroundActive=255,255,255 +ForegroundInactive=135,143,154 +ForegroundLink=156,212,255 +ForegroundNegative=225,150,209 +ForegroundNeutral=218,198,115 +ForegroundNormal=210,222,240 +ForegroundPositive=145,221,100 +ForegroundVisited=64,128,255 + +[Colors:Selection] +BackgroundAlternate=111,126,144 +BackgroundNormal=120,136,156 +DecorationFocus=125,141,153 +DecorationHover=119,149,179 +ForegroundActive=255,255,255 +ForegroundInactive=174,192,218 +ForegroundLink=156,212,255 +ForegroundNegative=225,150,209 +ForegroundNeutral=218,198,115 +ForegroundNormal=209,225,244 +ForegroundPositive=145,221,100 +ForegroundVisited=64,128,255 + +[Colors:Tooltip] +BackgroundAlternate=171,181,195 +BackgroundNormal=182,193,208 +DecorationFocus=125,141,153 +DecorationHover=119,149,179 +ForegroundActive=255,255,255 +ForegroundInactive=112,118,128 +ForegroundLink=87,161,218 +ForegroundNegative=99,66,92 +ForegroundNeutral=86,78,45 +ForegroundNormal=42,44,48 +ForegroundPositive=57,86,38 +ForegroundVisited=46,95,185 + +[Colors:View] +BackgroundAlternate=67,71,80 +BackgroundNormal=60,64,72 +DecorationFocus=125,141,153 +DecorationHover=119,149,179 +ForegroundActive=255,255,255 +ForegroundInactive=135,143,154 +ForegroundLink=156,212,255 +ForegroundNegative=225,150,209 +ForegroundNeutral=218,198,115 +ForegroundNormal=210,222,240 +ForegroundPositive=145,221,100 +ForegroundVisited=64,128,255 + +[Colors:Window] +BackgroundAlternate=78,83,94 +BackgroundNormal=73,78,88 +DecorationFocus=125,141,153 +DecorationHover=119,149,179 +ForegroundActive=255,255,255 +ForegroundInactive=135,143,154 +ForegroundLink=156,212,255 +ForegroundNegative=225,150,209 +ForegroundNeutral=218,198,115 +ForegroundNormal=182,193,208 +ForegroundPositive=145,221,100 +ForegroundVisited=64,128,255 + +[General] +Name=Wonton Soup + +[WM] +activeBackground=138,151,166 +activeForeground=224,237,255 +inactiveBackground=82,89,99 +inactiveForeground=140,152,168 diff --git a/kcms/colors/schemes/Zion.colors b/kcms/colors/schemes/Zion.colors new file mode 100644 index 0000000000..4427567df9 --- /dev/null +++ b/kcms/colors/schemes/Zion.colors @@ -0,0 +1,96 @@ +[ColorEffects:Disabled] +Color=210,205,218 +ColorAmount=-0.9 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0 +IntensityEffect=0 + +[ColorEffects:Inactive] +Color=0,0,0 +ColorAmount=0.5 +ColorEffect=1 +ContrastAmount=0.25 +ContrastEffect=1 +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=252,252,252 +BackgroundNormal=255,255,255 +DecorationFocus=128,128,128 +DecorationHover=0,0,0 +ForegroundActive=0,102,255 +ForegroundInactive=112,112,112 +ForegroundLink=0,0,192 +ForegroundNegative=128,0,0 +ForegroundNeutral=112,96,0 +ForegroundNormal=0,0,0 +ForegroundPositive=0,96,0 +ForegroundVisited=88,0,176 + +[Colors:Selection] +BackgroundAlternate=171,188,248 +BackgroundNormal=176,192,255 +DecorationFocus=128,128,128 +DecorationHover=0,0,0 +ForegroundActive=0,102,255 +ForegroundInactive=64,64,192 +ForegroundLink=0,0,192 +ForegroundNegative=128,0,0 +ForegroundNeutral=112,96,0 +ForegroundNormal=0,0,0 +ForegroundPositive=0,96,0 +ForegroundVisited=88,0,176 + +[Colors:Tooltip] +BackgroundAlternate=250,250,250 +BackgroundNormal=255,255,255 +DecorationFocus=128,128,128 +DecorationHover=0,0,0 +ForegroundActive=0,102,255 +ForegroundInactive=112,112,112 +ForegroundLink=0,0,192 +ForegroundNegative=128,0,0 +ForegroundNeutral=112,96,0 +ForegroundNormal=0,0,0 +ForegroundPositive=0,96,0 +ForegroundVisited=88,0,176 + +[Colors:View] +BackgroundAlternate=252,252,252 +BackgroundNormal=255,255,255 +DecorationFocus=128,128,128 +DecorationHover=0,0,0 +ForegroundActive=0,102,255 +ForegroundInactive=112,112,112 +ForegroundLink=0,0,192 +ForegroundNegative=128,0,0 +ForegroundNeutral=112,96,0 +ForegroundNormal=0,0,0 +ForegroundPositive=0,96,0 +ForegroundVisited=88,0,176 + +[Colors:Window] +BackgroundAlternate=248,248,248 +BackgroundNormal=252,252,252 +DecorationFocus=128,128,128 +DecorationHover=0,0,0 +ForegroundActive=0,102,255 +ForegroundInactive=112,112,112 +ForegroundLink=0,0,192 +ForegroundNegative=128,0,0 +ForegroundNeutral=112,96,0 +ForegroundNormal=0,0,0 +ForegroundPositive=0,96,0 +ForegroundVisited=88,0,176 + +[General] +Name=Zion + +[WM] +activeBackground=176,193,255 +activeForeground=0,0,0 +inactiveBackground=255,255,255 +inactiveForeground=0,0,0 diff --git a/kcms/colors/schemes/ZionReversed.colors b/kcms/colors/schemes/ZionReversed.colors new file mode 100644 index 0000000000..36131d4ac1 --- /dev/null +++ b/kcms/colors/schemes/ZionReversed.colors @@ -0,0 +1,96 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0.5 +ColorEffect=2 +ContrastAmount=0.5 +ContrastEffect=1 +IntensityAmount=0.05 +IntensityEffect=0 + +[ColorEffects:Inactive] +Color=0,0,0 +ColorAmount=0.5 +ColorEffect=1 +ContrastAmount=0.25 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=12,12,12 +BackgroundNormal=0,0,0 +DecorationFocus=192,192,192 +DecorationHover=255,255,255 +ForegroundActive=192,255,255 +ForegroundInactive=160,160,160 +ForegroundLink=128,181,255 +ForegroundNegative=255,128,172 +ForegroundNeutral=255,212,128 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=192,128,255 + +[Colors:Selection] +BackgroundAlternate=0,52,116 +BackgroundNormal=0,49,110 +DecorationFocus=192,192,192 +DecorationHover=255,255,255 +ForegroundActive=192,255,255 +ForegroundInactive=96,148,207 +ForegroundLink=128,181,255 +ForegroundNegative=255,128,172 +ForegroundNeutral=255,212,128 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=192,128,255 + +[Colors:Tooltip] +BackgroundAlternate=12,12,12 +BackgroundNormal=0,0,0 +DecorationFocus=192,192,192 +DecorationHover=255,255,255 +ForegroundActive=192,255,255 +ForegroundInactive=160,160,160 +ForegroundLink=128,181,255 +ForegroundNegative=255,128,172 +ForegroundNeutral=255,212,128 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=192,128,255 + +[Colors:View] +BackgroundAlternate=12,12,12 +BackgroundNormal=0,0,0 +DecorationFocus=192,192,192 +DecorationHover=255,255,255 +ForegroundActive=192,255,255 +ForegroundInactive=160,160,160 +ForegroundLink=128,181,255 +ForegroundNegative=255,128,172 +ForegroundNeutral=255,212,128 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=192,128,255 + +[Colors:Window] +BackgroundAlternate=20,20,20 +BackgroundNormal=16,16,16 +DecorationFocus=192,192,192 +DecorationHover=255,255,255 +ForegroundActive=192,255,255 +ForegroundInactive=160,160,160 +ForegroundLink=128,181,255 +ForegroundNegative=255,128,172 +ForegroundNeutral=255,212,128 +ForegroundNormal=255,255,255 +ForegroundPositive=128,255,128 +ForegroundVisited=192,128,255 + +[General] +Name=Zion (Reversed) + +[WM] +activeBackground=0,49,110 +activeForeground=255,255,255 +inactiveBackground=64,64,64 +inactiveForeground=128,128,128 diff --git a/kcms/colors/setpreview.ui b/kcms/colors/setpreview.ui new file mode 100644 index 0000000000..09ae55dee8 --- /dev/null +++ b/kcms/colors/setpreview.ui @@ -0,0 +1,347 @@ + + + setpreview + + + + 0 + 0 + 550 + 96 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 0 + + + + + + + + Normal Text on Normal Background + + + normal + + + + + + + + + + + + + + true + + + + Link Text on Normal Background + + + link + + + + + + + + + + + + + + true + + + + Visited Text on Normal Background + + + visited + + + + + + + + + + Active Text on Normal Background + + + + + + active + + + + + + + + + + + + + Inactive Text on Normal Background + + + inactive + + + + + + + + + + + + + Negative Text on Normal Background + + + negative + + + + + + + + + + + + + Neutral Text on Normal Background + + + neutral + + + + + + + + + + + + + Positive Text on Normal Background + + + positive + + + + + + + + + + Hover on Normal Background + + + + + + hover + + + + + + + + + + + + + Normal Text on Normal Background + + + normal + + + + + + + + + + + + + + true + + + + Link Text on Link Background +(Note: Link Background is derived from Link Text and cannot be separately configured at this time) + + + link + + + + + + + + + + + + + + true + + + + Visited Text on Visited Background +(Note: Visited Background is derived from Visited Text and cannot be separately configured at this time) + + + visited + + + + + + + + + + + + + Active Text on Active Background +(Note: Active Background is derived from Active Text and cannot be separately configured at this time) + + + active + + + + + + + + + + + + + Inactive Text on Alternate Background + + + alternate + + + + + + + + + + + + + Negative Text on Negative Background +(Note: Negative Background is derived from Negative Text and cannot be separately configured at this time) + + + negative + + + + + + + + + + + + + Neutral Text on Neutral Background +(Note: Neutral Background is derived from Neutral Text and cannot be separately configured at this time) + + + neutral + + + + + + + + + + + + + Positive Text on Positive Background +(Note: Positive Background is derived from Positive Text and cannot be separately configured at this time) + + + positive + + + + + + + + + + + + + Focus on Normal Background + + + focus + + + + + + + + + + + diff --git a/kcms/colors/setpreviewwidget.cpp b/kcms/colors/setpreviewwidget.cpp new file mode 100644 index 0000000000..98413c556d --- /dev/null +++ b/kcms/colors/setpreviewwidget.cpp @@ -0,0 +1,138 @@ +/* Preview widget for KDE Display color scheme setup module + * Copyright (C) 2007 Matthew Woehlke + * eventFilter code Copyright (C) 2007 Urs Wolfer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "setpreviewwidget.h" + +#include + +void setAutoFill(QWidget* widget) +{ + widget->setAutoFillBackground(true); + widget->setBackgroundRole(QPalette::Base); +} + +SetPreviewWidget::SetPreviewWidget(QWidget *parent) : QFrame(parent) +{ + setupUi(this); + + // set correct colors on... lots of things + setAutoFillBackground(true); + setBackgroundRole(QPalette::Base); + setAutoFill(widgetBack0); + setAutoFill(widgetBack1); + setAutoFill(widgetBack2); + setAutoFill(widgetBack3); + setAutoFill(widgetBack4); + setAutoFill(widgetBack5); + setAutoFill(widgetBack6); + setAutoFill(widgetBack7); + setAutoFillBackground(true); +/* + frame->setBackgroundRole(QPalette::Base); + viewWidget->setBackgroundRole(QPalette::Base); + labelView0->setBackgroundRole(QPalette::Base); + labelView3->setBackgroundRole(QPalette::Base); + labelView4->setBackgroundRole(QPalette::Base); + labelView2->setBackgroundRole(QPalette::Base); + labelView1->setBackgroundRole(QPalette::Base); + labelView5->setBackgroundRole(QPalette::Base); + labelView6->setBackgroundRole(QPalette::Base); + labelView7->setBackgroundRole(QPalette::Base); + selectionWidget->setBackgroundRole(QPalette::Highlight); + labelSelection0->setBackgroundRole(QPalette::Highlight); + labelSelection3->setBackgroundRole(QPalette::Highlight); + labelSelection4->setBackgroundRole(QPalette::Highlight); + labelSelection2->setBackgroundRole(QPalette::Highlight); + labelSelection1->setBackgroundRole(QPalette::Highlight); + labelSelection5->setBackgroundRole(QPalette::Highlight); + labelSelection6->setBackgroundRole(QPalette::Highlight); + labelSelection7->setBackgroundRole(QPalette::Highlight); +*/ + + QList widgets = findChildren(); + foreach (QWidget* widget, widgets) + { + widget->installEventFilter(this); + widget->setFocusPolicy(Qt::NoFocus); + } +} + +SetPreviewWidget::~SetPreviewWidget() +{ +} + +bool SetPreviewWidget::eventFilter(QObject *, QEvent *ev) +{ + switch (ev->type()) + { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::Enter: + case QEvent::Leave: + case QEvent::Wheel: + case QEvent::ContextMenu: + return true; // ignore + default: + break; + } + return false; +} + +void SetPreviewWidget::setPalette(const KSharedConfigPtr &config, + KColorScheme::ColorSet set) +{ + QPalette palette = KGlobalSettings::createApplicationPalette(config); + KColorScheme::adjustBackground(palette, KColorScheme::NormalBackground, + QPalette::Base, set, config); + QFrame::setPalette(palette); + +#define SET_ROLE_PALETTE(n, f, b) \ + KColorScheme::adjustForeground(palette, KColorScheme::f, QPalette::Text, set, config); \ + labelFore##n->setPalette(palette); \ + KColorScheme::adjustBackground(palette, KColorScheme::b, QPalette::Base, set, config); \ + labelBack##n->setPalette(palette); \ + widgetBack##n->setPalette(palette); + + SET_ROLE_PALETTE(0, NormalText, NormalBackground); + SET_ROLE_PALETTE(1, InactiveText, AlternateBackground); + SET_ROLE_PALETTE(2, ActiveText, ActiveBackground); + SET_ROLE_PALETTE(3, LinkText, LinkBackground); + SET_ROLE_PALETTE(4, VisitedText, VisitedBackground); + SET_ROLE_PALETTE(5, NegativeText, NegativeBackground); + SET_ROLE_PALETTE(6, NeutralText, NeutralBackground); + SET_ROLE_PALETTE(7, PositiveText, PositiveBackground); + + KColorScheme kcs(QPalette::Active, set, config); + QBrush deco; + +#define SET_DECO_PALETTE(n, d) \ + deco = kcs.decoration(KColorScheme::d); \ + palette.setBrush(QPalette::Text, deco); \ + labelFore##n->setPalette(palette); \ + + SET_DECO_PALETTE(8, HoverColor); + SET_DECO_PALETTE(9, FocusColor); +} + +#include "setpreviewwidget.moc" diff --git a/kcms/colors/setpreviewwidget.h b/kcms/colors/setpreviewwidget.h new file mode 100644 index 0000000000..253414e84c --- /dev/null +++ b/kcms/colors/setpreviewwidget.h @@ -0,0 +1,49 @@ +/* Colorset Preview widget for KDE Display color scheme setup module + * Copyright (C) 2008 Matthew Woehlke + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SCHEMEPREVIEWSET_H__ +#define __SCHEMEPREVIEWSET_H__ + +#include +#include + +#include +#include + +#include "ui_setpreview.h" + +/** + * The Desktop/Colors tab in kcontrol. + */ +class SetPreviewWidget : public QFrame, Ui::setpreview +{ + Q_OBJECT + +public: + SetPreviewWidget(QWidget *parent); + virtual ~SetPreviewWidget(); + + void setPalette(const KSharedConfigPtr &config, KColorScheme::ColorSet); + +protected: + void setPaletteRecursive(QWidget*, const QPalette&); + bool eventFilter(QObject *, QEvent *); +}; + +#endif diff --git a/kcms/dateandtime/CMakeLists.txt b/kcms/dateandtime/CMakeLists.txt new file mode 100644 index 0000000000..93099c8ee1 --- /dev/null +++ b/kcms/dateandtime/CMakeLists.txt @@ -0,0 +1,34 @@ +########### next target ############### + +set(kcm_clock_PART_SRCS dtime.cpp main.cpp ) + +kde4_add_ui_files(kcm_clock_PART_SRCS dateandtime.ui) +add_library(kcm_clock MODULE ${kcm_clock_PART_SRCS}) + +target_link_libraries(kcm_clock + Qt5::DBus + KF5::ItemViews + KF5::Auth + KF5::KCMUtils + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::Plasma + KF5::KDE4Support +) + +install(TARGETS kcm_clock DESTINATION ${PLUGIN_INSTALL_DIR} ) + +########### next target ############### + +add_executable(kcmdatetimehelper helper.cpp ${helper_mocs}) +target_link_libraries(kcmdatetimehelper KF5::Auth KF5::ConfigCore KF5::KDE4Support) +install(TARGETS kcmdatetimehelper DESTINATION ${LIBEXEC_INSTALL_DIR}) + +kauth_install_helper_files(kcmdatetimehelper org.kde.kcontrol.kcmclock root) + +kauth_install_actions(org.kde.kcontrol.kcmclock kcmclock_actions.actions) + +########### install files ############### + +install( FILES clock.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcms/dateandtime/Messages.sh b/kcms/dateandtime/Messages.sh new file mode 100644 index 0000000000..749f180170 --- /dev/null +++ b/kcms/dateandtime/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$EXTRACTRC *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcmkclock.pot diff --git a/kcms/dateandtime/clock.desktop b/kcms/dateandtime/clock.desktop new file mode 100644 index 0000000000..2c23adad7d --- /dev/null +++ b/kcms/dateandtime/clock.desktop @@ -0,0 +1,196 @@ +[Desktop Entry] +Exec=kcmshell5 clock +Icon=preferences-system-time +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/clock/index.html + +X-KDE-Library=kcm_clock +X-KDE-ParentApp=kcontrol + +#should this be in system settings when we can get to it through the panel? +#Answer: Yes. +X-KDE-System-Settings-Parent-Category=system-administration + +Name=Date & Time +Name[af]=Datum & Tyd +Name[ar]=التاريخ و الوقت +Name[ast]=Data y hora +Name[be]=Дата і час +Name[be@latin]=Data j čas +Name[bg]=Дата и час +Name[bn]=তারিখ এবং সময় +Name[bn_IN]=তারিখ ও সময় +Name[br]=Deiziad hag eur +Name[bs]=Datum i vrijeme +Name[ca]=Data i hora +Name[ca@valencia]=Data i hora +Name[cs]=Datum a čas +Name[csb]=Datum ë czas +Name[cy]=Dyddiad ac Amser +Name[da]=Dato og tid +Name[de]=Datum & Zeit +Name[el]=Ημερομηνία & Ώρα +Name[en_GB]=Date & Time +Name[eo]=Dato kaj Tempo +Name[es]=Fecha y hora +Name[et]=Kuupäev ja kellaaeg +Name[eu]=Data eta ordua +Name[fa]=تاریخ و زمان +Name[fi]=Aika ja päiväys +Name[fr]=Date et heure +Name[fy]=Datum en Tiid +Name[ga]=Dáta agus Am +Name[gl]=Data e hora +Name[gu]=તારીખ & સમય +Name[he]=תאריך ושעה +Name[hi]=तारीख़ व समय +Name[hne]=तारीक अउ समय +Name[hr]=Datum i vrijeme +Name[hsb]=Datum & čas +Name[hu]=Dátum és idő +Name[ia]=Data & Tempore +Name[id]=Tanggal & Waktu +Name[is]=Dagsetning og tími +Name[it]=Data e ora +Name[ja]=日付と時刻 +Name[ka]=თარიღი და დრო +Name[kk]=Күн мен уақыт +Name[km]=កាល​បរិច្ឆេទ និង​ពេលវេលា +Name[kn]=ದಿನಾಂಕ ಮತ್ತು ಸಮಯ +Name[ko]=날짜와 시간 +Name[ku]=Roj & Dem +Name[lt]=Data ir laikas +Name[lv]=Datums un laiks +Name[mai]=दिनाँक आ समय +Name[mk]=Датум и време +Name[ml]=തീയതിയും സമയവും +Name[mr]=दिनांक व वेळ +Name[ms]=Tarikh & Masa +Name[nb]=Dato og klokkeslett +Name[nds]=Datum & Tiet +Name[ne]=मिति र समय +Name[nl]=Datum en Tijd +Name[nn]=Dato og klokkeslett +Name[oc]=Data e ora +Name[or]=ତାରିଖ ଏବଂ ସମୟ +Name[pa]=ਮਿਤੀ ਅਤੇ ਟਾਈਮ +Name[pl]=Data i czas +Name[pt]=Data e Hora +Name[pt_BR]=Data e hora +Name[ro]=Data și ora +Name[ru]=Дата и время +Name[se]=Dáhton ja áigi +Name[si]=දිනය සහ වේලාව +Name[sk]=Dátum a čas +Name[sl]=Datum in čas +Name[sr]=Датум и време +Name[sr@ijekavian]=Датум и вријеме +Name[sr@ijekavianlatin]=Datum i vrijeme +Name[sr@latin]=Datum i vreme +Name[sv]=Datum och tid +Name[ta]=தேதி & நேரம் +Name[te]=తేది & సమయం +Name[tg]=Сана ва вақт +Name[th]=วันและเวลา +Name[tr]=Tarih ve Saat +Name[ug]=چېسلا & ۋاقىت +Name[uk]=Дата і час +Name[uz]=Sana va vaqt +Name[uz@cyrillic]=Сана ва вақт +Name[vi]=Ngày & giờ +Name[wa]=Date et eure +Name[xh]=Umhla & Ixesha +Name[x-test]=xxDate & Timexx +Name[zh_CN]=日期和时间 +Name[zh_TW]=日期和時間 + +Comment=Configure date and time settings +Comment[bs]=Podesi datum i vrijeme +Comment[ca]=Configuració de l'arranjament de la data i de l'hora +Comment[ca@valencia]=Configuració de l'arranjament de la data i de l'hora +Comment[cs]=Nastavení voleb data a času +Comment[da]=Indstilling af dato og tid +Comment[de]=Einstellungen für Datum und Zeit ändern +Comment[el]=Διαμόρφωση ρυθμίσεων ώρας και ημερομηνίας +Comment[es]=Configurar las preferencias de fecha y hora +Comment[et]=Kuupäeva ja kellaaja seadistamine +Comment[eu]=Konfiguratu data- eta ordu-ezarpenak +Comment[fi]=Aika-asetukset +Comment[fr]=Configuration des paramètres de date et d'heure +Comment[ga]=Cumraigh socruithe dáta agus ama +Comment[gl]=Configurar as opcións da data e hora +Comment[he]=הגדר זמן ותאריך +Comment[hu]=Dátum- és időbeállítások +Comment[ia]=Configura preferentias de data e tempore +Comment[is]=Stilla dagsetningu og tíma +Comment[it]=Configura le impostazioni della data e ora +Comment[kk]=Күн мен уақыт параметрлерін баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ការ​កំណត់កាលបរិច្ឆេទ និង​ពេលវេលា +Comment[ko]=날짜 및 시간 설정 변경 +Comment[lt]=Konfigūruoti datos ir laiko nustatymus +Comment[mr]=दिनांक व वेळ संयोजीत करा +Comment[nb]=Innstillinger for dato og tid +Comment[nds]=Instellen vun Datum un Tiet fastleggen +Comment[nl]=Instellingen van datum en tijd instellen +Comment[pa]=ਮਿਤੀ ਅਤੇ ਸਮਾਂ ਸੈਟਿੰਗ ਸੰਰਚਨਾ +Comment[pl]=Konfiguruj ustawienia daty i czasu +Comment[pt]=Configuração das opções da data e hora +Comment[pt_BR]=Configuração das opções de data e hora +Comment[ro]=Configurează opțiunile de dată și oră +Comment[ru]=Настройка даты и времени +Comment[sk]=Nastaviť nastavenia dátumu a času +Comment[sl]=Nastavi datum in čas +Comment[sr]=Подешавање датума и времена +Comment[sr@ijekavian]=Подешавање датума и времена +Comment[sr@ijekavianlatin]=Podešavanje datuma i vremena +Comment[sr@latin]=Podešavanje datuma i vremena +Comment[sv]=Anpassa inställningar av datum och tid +Comment[tr]=Tarih ve saat ayarlarını yapılandır +Comment[uk]=Налаштування параметрів дати і часу +Comment[vi]=Cấu hình thiết lập ngày giờ +Comment[x-test]=xxConfigure date and time settingsxx +Comment[zh_CN]=配置日期和时间设置 +Comment[zh_TW]=設定日期與時間 + +X-KDE-Keywords=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time +X-KDE-Keywords[bs]=sat,datum,vrijeme,vremenska zona,ntp,sinkronizirati,kalendar,lokalno vrijeme,regionalno vrijeme +X-KDE-Keywords[ca]=rellotge,data,hora,zona horària,ntp,sincronització,calendari,hora local,hora de la regió +X-KDE-Keywords[ca@valencia]=rellotge,data,hora,zona horària,ntp,sincronització,calendari,hora local,hora de la regió +X-KDE-Keywords[da]=ur,dato,tid,tidszone,ntp,synkroniser,kalender,lokal tid,regional tid +X-KDE-Keywords[de]=Uhr,Datum,Zeit,Zeitzone,ntp,synchronisation,abgleich,Kalender, +X-KDE-Keywords[el]=ρολόι,ημερομηνία,ώρα,ζώνη ώρας,ntp,συγχρονισμός,ημερολόγιο,τοπική ώρα,ώρα περιοχής +X-KDE-Keywords[es]=reloj,fecha,hora,zona horaria,ntp,sincronizar,calendario,hora local,hora regional +X-KDE-Keywords[et]=kell,kuupäev,kellaaeg,ajavöönd,ntp,sünkroonimine,sünkroniseerimine,kalender,kohalik aeg,vööndiaeg +X-KDE-Keywords[eu]=erloju,data,ordu,ordu-zona,ntp,sinkronizatu,egutegi,ordu lokal,eskualdeko ordu +X-KDE-Keywords[fi]=kello,päivä,päiväys,päivämäärä,aika,aikavyöhyke,ntp,synkronointi,kalenteri,paikallinen aika,alueellinen aika +X-KDE-Keywords[fr]=horloge, date, heure, zone horaire, ntp, synchronisation, calendrier, heure locale, heure régionale +X-KDE-Keywords[gl]=reloxo, data, hora, fuso, fuso horario, ntp, sincronizar, calendario, almanaque, hora local +X-KDE-Keywords[hu]=óra,dátum,idő,időzóna,ntp,szinkronizáció,naptár,helyi idő,régió idő +X-KDE-Keywords[ia]=horologio,data,tempore,fuso horari,ntp,synchronisa,calendario,tempore local,tempore de region +X-KDE-Keywords[it]=orologio,data,ora,fuso orario,ntp,sincronizza,calendario,ora locale,ora regionale +X-KDE-Keywords[kk]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time +X-KDE-Keywords[km]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time +X-KDE-Keywords[ko]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,시간,날짜,시간대,동기화 +X-KDE-Keywords[mr]=घड्याळ, दिनांक, वेळ, समय क्षेत्र, एन टी पी , सिन्क्रोनाइज़, दिनदर्शिका, स्थानिक वेळ, क्षेत्रीय वेळ +X-KDE-Keywords[nb]=klokke,dato,tid,tidssone,ntp,synkronisere,kalender,lokal tid,regiontid +X-KDE-Keywords[nds]=Klock,Datum,Tiet,Tietrebeet,NTP,synkroniseren,Kalenner,lokaal Tiet +X-KDE-Keywords[nl]=klok,datum,tijd,tijdzone,ntp,synchroniseren,agenda,lokale tijd,tijd in regio +X-KDE-Keywords[pl]=zegar,data,czas,strefa czasowa,ntp,synchronizuj,kalendarz,czas lokalny,czas regionalny +X-KDE-Keywords[pt]=relógio,data,hora,fuso-horário,ntp,sincronizar,calendário,hora local,hora regional +X-KDE-Keywords[pt_BR]=relógio,data,hora,fuso horário,ntp,sincronizar,calendário,hora local,hora regional +X-KDE-Keywords[ru]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,время,дата,часы,часовой пояс,синхронизировать,календарь,местное время,локальное время,региональное время +X-KDE-Keywords[sk]=hodiny,dátum,čas,časové pásmo,ntp,synchronizácia,kalendár,miestny čas,regionálny čas +X-KDE-Keywords[sl]=ura,datum,čas,časovni pas,ntp,uskladitev,koledar,krajevni čas,področni čas +X-KDE-Keywords[sr]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,сат,часовник,датум,време,временска зона,НТП,синхронизовати,календар,локално време +X-KDE-Keywords[sr@ijekavian]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,сат,часовник,датум,време,временска зона,НТП,синхронизовати,календар,локално време +X-KDE-Keywords[sr@ijekavianlatin]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,sat,časovnik,datum,vreme,vremenska zona,NTP,sinhronizovati,kalendar,lokalno vreme +X-KDE-Keywords[sr@latin]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,sat,časovnik,datum,vreme,vremenska zona,NTP,sinhronizovati,kalendar,lokalno vreme +X-KDE-Keywords[sv]=klocka,datum,tid,tidszon,ntp,synkronisera,kalender,lokal tid,områdestid +X-KDE-Keywords[tr]=saat,tarih,zaman,zaman dilimi,ntp,eşzamanlama,takvim,yerel saat,bölge saati +X-KDE-Keywords[uk]=годинник,дата,час,часовий пояс,синхронізація,календар,місцевий час,clock,date,time,time zone,ntp,synchronize,calendar,local time,region time +X-KDE-Keywords[x-test]=xxclock,date,time,time zone,ntp,synchronize,calendar,local time,region timexx +X-KDE-Keywords[zh_CN]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time,时钟,日期,时间,时区,同步,日历,本地时间,区域时间 +X-KDE-Keywords[zh_TW]=clock,date,time,time zone,ntp,synchronize,calendar,local time,region time + +Categories=Qt;KDE;X-KDE-settings-system; diff --git a/kcms/dateandtime/dateandtime.ui b/kcms/dateandtime/dateandtime.ui new file mode 100644 index 0000000000..c073b5e405 --- /dev/null +++ b/kcms/dateandtime/dateandtime.ui @@ -0,0 +1,198 @@ + + + Davide Bettio <davide.bettio@kdemail.net> + DateAndTime + + + + 0 + 0 + 613 + 463 + + + + + + + 0 + + + + Date and Time + + + + + + + + Set date and time &automatically: + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 21 + + + + + + + + Time server: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + timeServerList + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + Here you can change the system date's day of the month, month and year. + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Time Zone + + + + + + To change the local time zone, select your area from the list below. + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + Current local time zone: + + + + + + + true + + + Search + + + + + + + + + + + + + + + + K4TimeZoneWidget + QTreeWidget +
    k4timezonewidget.h
    +
    + + KTreeWidgetSearchLine + KLineEdit +
    ktreewidgetsearchline.h
    +
    + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KSeparator + QFrame +
    kseparator.h
    +
    + + KDatePicker + QFrame +
    kdatepicker.h
    +
    +
    + + +
    diff --git a/kcms/dateandtime/dtime.cpp b/kcms/dateandtime/dtime.cpp new file mode 100644 index 0000000000..e8511d1851 --- /dev/null +++ b/kcms/dateandtime/dtime.cpp @@ -0,0 +1,476 @@ +/* + * dtime.cpp + * + * Copyright (C) 1998 Luca Montecchiani + * + * Plasma analog-clock drawing code: + * + * Copyright 2007 by Aaron Seigo + * Copyright 2007 by Riccardo Iaconelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "dtime.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dtime.moc" + +#include "helper.h" + +Dtime::Dtime(QWidget * parent) + : QWidget(parent) +{ + setupUi(this); + + connect(setDateTimeAuto, SIGNAL(toggled(bool)), this, SLOT(serverTimeCheck())); + connect(setDateTimeAuto, SIGNAL(toggled(bool)), SLOT(configChanged())); + + timeServerList->setEditable(false); + connect(timeServerList, SIGNAL(activated(int)), SLOT(configChanged())); + connect(timeServerList, SIGNAL(editTextChanged(QString)), SLOT(configChanged())); + connect(setDateTimeAuto, SIGNAL(toggled(bool)), timeServerList, SLOT(setEnabled(bool))); + timeServerList->setEnabled(false); + timeServerList->setEditable(true); + findNTPutility(); + if (ntpUtility.isEmpty()) { + setDateTimeAuto->setEnabled(false); + setDateTimeAuto->setToolTip(i18n("No NTP utility has been found. " + "Install 'ntpdate' or 'rdate' command to enable automatic " + "updating of date and time.")); + } + + QVBoxLayout *v2 = new QVBoxLayout( timeBox ); + v2->setMargin( 0 ); + + kclock = new Kclock( timeBox ); + kclock->setObjectName("Kclock"); + kclock->setMinimumSize(150,150); + v2->addWidget( kclock ); + + v2->addSpacing( KDialog::spacingHint() ); + + QHBoxLayout *v3 = new QHBoxLayout( ); + v2->addLayout( v3 ); + + v3->addStretch(); + + timeEdit = new QTimeEdit( timeBox ); + timeEdit->setWrapping(true); + timeEdit->setDisplayFormat(KGlobal::locale()->use12Clock() ? "hh:mm:ss ap" : "HH:mm:ss"); + v3->addWidget(timeEdit); + + v3->addStretch(); + + QString wtstr = i18n("Here you can change the system time. Click into the" + " hours, minutes or seconds field to change the relevant value, either" + " using the up and down buttons to the right or by entering a new value."); + timeEdit->setWhatsThis( wtstr ); + + connect( timeEdit, SIGNAL(timeChanged(QTime)), SLOT(set_time()) ); + connect( cal, SIGNAL(dateChanged(QDate)), SLOT(changeDate(QDate))); + + connect( &internalTimer, SIGNAL(timeout()), SLOT(timeout()) ); + + kclock->setEnabled(false); + + //Timezone + connect( tzonelist, SIGNAL(itemSelectionChanged()), SLOT(configChanged()) ); + tzonesearch->setTreeWidget(tzonelist); +} + +void Dtime::currentZone() +{ + KTimeZone localZone = KSystemTimeZones::local(); + + if (localZone.abbreviations().isEmpty()) { + m_local->setText(i18nc("%1 is name of time zone", "Current local time zone: %1", + K4TimeZoneWidget::displayName(localZone))); + } else { + m_local->setText(i18nc("%1 is name of time zone, %2 is its abbreviation", + "Current local time zone: %1 (%2)", + K4TimeZoneWidget::displayName(localZone), + QString::fromUtf8(localZone.abbreviations().first()))); + } +} + +void Dtime::serverTimeCheck() { + bool enabled = !setDateTimeAuto->isChecked(); + cal->setEnabled(enabled); + timeEdit->setEnabled(enabled); + //kclock->setEnabled(enabled); +} + +void Dtime::findNTPutility(){ + QByteArray envpath = qgetenv("PATH"); + if (!envpath.isEmpty() && envpath[0] == ':') { + envpath = envpath.mid(1); + } + + QString path = "/sbin:/usr/sbin:"; + if (!envpath.isEmpty()) { + path += QString::fromLocal8Bit(envpath); + } else { + path += QLatin1String("/bin:/usr/bin"); + } + + foreach(const QString &possible_ntputility, QStringList() << "ntpdate" << "rdate" ) { + if( !((ntpUtility = KStandardDirs::findExe(possible_ntputility, path)).isEmpty()) ) { + kDebug() << "ntpUtility = " << ntpUtility; + return; + } + } + + kDebug() << "ntpUtility not found!"; +} + +void Dtime::set_time() +{ + if( ontimeout ) + return; + + internalTimer.stop(); + + time = timeEdit->time(); + kclock->setTime( time ); + + emit timeChanged( true ); +} + +void Dtime::changeDate(const QDate &d) +{ + date = d; + emit timeChanged( true ); +} + +void Dtime::configChanged(){ + emit timeChanged( true ); +} + +void Dtime::load() +{ + // The config is actually written to the system config, but the user does not have any local config, + // since there is nothing writing it. + KConfig _config( "kcmclockrc", KConfig::NoGlobals ); + KConfigGroup config(&_config, "NTP"); + timeServerList->clear(); + timeServerList->addItems(config.readEntry("servers", + i18n("Public Time Server (pool.ntp.org),\ +asia.pool.ntp.org,\ +europe.pool.ntp.org,\ +north-america.pool.ntp.org,\ +oceania.pool.ntp.org")).split(',', QString::SkipEmptyParts)); + setDateTimeAuto->setChecked(config.readEntry("enabled", false)); + + // Reset to the current date and time + time = QTime::currentTime(); + date = QDate::currentDate(); + cal->setDate(date); + + // start internal timer + internalTimer.start( 1000 ); + + timeout(); + + //Timezone + currentZone(); + + // read the currently set time zone + tzonelist->setSelected(KSystemTimeZones::local().name(), true); +} + +void Dtime::save( QVariantMap& helperargs ) +{ + // Save the order, but don't duplicate! + QStringList list; + if( timeServerList->count() != 0) + list.append(timeServerList->currentText()); + for ( int i=0; icount();i++ ) { + QString text = timeServerList->itemText(i); + if( !list.contains(text) ) + list.append(text); + // Limit so errors can go away and not stored forever + if( list.count() == 10) + break; + } + + helperargs["ntp"] = true; + helperargs["ntpServers"] = list; + helperargs["ntpEnabled"] = setDateTimeAuto->isChecked(); + helperargs["ntpUtility"] = ntpUtility; + + if(setDateTimeAuto->isChecked() && !ntpUtility.isEmpty()){ + // NTP Time setting - done in helper + timeServer = timeServerList->currentText(); + kDebug() << "Setting date from time server " << timeServer; + } + else { + // User time setting + QDateTime dt(date, QTime(timeEdit->time())); + + kDebug() << "Set date " << dt; + + helperargs["date"] = true; + helperargs["newdate"] = QString::number(dt.toTime_t()); + helperargs["olddate"] = QString::number(::time(0)); + } + + // restart time + internalTimer.start( 1000 ); + + QStringList selectedZones(tzonelist->selection()); + + if (selectedZones.count() > 0) { + QString selectedzone(selectedZones[0]); + helperargs["tz"] = true; + helperargs["tzone"] = selectedzone; + } else { + helperargs["tzreset"] = true; // // make the helper reset the timezone + } + + currentZone(); +} + +void Dtime::processHelperErrors( int code ) +{ + if( code & ClockHelper::NTPError ) { + KMessageBox::error( this, i18n("Unable to contact time server: %1.", timeServer) ); + setDateTimeAuto->setChecked( false ); + } + if( code & ClockHelper::DateError ) { + KMessageBox::error( this, i18n("Can not set date.")); + } + if( code & ClockHelper::TimezoneError) + KMessageBox::error( this, i18n("Error setting new time zone."), + i18n("Time zone Error")); +} + +void Dtime::timeout() +{ + // get current time + time = QTime::currentTime(); + + ontimeout = true; + timeEdit->setTime(time); + ontimeout = false; + + kclock->setTime( time ); +} + +QString Dtime::quickHelp() const +{ + return i18n("

    Date & Time

    This system settings module can be used to set the system date and" + " time. As these settings do not only affect you as a user, but rather the whole system, you" + " can only change these settings when you start the System Settings as root. If you do not have" + " the root password, but feel the system time should be corrected, please contact your system" + " administrator."); +} + +Kclock::Kclock(QWidget *parent) + : QWidget(parent) +{ + m_theme = new Plasma::Svg(this); + m_theme->setImagePath("widgets/clock"); + m_theme->setContainsMultipleImages(true); +} + +Kclock::~Kclock() +{ + delete m_theme; +} + +void Kclock::showEvent( QShowEvent *event ) +{ + setClockSize( size() ); + QWidget::showEvent( event ); +} + +void Kclock::resizeEvent( QResizeEvent * ) +{ + setClockSize( size() ); +} + +void Kclock::setClockSize(const QSize &size) +{ + int dim = qMin(size.width(), size.height()); + QSize newSize = QSize(dim, dim); + + if (newSize != m_faceCache.size()) { + m_faceCache = QPixmap(newSize); + m_handsCache = QPixmap(newSize); + m_glassCache = QPixmap(newSize); + + m_theme->resize(newSize); + m_repaintCache = RepaintAll; + } +} + +void Kclock::setTime(const QTime &time) +{ + if (time.minute() != this->time.minute() || time.hour() != this->time.hour()) { + if (m_repaintCache == RepaintNone) { + m_repaintCache = RepaintHands; + } + } + this->time = time; + update(); +} + +void Kclock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName) +{ + // this code assumes the following conventions in the svg file: + // - the _vertical_ position of the hands should be set with respect to the center of the face + // - the _horizontal_ position of the hands does not matter + // - the _shadow_ elements should have the same vertical position as their _hand_ element counterpart + + QRectF elementRect; + QString name = handName + "HandShadow"; + if (m_theme->hasElement(name)) { + p->save(); + + elementRect = m_theme->elementRect(name); + if( rect.height() < 64 ) + elementRect.setWidth( elementRect.width() * 2.5 ); + static const QPoint offset = QPoint(2, 3); + + p->translate(rect.x() + (rect.width() / 2) + offset.x(), rect.y() + (rect.height() / 2) + offset.y()); + p->rotate(rotation); + p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); + m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); + + p->restore(); + } + + p->save(); + + name = handName + "Hand"; + elementRect = m_theme->elementRect(name); + if (rect.height() < 64) { + elementRect.setWidth(elementRect.width() * 2.5); + } + + p->translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); + p->rotate(rotation); + p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); + m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); + + p->restore(); +} + +void Kclock::paintInterface(QPainter *p, const QRect &rect) +{ + const bool m_showSecondHand = true; + + // compute hand angles + const qreal minutes = 6.0 * time.minute() - 180; + const qreal hours = 30.0 * time.hour() - 180 + + ((time.minute() / 59.0) * 30.0); + qreal seconds = 0; + if (m_showSecondHand) { + static const double anglePerSec = 6; + seconds = anglePerSec * time.second() - 180; + } + + // paint face and glass cache + QRect faceRect = m_faceCache.rect(); + if (m_repaintCache == RepaintAll) { + m_faceCache.fill(Qt::transparent); + m_glassCache.fill(Qt::transparent); + + QPainter facePainter(&m_faceCache); + QPainter glassPainter(&m_glassCache); + facePainter.setRenderHint(QPainter::SmoothPixmapTransform); + glassPainter.setRenderHint(QPainter::SmoothPixmapTransform); + + m_theme->paint(&facePainter, m_faceCache.rect(), "ClockFace"); + + glassPainter.save(); + QRectF elementRect = QRectF(QPointF(0, 0), m_theme->elementSize("HandCenterScrew")); + glassPainter.translate(faceRect.width() / 2 - elementRect.width() / 2, faceRect.height() / 2 - elementRect.height() / 2); + m_theme->paint(&glassPainter, elementRect, "HandCenterScrew"); + glassPainter.restore(); + + m_theme->paint(&glassPainter, faceRect, "Glass"); + + // get vertical translation, see drawHand() for more details + m_verticalTranslation = m_theme->elementRect("ClockFace").center().y(); + } + + // paint hour and minute hands cache + if (m_repaintCache == RepaintHands || m_repaintCache == RepaintAll) { + m_handsCache.fill(Qt::transparent); + + QPainter handsPainter(&m_handsCache); + handsPainter.drawPixmap(faceRect, m_faceCache, faceRect); + handsPainter.setRenderHint(QPainter::SmoothPixmapTransform); + + drawHand(&handsPainter, faceRect, m_verticalTranslation, hours, "Hour"); + drawHand(&handsPainter, faceRect, m_verticalTranslation, minutes, "Minute"); + } + + // reset repaint cache flag + m_repaintCache = RepaintNone; + + // paint caches and second hand + QRect targetRect = faceRect; + if (targetRect.width() < rect.width()) { + targetRect.moveLeft((rect.width() - targetRect.width()) / 2); + } + + p->drawPixmap(targetRect, m_handsCache, faceRect); + if (m_showSecondHand) { + p->setRenderHint(QPainter::SmoothPixmapTransform); + drawHand(p, targetRect, m_verticalTranslation, seconds, "Second"); + } + p->drawPixmap(targetRect, m_glassCache, faceRect); +} + +void Kclock::paintEvent( QPaintEvent * ) +{ + QPainter paint(this); + + paint.setRenderHint(QPainter::Antialiasing); + paintInterface(&paint, rect()); +} + diff --git a/kcms/dateandtime/dtime.h b/kcms/dateandtime/dtime.h new file mode 100644 index 0000000000..1a90698c6c --- /dev/null +++ b/kcms/dateandtime/dtime.h @@ -0,0 +1,121 @@ +/* + * dtime.h + * + * Copyright (C) 1998 Luca Montecchiani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef dtime_included +#define dtime_included + +#include "ui_dateandtime.h" + +#include +#include +#include +#include +#include +//Added by qt3to4: +#include + +#include +#include + +class Kclock; +class QTimeEdit; + +namespace Plasma { + class Svg; +} + +class Dtime : public QWidget, public Ui::DateAndTime +{ + Q_OBJECT + public: + Dtime( QWidget *parent=0 ); + + void save( QVariantMap &helperargs ); + void processHelperErrors( int code ); + void load(); + + QString quickHelp() const; + +Q_SIGNALS: + void timeChanged(bool); + + private Q_SLOTS: + void configChanged(); + void serverTimeCheck(); + void timeout(); + void set_time(); + void changeDate(const QDate&); + +private: + void currentZone(); + void findNTPutility(); + QString ntpUtility; + + QTimeEdit *timeEdit; + + Kclock *kclock; + + QTime time; + QDate date; + QTimer internalTimer; + + QString timeServer; + int BufI; + bool refresh; + bool ontimeout; +}; + +class Kclock : public QWidget +{ + Q_OBJECT + +public: + Kclock( QWidget *parent=0 ); + ~Kclock(); + + void setTime(const QTime&); + +protected: + virtual void paintEvent( QPaintEvent *event ); + virtual void showEvent( QShowEvent *event ); + virtual void resizeEvent( QResizeEvent *event ); + +private: + void setClockSize(const QSize &size); + void drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName); + void paintInterface(QPainter *p, const QRect &rect); + +private: + QTime time; + Plasma::Svg *m_theme; + enum RepaintCache { + RepaintNone, + RepaintAll, + RepaintHands + }; + RepaintCache m_repaintCache; + QPixmap m_faceCache; + QPixmap m_handsCache; + QPixmap m_glassCache; + qreal m_verticalTranslation; +}; + +#endif // dtime_included diff --git a/kcms/dateandtime/helper.cpp b/kcms/dateandtime/helper.cpp new file mode 100644 index 0000000000..11207751ae --- /dev/null +++ b/kcms/dateandtime/helper.cpp @@ -0,0 +1,254 @@ +/* + * tzone.cpp + * + * Copyright (C) 1998 Luca Montecchiani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + + A helper that's run using KAuth and does the system modifications. + +*/ + +#include "helper.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(USE_SOLARIS) +#include +#include +#include +#include +#endif + +// We cannot rely on the $PATH environment variable, because D-Bus activation +// clears it. So we have to use a reasonable default. +static const QString exePath = QLatin1String("/usr/sbin:/usr/bin:/sbin:/bin"); + +int ClockHelper::ntp( const QStringList& ntpServers, bool ntpEnabled, + const QString& ntpUtility ) +{ + int ret = 0; + + // write to the system config file + QFile config_file(KDE_CONFDIR "/kcmclockrc"); + if(!config_file.exists()) { + config_file.open(QIODevice::WriteOnly); + config_file.close(); + config_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther); + } + KConfig _config(config_file.fileName(), KConfig::SimpleConfig); + KConfigGroup config(&_config, "NTP"); + config.writeEntry("servers", ntpServers ); + config.writeEntry("enabled", ntpEnabled ); + + if ( ntpEnabled && !ntpUtility.isEmpty() ) { + // NTP Time setting + QString timeServer = ntpServers.first(); + if( timeServer.indexOf( QRegExp(".*\\(.*\\)$") ) != -1 ) { + timeServer.replace( QRegExp(".*\\("), "" ); + timeServer.replace( QRegExp("\\).*"), "" ); + // Would this be better?: s/^.*\(([^)]*)\).*$/\1/ + } + + KProcess proc; + proc << ntpUtility << timeServer; + if ( proc.execute() != 0 ) { + ret |= NTPError; + } else { + toHwclock(); + } + } else if( ntpEnabled ) { + ret |= NTPError; + } + + return ret; +} + +int ClockHelper::date( const QString& newdate, const QString& olddate ) +{ + struct timeval tv; + + tv.tv_sec = newdate.toULong() - olddate.toULong() + time(0); + tv.tv_usec = 0; + if (settimeofday(&tv, 0)) { + return DateError; + } + + toHwclock(); + return 0; +} + +// on non-Solaris systems which do not use /etc/timezone? +int ClockHelper::tz( const QString& selectedzone ) +{ + int ret = 0; +#if defined(USE_SOLARIS) // MARCO + + KTemporaryFile tf; + tf.setPrefix("kde-tzone"); + tf.open(); + QTextStream ts(&tf); + + QFile fTimezoneFile(INITFILE); + bool updatedFile = false; + + if (fTimezoneFile.open(QIODevice::ReadOnly)) + { + bool found = false; + + QTextStream is(&fTimezoneFile); + + for (QString line = is.readLine(); !line.isNull(); + line = is.readLine()) + { + if (line.find("TZ=") == 0) + { + ts << "TZ=" << selectedzone << endl; + found = true; + } + else + { + ts << line << endl; + } + } + + if (!found) + { + ts << "TZ=" << selectedzone << endl; + } + + updatedFile = true; + fTimezoneFile.close(); + } + + if (updatedFile) + { + ts.device()->reset(); + fTimezoneFile.remove(); + + if (fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + QTextStream os(&fTimezoneFile); + + for (QString line = ts->readLine(); !line.isNull(); + line = ts->readLine()) + { + os << line << endl; + } + + fchmod(fTimezoneFile.handle(), + S_IXUSR | S_IRUSR | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH); + fTimezoneFile.close(); + } + } + + + QString val = selectedzone; +#else + QString tz = "/usr/share/zoneinfo/" + selectedzone; + + QString zic = KStandardDirs::findExe("zic", exePath); + if (!zic.isEmpty()) { + KProcess::execute(zic, QStringList() << "-l" << selectedzone); + } else if (!QFile::remove("/etc/localtime")) { + ret |= TimezoneError; + } else if (!QFile::copy(tz, "/etc/localtime")) { + ret |= TimezoneError; + } + + QFile fTimezoneFile("/etc/timezone"); + + if (fTimezoneFile.exists() && fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate) ) { + QTextStream t(&fTimezoneFile); + t << selectedzone; + fTimezoneFile.close(); + } + + QString val = ':' + tz; +#endif // !USE_SOLARIS + + setenv("TZ", val.toAscii(), 1); + tzset(); + + return ret; +} + +int ClockHelper::tzreset() +{ +#if !defined(USE_SOLARIS) // Do not update the System! + unlink( "/etc/timezone" ); + unlink( "/etc/localtime" ); + + setenv("TZ", "", 1); + tzset(); +#endif // !USE SOLARIS + return 0; +} + +void ClockHelper::toHwclock() +{ + QString hwclock = KStandardDirs::findExe("hwclock", exePath); + if (!hwclock.isEmpty()) { + KProcess::execute(hwclock, QStringList() << "--systohc"); + } +} + +ActionReply ClockHelper::save(const QVariantMap &args) +{ + bool _ntp = args.value("ntp").toBool(); + bool _date = args.value("date").toBool(); + bool _tz = args.value("tz").toBool(); + bool _tzreset = args.value("tzreset").toBool(); + + KComponentData data( "kcmdatetimehelper" ); + + int ret = 0; // error code +// The order here is important + if( _ntp ) + ret |= ntp( args.value("ntpServers").toStringList(), args.value("ntpEnabled").toBool(), args.value("ntpUtility").toString() ); + if( _date ) + ret |= date( args.value("newdate").toString(), args.value("olddate").toString() ); + if( _tz ) + ret |= tz( args.value("tzone").toString() ); + if( _tzreset ) + ret |= tzreset(); + + if (ret == 0) { + return ActionReply::SuccessReply(); + } else { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setErrorCode(static_cast(ret)); + return reply; + } +} + +KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmclock", ClockHelper) diff --git a/kcms/dateandtime/helper.h b/kcms/dateandtime/helper.h new file mode 100644 index 0000000000..2b75805d72 --- /dev/null +++ b/kcms/dateandtime/helper.h @@ -0,0 +1,54 @@ +/* + * main.h + * + * Copyright (C) 1998 Luca Montecchiani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef CLOCK_HELPER_H +#define CLOCK_HELPER_H + +#include + +using namespace KAuth; + +class ClockHelper : public QObject +{ + Q_OBJECT + + public: + enum + { + CallError = 1 << 0, + TimezoneError = 1 << 1, + NTPError = 1 << 2, + DateError = 1 << 3 + }; + + public Q_SLOTS: + ActionReply save(const QVariantMap &map); + + private: + int ntp(const QStringList& ntpServers, bool ntpEnabled, + const QString& ntpUtility); + int date(const QString& newdate, const QString& olddate); + int tz(const QString& selectedzone); + int tzreset(); + + void toHwclock(); +}; + +#endif // CLOCK_HELPER_H diff --git a/kcms/dateandtime/kcmclock_actions.actions b/kcms/dateandtime/kcmclock_actions.actions new file mode 100644 index 0000000000..14b4433bb8 --- /dev/null +++ b/kcms/dateandtime/kcmclock_actions.actions @@ -0,0 +1,209 @@ +[Domain] +Name=Date and Time Control Module +Name[ar]=وحدة التحكم بالتاريخ والوقت +Name[ast]=Módulu de control de data y hora +Name[bg]=Управление на дата и час +Name[bn]=তারিখ এবং সময় নিয়ন্ত্রণ মডিউল +Name[bs]=Kontrolni modul datuma i vremena +Name[ca]=Mòdul de control de data i hora +Name[ca@valencia]=Mòdul de control de data i hora +Name[cs]=Ovládací modul data a času +Name[csb]=Mòduł kòntrolë datuma ë czasu +Name[da]=Kontrolmodul til dato og tid +Name[de]=Kontrollmodul für Datum und Zeit +Name[el]=Μονάδα ελέγχου ημερομηνίας και ώρας +Name[en_GB]=Date and Time Control Module +Name[eo]=Kontrol-Modulo de Dato kaj Tempo +Name[es]=Módulo de control de fecha y hora +Name[et]=Kuupäeva ja kellaaja juhtimismoodul +Name[eu]=Data eta ordua kontrolatzeko modulua +Name[fi]=Aika-asetukset +Name[fr]=Module de contrôle de la date et de l'heure +Name[fy]=Datum en tiid kontrôle module +Name[ga]=Modúl Rialaithe Dáta agus Am +Name[gl]=Módulo de control da data e hora +Name[gu]=તારીખ અને સમય નિયંત્રણ મોડ્યુલ +Name[he]=מודול הגדרות תאריך ושעה +Name[hi]=तारीख़ और समय तंत्र मोड्यूल +Name[hr]=Upravljački modul za datum i vrijeme +Name[hu]=Dátum és idő beállítómodul +Name[ia]=Modulo de controlo de data e tempore +Name[id]=Modul Kontrol Tanggal dan Waktu +Name[is]=Stjórneining fyrir dagsetningu og tíma +Name[it]=Modulo di controllo di data e ora +Name[ja]=日付と時刻の設定モジュール +Name[kk]=Күні мен уақытын басқару модулі +Name[km]=ម៉ូឌុល​ត្រួតពិនិត្យ​ពេលវេលា និងកាលបរិច្ឆេទ +Name[kn]=ದಿನಾಂಕ ಮತ್ತು ಸಮಯ ನಿಯಂತ್ರಣ ಘಟಕ +Name[ko]=날짜와 시간 제어 모듈 +Name[lt]=Datos ir laiko valdymo modulis +Name[lv]=Datuma un laika kontroles modulis +Name[mk]=Контролен модул за датум и време +Name[ml]=തീയതിയും സമയവും നിയന്ത്രിയ്ക്കുന്ന ഭാഗം +Name[mr]=दिनांक व वेळ नियंत्रण विभाग +Name[nb]=Styremodul for dato og tid +Name[nds]=Datum- un Tiet-Kuntrullmoduul +Name[nl]=Controlemodule van datum en tijd +Name[nn]=Kontrollmodul for dato og klokkeslett +Name[pa]=ਮਿਤੀ ਅਤੇ ਟਾਈਮ ਕੰਟਰੋਲ ਮੋਡੀਊਲ +Name[pl]=Moduł sterowania datą i czasem +Name[pt]=Módulo de Controlo da Data e Hora +Name[pt_BR]=Módulo de controle da data e hora +Name[ro]=Modul de control dată și oră +Name[ru]=Модуль настройки даты и времени +Name[si]=දිනය සහ වේලා පාලක මොඩියුලය +Name[sk]=Modul pre nastavenie dátumu a času +Name[sl]=Nadzorni modul za datum in čas +Name[sr]=Контролни модул датума и времена +Name[sr@ijekavian]=Контролни модул датума и времена +Name[sr@ijekavianlatin]=Kontrolni modul datuma i vremena +Name[sr@latin]=Kontrolni modul datuma i vremena +Name[sv]=Inställningsmodul för datum och tid +Name[tg]=Сана ва вақт +Name[th]=มอดูลควบคุมสำหรับวันและเวลา +Name[tr]=Tarih ve Saat Denetim Modülü +Name[ug]=چېسلا ۋە ۋاقىت تىزگىن بۆلىكى +Name[uk]=Модуль керування датою і часом +Name[vi]=Mô-đun điều khiển ngày giờ +Name[wa]=Module di controle del date eyet d' l' eure +Name[x-test]=xxDate and Time Control Modulexx +Name[zh_CN]=日期和时间控制模块 +Name[zh_TW]=日期與時間控制模組 +Icon=preferences-system-time + +[org.kde.kcontrol.kcmclock.save] +Name=Save the date/time settings +Name[ar]=احفظ إعدادات التاريخ والوقت +Name[ast]=Guardar les preferencies de data/hora +Name[bg]=Запазване настройките за дата и час +Name[bn]=তারিখ এবং সময় সেটিংস সংরক্ষণ করো +Name[bs]=Sačuvaj postavke datuma i vremena +Name[ca]=Desa la configuració de data i hora +Name[ca@valencia]=Guarda la configuració de data i hora +Name[cs]=Uložit nastavení data a času +Name[csb]=Zapiszë nastôw datuma ë czasu +Name[da]=Gem dato- og tidsindstillinger +Name[de]=Einstellungen für Datum und Zeit speichern +Name[el]=Αποθήκευση ρυθμίσεων ημερομηνίας και ώρας +Name[en_GB]=Save the date/time settings +Name[eo]=Konservu Agordon de dato kaj tempo +Name[es]=Guardar las preferencias de fecha/hora +Name[et]=Kuupäeva ja kellaaja seadistuste salvestamine +Name[eu]=Gorde data- eta ordu-ezarpenak +Name[fi]=Tallenna päiväyksen ja ajan asetukset +Name[fr]=Enregistrer les paramètres de date et d'heure +Name[fy]=Datum en tiid ynstellings bewarje +Name[ga]=Sábháil na socruithe dáta/ama +Name[gl]=Garda a configuración da data e hora +Name[gu]=તારીખ/સમય ગોઠવણીઓ સંગ્રહો +Name[he]=שמירת הגדרות התאריך והשעה +Name[hi]=तारीख़ तथा समय विन्यास सहेजें +Name[hr]=Spremi datumske/vremenske postavke +Name[hu]=A dátum- és időbeállítások mentése +Name[ia]=Salveguarda le preferentias de data & tempore +Name[id]=Simpan pengaturan tanggal/waktu +Name[is]=Vista stillingar dagssetningar og tíma +Name[it]=Salva le impostazioni di data e ora +Name[ja]=日付と時刻の設定を保存 +Name[kk]=Күн/уақыт параметрлерін сақтау +Name[km]=រក្សាទុកការកំណត់​ពេលវេលា/កាលបរិច្ឆេទ +Name[kn]=ದಿನಾಂಕ/ಸಮಯ ಸಂಯೋಜನೆಗಳನ್ನು ಉಳಿಸಿ +Name[ko]=날짜와 시간 설정 저장 +Name[lt]=Duomenų ir laiko nustatymai +Name[lv]=Saglabāt datuma/laika iestatījumus +Name[mk]=Зачувување на поставувања за датум и време +Name[ml]=തീയതിയുടെ/സമയത്തിന്റെ സജ്ജീകരണങ്ങള്‍ സൂക്ഷിയ്ക്കുക +Name[mr]=दिनांक व वेळ संयोजना साठवा +Name[nb]=Lagre dato og klokkeslett-innstillinger +Name[nds]=Instellen vun Datum un Tiet sekern +Name[nl]=Datum- en tijdinstellingen opslaan +Name[nn]=Lagra innstillingane for dato og klokkeslett +Name[pa]=ਮਿਤੀ/ਸਮਾਂ ਸੈਟਿੰਗ ਸੰਭਾਲੋ +Name[pl]=Zapisz ustawienia daty/czasu +Name[pt]=Mudar a configuração da data/hora +Name[pt_BR]=Salvar as configurações de data e hora +Name[ro]=Salvează configurările datei și orei +Name[ru]=Сохранить параметры даты и времени +Name[si]=දිනය/වේලාව සිටුවම් සුරකින්න +Name[sk]=Uložiť nastavenia dátumu a času +Name[sl]=Shranjevanje nastavitev datuma in časa +Name[sr]=Сачувај поставке датума и времена +Name[sr@ijekavian]=Сачувај поставке датума и времена +Name[sr@ijekavianlatin]=Sačuvaj postavke datuma i vremena +Name[sr@latin]=Sačuvaj postavke datuma i vremena +Name[sv]=Spara inställningar av datum och tid +Name[tg]=Танзимоти сана ва вақт +Name[th]=จัดเก็บการตั้งค่าวันและเวลา +Name[tr]=Tarih ve saat ayarlarını kaydet +Name[ug]=چېسلا/ۋاقىت تەڭشىكىنى ساقلايدۇ +Name[uk]=Зберегти параметри дати/часу +Name[wa]=Schaper ls apontiaedjes del date et d' l' eure +Name[x-test]=xxSave the date/time settingsxx +Name[zh_CN]=保存日期和时间设置 +Name[zh_TW]=儲存日期和時間設定值 +Description=System policies prevent you from saving the date/time settings. +Description[ar]=إن سياسات النظام لا تسمح لك بحفظ إعدادات الوقت والتاريخ +Description[ast]=Les polítiques del sistema impiden guardar les preferencies de data/hora. +Description[bg]=Системните правила не позволяват запазване на настройките за дата и час. +Description[bn]=সিস্টেম পলিসি আপনাকে তারিখ এবং সময় সেটিংস সংরক্ষণ করতে দিচ্ছে না। +Description[bs]=Sistemske smjernice sprečavaju upisivanje postavki datuma i vremena. +Description[ca]=Les polítiques del sistema impedeixen que deseu la configuració de data i hora. +Description[ca@valencia]=Les polítiques del sistema impedeixen que guardeu la configuració de data i hora. +Description[cs]=Nastavení systému vám znemožňuje uložit datum/čas. +Description[csb]=Przez systemòwi nastôw nié mòżé zapisac nastôwù datuma ë czasu. +Description[da]=Systempolitikker forhindrer dig i at gemme dato- og tidsindstillinger. +Description[de]=Die Einstellungen für Datum und Zeit können aufgrund einer Systemrichtlinie nicht gespeichert werden. +Description[el]=Οι πολιτικές του συστήματος αποτρέπουν την αποθήκευση των ρυθμίσεων ώρας/ημερομηνίας. +Description[en_GB]=System policies prevent you from saving the date/time settings. +Description[es]=Las políticas del sistema le impiden guardar las preferencias de fecha/hora. +Description[et]=Süsteemi reeglid takistavad kuupäeva ja kellaaja seadistuste salvestamist. +Description[eu]=Sistemako gidalerroek data- eta ordu-ezarpenak gordetzea eragozten dizute +Description[fi]=Järjestelmämenettelytavat estävät sinua tallentamasta päivämäärä/aika-asetuksia. +Description[fr]=La stratégie système vous empêche d'enregistrer les paramètres de date et d'heure. +Description[fy]=Systeem belied foarkomt dat jo datum en tiid ynstellings bewarje kinne. +Description[ga]=De bharr polasaí an chórais, níl cead agat socruithe an dáta/ama a shábháil. +Description[gl]=As políticas do sistema non permiten que garde a configuración da data e hora. +Description[he]=מדיניות המערכת מונעת ממך שמירת הגדרות התאריך והשעה. +Description[hr]=Pravila sustava vam brane spremanje datumskih/vremenskih postavki. +Description[hu]=A rendszer házirendjei nem engedik Önnek a dátum- és időbeállítások mentését. +Description[ia]=Le politicas de systema preveni te ex salveguardar le preferentias de tempore/data. +Description[id]=Kebijakan sistem menghalangi anda untuk menyimpan pengaturan tanggal/waktu. +Description[is]=Öryggisstillingar kerfisins koma í veg fyrir að þú getir vistað tíma/dagsetningu. +Description[it]=Le regole di condotta del sistema impediscono di salvare le impostazioni di data e ora. +Description[ja]=システムポリシーにより、日時設定を保存することができません。 +Description[kk]=Күн/уақыт параметрлерін сақтауды болдырмайтын жүйелік ережелері. +Description[km]=គោលនយោបាយ​ប្រព័ន្ធ​ ការពារ​អ្នក​​មិន​ឲ្យ​រក្សាទុក​ការកំណត់​កាលបរិច្ឆេទ/ពេលវេលា ។ +Description[kn]=ವ್ಯವಸ್ಥೆಯ ಕಾರ್ಯನೀತಿ ನಿಮ್ಮನ್ನು ಸಮಯ/ದಿನಾಂಕ ಸಂಯೋಜನೆಗಳನ್ನು ಉಳಿಸಿಕೊಳ್ಳುವುದರಿಂದ ತಡೆಯುತ್ತಿವೆ +Description[ko]=시스템 정책 때문에 날짜와 시간 설정을 저장할 수 없습니다. +Description[lt]=Sistemos taisyklės neleidžia jums įrašyti datos/laiko. +Description[lv]=Sistēmas politikas neļauj jums saglabāt datuma/laika iestatījumus. +Description[mk]=Политиката на системот Ве спречува да ги зачувате поставувањата за датум и време. +Description[ml]=സിസ്റ്റത്തിലെ നയങ്ങള്‍ തിയ്യതി/സമയം ക്രമീകരിയ്ക്കുന്നതില്‍ നിന്നും നിങ്ങളെ തടയുന്നു. +Description[mr]=प्रणाली धोरण दिनांक व वेळ संयोजना संचयीत करण्यास प्रतिबंध करत आहे. +Description[nb]=Systembestemmelser gjør at du ikke kan lagre dato/tid-innstillingene +Description[nds]=De Systeemregeln verlöövt nich, dat Du de Instellen för Datum un Tiet sekerst. +Description[nl]=Systeembeleid voorkwam dat u de datum- en tijdinstellingen kon opslaan. +Description[nn]=Systemreglane hindrar deg i å lagra innstillingane for dato og klokkeslett. +Description[pa]=ਸਿਸਟਮ ਪਾਲਸੀ ਤੁਹਾਨੂੰ ਮਿਤੀ/ਸਮਾਂ ਸੈਟਿੰਗ ਬਦਲਣ ਤੋਂ ਤੁਹਾਨੂੰ ਰੋਕਦੀ ਹੈ। +Description[pl]=Polityka systemu nie pozwala ci na zapisanie ustawień daty/czasu. +Description[pt]=As políticas do sistema proíbem a modificação da configuração de data/hora. +Description[pt_BR]=As políticas do sistema não permitem a modificação das configurações de data e hora. +Description[ro]=Politicile sistemului vă interzic salvarea configurărilor de dată și oră. +Description[ru]=Правила безопасности запрещают вам управлять системной датой и временем. +Description[si]=පද්ධති ප්‍රතිපත්තො ඔබව දිනය/වේලාව සැකසුම් සුරැකිම වලකාලයි. +Description[sk]=Systémové politiky vám zabránili uložiť nastavenie dátumu a času. +Description[sl]=Sistemski pravilniki vam onemogočajo, da bi shranili nastavitve datuma in časa. +Description[sr]=Системске смернице спречавају уписивање поставки датума и времена. +Description[sr@ijekavian]=Системске смјернице спречавају уписивање поставки датума и времена. +Description[sr@ijekavianlatin]=Sistemske smjernice sprečavaju upisivanje postavki datuma i vremena. +Description[sr@latin]=Sistemske smernice sprečavaju upisivanje postavki datuma i vremena. +Description[sv]=Systemets policy förhindrar att du sparar inställningar av datum och tid. +Description[th]=นโยบายของระบบเพื่อป้องกันจากการจัดเก็บการตั้งค่าต่าง ๆ ของวันและเวลา +Description[tr]=Sistem politikaları tarih saat ayarlarını kaydetmenizi engelliyor. +Description[ug]=سىستېما بىخەتەرلىك تەدبىرىڭىز چېسلا/ۋاقىت تەڭشەكلەرنى ساقلاشقا يول قويمايدۇ. +Description[uk]=Відповідно до загальносистемних правил, ви не можете зберігати параметри дати/часу. +Description[wa]=Les politikes do sistinme vos espaitchèt d' schaper les apontiaedjes del date/eure. +Description[x-test]=xxSystem policies prevent you from saving the date/time settings.xx +Description[zh_CN]=系统安全策略不允许您保存日期/时间设置。 +Description[zh_TW]=系統政策不允許您儲存日期與時間的設定 +Policy=auth_admin diff --git a/kcms/dateandtime/main.cpp b/kcms/dateandtime/main.cpp new file mode 100644 index 0000000000..bf2d96ada8 --- /dev/null +++ b/kcms/dateandtime/main.cpp @@ -0,0 +1,117 @@ +/* + * main.cpp + * + * Copyright (C) 1998 Luca Montecchiani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "main.h" + +#include + +//Added by qt3to4: +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dtime.h" +#include "helper.h" + +#include +#include + +K_PLUGIN_FACTORY(KlockModuleFactory, registerPlugin();) +K_EXPORT_PLUGIN(KlockModuleFactory("kcmkclock")) + + +KclockModule::KclockModule(QWidget *parent, const QVariantList &) + : KCModule(parent/*, name*/) +{ + KAboutData *about = + new KAboutData(I18N_NOOP("kcmclock"), QString(), i18n("KDE Clock Control Module"), + QString(), QString(), KAboutData::License_GPL, + i18n("(c) 1996 - 2001 Luca Montecchiani")); + + about->addAuthor(i18n("Luca Montecchiani"), i18n("Original author"), QStringLiteral("m.luca@usa.net")); + about->addAuthor(i18n("Paul Campbell"), i18n("Current Maintainer"), QStringLiteral("paul@taniwha.com")); + about->addAuthor(i18n("Benjamin Meyer"), i18n("Added NTP support"), QStringLiteral("ben+kcmclock@meyerhome.net")); + setAboutData( about ); + setQuickHelp( i18n("

    Date & Time

    This control module can be used to set the system date and" + " time. As these settings do not only affect you as a user, but rather the whole system, you" + " can only change these settings when you start the System Settings as root. If you do not have" + " the root password, but feel the system time should be corrected, please contact your system" + " administrator.")); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(KDialog::spacingHint()); + + dtime = new Dtime(this); + layout->addWidget(dtime); + connect(dtime, SIGNAL(timeChanged(bool)), this, SIGNAL(changed(bool))); + + setButtons(Help|Apply); + + setNeedsAuthorization(true); + + process = NULL; +} + +void KclockModule::save() +{ + setDisabled(true); + + QVariantMap helperargs; + dtime->save( helperargs ); + + Action action = authAction(); + action.setArguments(helperargs); + + ExecuteJob *job = action.execute(); + + if (!job->exec()) { + KMessageBox::error(this, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); + } + else { + QDBusMessage msg = QDBusMessage::createSignal("/org/kde/kcmshell_clock", "org.kde.kcmshell_clock", "clockUpdated"); + QDBusConnection::sessionBus().send(msg); + } + + // NOTE: super nasty hack #1 + // Try to work around time mismatch between KSystemTimeZones' update of local + // timezone and reloading of data, so that the new timezone is taken into account. + // The Ultimate solution to this would be if KSTZ emitted a signal when a new + // local timezone was found. + QTimer::singleShot(5000, this, SLOT(load())); + + // setDisabled(false) happens in load(), since QTimer::singleShot is non-blocking +} + +void KclockModule::load() +{ + dtime->load(); + setDisabled(false); +} + +#include "main.moc" diff --git a/kcms/dateandtime/main.h b/kcms/dateandtime/main.h new file mode 100644 index 0000000000..c1e5234091 --- /dev/null +++ b/kcms/dateandtime/main.h @@ -0,0 +1,47 @@ +/* + * main.h + * + * Copyright (C) 1998 Luca Montecchiani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef main_included +#define main_included + +#include + +class Dtime; +class QTabWidget; +class KProcess; + + +class KclockModule : public KCModule +{ + Q_OBJECT + +public: + KclockModule(QWidget *parent, const QVariantList &); + + void save(); + void load(); + +private: + QTabWidget *tab; + Dtime *dtime; + KProcess *process; +}; + +#endif // main_included diff --git a/kcms/desktoppaths/CMakeLists.txt b/kcms/desktoppaths/CMakeLists.txt new file mode 100644 index 0000000000..4076f68959 --- /dev/null +++ b/kcms/desktoppaths/CMakeLists.txt @@ -0,0 +1,17 @@ +set(kcm_desktoppaths_PART_SRCS + globalpaths.cpp +) + +add_library(kcm_desktoppaths MODULE ${kcm_desktoppaths_PART_SRCS}) + +target_link_libraries(kcm_desktoppaths + KF5::KCMUtils + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::KDE4Support +) + +install(TARGETS kcm_desktoppaths DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES desktoppath.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + diff --git a/kcms/desktoppaths/Messages.sh b/kcms/desktoppaths/Messages.sh new file mode 100644 index 0000000000..fe3c1740c6 --- /dev/null +++ b/kcms/desktoppaths/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kcm_desktoppaths.pot diff --git a/kcms/desktoppaths/desktoppath.desktop b/kcms/desktoppaths/desktoppath.desktop new file mode 100644 index 0000000000..c3ef14c3fc --- /dev/null +++ b/kcms/desktoppaths/desktoppath.desktop @@ -0,0 +1,237 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/paths/index.html +Icon=system-file-manager +Exec=kcmshell5 desktoppath + +X-KDE-Library=kcm_desktoppaths +X-KDE-ParentApp=kcontrol +X-KDE-System-Settings-Parent-Category=account-details +X-KDE-Weight=60 + +Name=Paths +Name[af]=Soekgidse +Name[ar]=المسارات +Name[as]=পথ +Name[ast]=Caminos +Name[be]=Шляхі +Name[be@latin]=Ściežki +Name[bg]=Пътища +Name[bn]=পাথ (Paths) +Name[bn_IN]=পাথ +Name[br]=Hentoù +Name[bs]=Putanje +Name[ca]=Camins +Name[ca@valencia]=Camins +Name[cs]=Cesty +Name[csb]=Stegnë +Name[cy]=Llwybrau +Name[da]=Stier +Name[de]=Pfade +Name[el]=Κατάλογοι +Name[en_GB]=Paths +Name[eo]=Vojoj +Name[es]=Rutas +Name[et]=Otsinguteed +Name[eu]=Bide-izenak +Name[fa]=مسیرها +Name[fi]=Polut +Name[fr]=Emplacements +Name[fy]=Paden +Name[ga]=Conairí +Name[gl]=Rutas +Name[gu]=માર્ગો +Name[he]=נתיבים +Name[hi]=पथ +Name[hne]=पथ +Name[hr]=Putanje +Name[hsb]=Puće +Name[hu]=Elérési utak +Name[ia]=Percursos +Name[id]=Alamat +Name[is]=Slóðir +Name[it]=Percorsi +Name[ja]=パス +Name[ka]=ბილიკი +Name[kk]=Жолдар +Name[km]=ផ្លូវ +Name[kn]=ಪಥಗಳು (ಪಾತ್) +Name[ko]=경로 +Name[ku]=Rêç +Name[lt]=Keliai +Name[lv]=Ceļi +Name[mai]=पथ +Name[mk]=Патеки +Name[ml]=വഴികള്‍ +Name[mr]=मार्ग +Name[ms]=Laluan +Name[nb]=Stier +Name[nds]=Padden +Name[ne]=मार्ग +Name[nl]=Paden +Name[nn]=Stiar +Name[oc]=Camins +Name[or]=ପଥଗୁଡ଼ିକ +Name[pa]=ਮਾਰਗ +Name[pl]=Ścieżki +Name[pt]=Localizações +Name[pt_BR]=Caminhos +Name[ro]=Căi +Name[ru]=Пути +Name[se]=Bálgát +Name[si]=මාර්‍ග +Name[sk]=Cesty +Name[sl]=Poti +Name[sr]=Путање +Name[sr@ijekavian]=Путање +Name[sr@ijekavianlatin]=Putanje +Name[sr@latin]=Putanje +Name[sv]=Sökvägar +Name[ta]=பாதைகள் +Name[te]=దారులు +Name[tg]=Роҳчаҳо +Name[th]=พาธ +Name[tr]=Yollar +Name[ug]=يول +Name[uk]=Шляхи +Name[uz]=Yoʻllar +Name[uz@cyrillic]=Йўллар +Name[vi]=Đường dẫn +Name[wa]=Tchimins +Name[xh]=Umendo +Name[x-test]=xxPathsxx +Name[zh_CN]=路径 +Name[zh_TW]=路徑 + +Comment=Change the location important files are stored +Comment[af]=Verander die ligging belangrik lêers word gestoor +Comment[ar]=غيّر المكان المُستخدم لتخزين الملفات المهمة +Comment[as]=গুৰুত্বপূৰ্ণ নথিপত্ৰ সংৰক্ষণ কৰাৰ স্থান সলনি কৰক +Comment[ast]=Camudar l'allugamientu de ficheros importantes +Comment[be]=Змена тэчак для захавання важных файлаў +Comment[be@latin]=Źmiani miescy, dzie zachoŭvajucca važnyja fajły +Comment[bg]=Настройване местоположението на важните файлове +Comment[bn]=বিভিন্ন গুরুত্বপূর্ণ ফাইল কোন অবস্থানে থাকবে তা পরিবর্তন করুন +Comment[bn_IN]=গুরুত্বপূর্ণ ফাইল সংরক্ষণের স্থান পরিবর্তন করুন +Comment[bs]=Promijenite lokaciju gdje su smješteni važni datoteke +Comment[ca]=Canvia la localització d'on seran desats els fitxers importants +Comment[ca@valencia]=Canvia la localització d'on seran guardats els fitxers importants +Comment[cs]=Změna umístění důležitých souborů +Comment[csb]=Môl trzëmaniô wôżnëch lopków +Comment[cy]=Newid lle mae ffeiliau pwysig yn cael eu cadw +Comment[da]=Ændr stedet hvor vigtige filer gemmes +Comment[de]=Ordner für wichtige Dateien ändern +Comment[el]=Αλλάξτε την τοποθεσία στην οποία αποθηκεύονται σημαντικά αρχεία +Comment[en_GB]=Change the location important files are stored +Comment[eo]=Ŝanĝi la lokon, kie oni konservas gravajn dosierojn +Comment[es]=Cambiar la ubicación de archivos importantes +Comment[et]=Oluliste failide asukoha muutmine +Comment[eu]=Aldatu fitxategi garrantzitsuak gordetzen diren kokalekua +Comment[fa]=تغییر محل ذخیره پرونده‌های مهم +Comment[fi]=Tärkeiden tiedostojen tallennuspaikkojen asetukset +Comment[fr]=Changer l'emplacement où les fichiers importants sont stockés +Comment[fy]=Hjir kinne jo de lokaasje fan it bewarjen fan belangrike triemmen wizigje +Comment[ga]=Athraigh an suíomh ina stóráiltear comhaid thábhachtacha +Comment[gl]=Cambia o lugar onde se gardan os ficheiros importantes +Comment[gu]=મહત્વની ફાઇલો સંગ્રહ થાય તે સ્થાન બદલો +Comment[he]=שינוי המיקומים בהם מאוחסנים קבצים חשובים +Comment[hi]=महत्वपूर्ण फ़ाइलों के रखने का स्थान बदलें +Comment[hne]=जरूरी फाइल मन के रखे के जगह बदलव +Comment[hr]=Promijenite lokaciju za smještaj važnih datoteka +Comment[hsb]=Městna změnić, hdźež so wažne dataje namakaja +Comment[hu]=Néhány fontosabb elérési utat lehet itt beállítani +Comment[ia]=Modifica le location ubi files importante es immagazinate +Comment[id]=Ubah lokasi berkas penting disimpan +Comment[is]=Breyta staðsetningu mikilvægra skráa +Comment[it]=Cambia il posto dove vengono salvati i file importanti +Comment[ja]=重要なファイルなどの保存場所を変更 +Comment[ka]=მნიშვნელოვანი ფაილბის გეზის ცვლილება შენახულია +Comment[kk]=Маңызды файлдар орналасатын орындарын өзгерту +Comment[km]=ផ្លាស់ប្ដូរ​​ទីតាំង​ដែល​ត្រូវ​ទុក​ឯកសារ​សំខាន់ៗ +Comment[kn]=ಮಖ್ಯ ಕಡತಗಳನ್ನು ಸಂಗ್ರಹಿಸಲಾಗುವ ಸ್ಥಳವನ್ನು ಬದಲಾಯಿಸು +Comment[ko]=중요한 파일이 저장되는 곳 설정 +Comment[ku]=Ciyê ku pelên girîng lê tomar dibin biguherîne +Comment[lt]=Pakeiskite svarbių failų saugojimo vietą +Comment[lv]=Mainīt nozīmīgu failu atrašanās vietas +Comment[mai]=महत्वपूर्ण फाइलसभ केर रखबाक स्थान बदलू +Comment[mk]=Сменете ја локацијата каде што се чуваат важни датотеки +Comment[ml]=പ്രധാനപ്പെട്ട ഫയലുകള്‍ സൂക്ഷിച്ച സ്ഥാനം മാറ്റുക +Comment[mr]=महत्वपूर्ण फाईल संयोजनाचे स्थान बदला +Comment[ms]=Ubah lokasi fail penting disimpan +Comment[nb]=Endre plasseringa av viktige filer +Comment[nds]=Den Oort för wichtige Dateien ännern +Comment[ne]=महत्वपूर्ण फाइल भण्डारण गरिएको स्थान परिवर्तन गर्नुहोस् +Comment[nl]=Hier kunt u de locatie voor het opslaan van belangrijke bestanden wijzigen +Comment[nn]=Endra plasseringa av viktige filer +Comment[or]=ଦରକାରି ଫାଇଲଗୁଡ଼ିକୁ ସଂରକ୍ଷିତ କରାଯାଇଥିବା ସ୍ଥାନକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ +Comment[pa]=ਜ਼ਰੂਰੀ ਫਾਇਲਾਂ ਸੰਭਾਲਣ ਲਈ ਟਿਕਾਣਾ ਤਬਦੀਲ +Comment[pl]=Zmiana miejsca przechowywania ważnych plików +Comment[pt]=Alterar as localizações onde os ficheiros importantes estão guardados +Comment[pt_BR]=Altera o local de armazenamento dos arquivos importantes +Comment[ro]=Modifică locul de stocare a fișierelor importante +Comment[ru]=Пути к важным файлам +Comment[se]=Rievdat báikkiid gosa dehálaš fiillat bidjut +Comment[si]=වැදගත් ගොනු ගබඩා කර ඇති ස්ථානය වෙනස් කරන්න +Comment[sk]=Zmena umiestnenia dôležitých súborov +Comment[sl]=Spremenite mesta shranjevanja pomembnih datotek +Comment[sr]=Промените локацију где су смештени важни фајлови +Comment[sr@ijekavian]=Промијените локацију гдје су смјештени важни фајлови +Comment[sr@ijekavianlatin]=Promijenite lokaciju gdje su smješteni važni fajlovi +Comment[sr@latin]=Promenite lokaciju gde su smešteni važni fajlovi +Comment[sv]=Ändra plats för lagring av viktiga filer +Comment[ta]=முக்கிய கோப்புகள் சேகரிக்கப்பட்டுள்ள இடத்தை மாற்று +Comment[te]=ముఖ్యమైన దస్త్రములు నిల్వవుంచు స్థానమును మార్చుము +Comment[tg]=Ҷойгиршавии файлҳои лозимӣ +Comment[th]=เปลี่ยนตำแหน่งที่เก็บแฟ้มที่สำคัญต่าง ๆ +Comment[tr]=Önemli dosyaların kaydedildiği yerleri düzenle +Comment[ug]=مۇھىم ھۆججەت ساقلايدىغان ئورۇننى ئۆزگەرت +Comment[uk]=Зміна адреси зберігання важливих файлів +Comment[uz]=Muhim fayllarning joylashgan joyini oʻzgartirish +Comment[uz@cyrillic]=Муҳим файлларнинг жойлашган жойини ўзгартириш +Comment[vi]=Thay đổi đường dẫn tới các tập tin quan trọng +Comment[wa]=Candjî l' eplaeçmint ki les fitchîs impôrtants sont wårdés +Comment[xh]=Tshintsha indawo apho zibekwa kona iifayile ezibalulekileyo +Comment[x-test]=xxChange the location important files are storedxx +Comment[zh_CN]=更改重要文件存储的位置 +Comment[zh_TW]=改變儲存重要檔案的位置 +X-KDE-Keywords=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures +X-KDE-Keywords[bs]=konqueror, delfin, datoteke, menadžer datoteka, put, putevi, desktop, upute, autostart, preuuzimanja, muzika, dokumenti, filmovi, slike +X-KDE-Keywords[ca]=konqueror,dolphin,fitxers,gestor de fitxers,camí,camins,escriptori,directoris,inici automàtic,baixades,música,documents,pel·lícules,imatges +X-KDE-Keywords[ca@valencia]=konqueror,dolphin,fitxers,gestor de fitxers,camí,camins,escriptori,directoris,inici automàtic,baixades,música,documents,pel·lícules,imatges +X-KDE-Keywords[da]=konqueror,dolphin,filhåndtering,stifinder,stier,skrivebord,mapper,autostart,downoad,hentninger,musik,dokumenter,film,billeder,filer,sti +X-KDE-Keywords[de]=Konqueror,Dolphin,Dateien,Dateimanager,Dateiverwaltung,Pfade,Arbeitsfläche,Verzeichnisse,Ordner,Autostart,Download,Herunterladen,Musik,Dokumente,Filme,Bilder +X-KDE-Keywords[el]=konqueror,dolphin,αρχεία,διαχειριστής αρχείων,διαδρομή, διαδρομές,επιφάνεια εργασίας,κατάλογοι,αυτόματη εκκίνηση,λήψεις,μουσική,έγγραφα,κινηματογραφία,εικόνες +X-KDE-Keywords[es]=konqueror,dolphin,archivos,gestor de archivos,ruta,rutas,escritorio,directorios,autoinicio,descargas,música,documentos,películas,imágenes +X-KDE-Keywords[et]=konqueror,dolphin,failid,failihaldur,asukohad,töölaud,kataloogid,autostart,allalaadimised,muusika,dokumendid,filmid,pildid +X-KDE-Keywords[eu]=konqueror,dolphin,fitxategi,fitxategi-kudeatzaile,bide-izen, bide-izenak,mahaigain,direktorio,hasi automatikoki,deskargak,musika,dokumentuak,filmak,irudiak +X-KDE-Keywords[fi]=konqueror,dolphin,tiedostot,tiedostonhallinta,polku,polut,työpöytä,hakemistot,kansiot,automaattikäynnistys,lataukset,musiikki,asiakirjat,elokuvat,kuvat +X-KDE-Keywords[fr]=konqueror, dolphin, fichiers, gestionnaire de fichiers, emplacement, emplacements, bureau, dossiers, démarrage automatique, téléchargements, musique, documents, films, images +X-KDE-Keywords[ga]=konqueror,dolphin,comhaid,bainisteoir comhad,conairí,cosáin,deasc,comhadlanna,tosú uathoibríoch,íosluchtuithe,ceol,cáipéisí,scannáin,pictiúir,grianghraif +X-KDE-Keywords[gl]=konqueror, dolphin, ficheiros, xestor de ficheiros, ruta, rutas, escritorio, cartafoles, inicio automático, descargas, música, documentos, películas, imaxes +X-KDE-Keywords[hu]=konqueror,dolphin,fájlok,fájlkezelő,elérési út, elérési utak,asztal,mappák, automatikus indítás,letöltések,zene,dokumentumok,filmek,képek +X-KDE-Keywords[ia]=konqueror,dolphin,files,gerente de file,percurso,percursos,scriptorio,directorios,autoinitio,discargamentos, musica,documentos,pelliculas,photos +X-KDE-Keywords[it]=konqueror,dolphin,file,gestore dei file,percorso,percorsi,desktop,cartelle,avvio automatico,scaricamenti,musica,documenti,film,immagini +X-KDE-Keywords[kk]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures +X-KDE-Keywords[km]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures +X-KDE-Keywords[ko]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,파일,파일 관리자,탐색기,디렉터리,다운로드,음악,문서,비디오,그림 +X-KDE-Keywords[mr]=कॉन्करर , डॉल्फीन, फाईल्स, फाईल मेनेजर, पाथस , डेस्कटॉप , डीरेक्टरिज , ऑटोस्टार्ट, डाउनलोडस,म्युझिक, डॉक्युमेंट, मूव्हीज, पिक्चर्स +X-KDE-Keywords[nb]=konqueror,dolphin,filer,filbehandler,sti,stier,skrivebord,mapper,autostart,nedlastinger,musikk,dokumenter,filmer,bilder +X-KDE-Keywords[nds]=Konqueror,Dolphin,Dateien,Dateipleger,Padden,Schriefdisch,Ornern,Vertekens,Autostart,Daalladen,Musik,Dokmenten,Filmen,Biller +X-KDE-Keywords[nl]=konqueror,dolphin,bestanden,bestandsbeheerder,pad,paden,bureaublad,mappen,autostart,downloads,muziek,documenten,films,afbeeldingen +X-KDE-Keywords[pl]=konqueror,dolphin,pliki,menadżer plików,ścieżka,ścieżki,pulpit,katalogi,auto-uruchamianie, pobierania,muzyka,dokumenty,filmy,zdjęcia,autostart +X-KDE-Keywords[pt]=konqueror,gestor de ficheiros,localizações,ecrã,pastas,arranque,transferências,música,documentos,filmes,imagens +X-KDE-Keywords[pt_BR]=konqueror,dolphin,arquivos,gerenciador de arquivos,localização,localizações,caminho,caminhos,área de trabalho,pastas,inicialização,downloads,música,documentos,filmes,imagens +X-KDE-Keywords[ru]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,файлы,диспетчер файлов,путь,местоположение,рабочий стол,директории,каталоги,папки,автозапуск,загрузки,музыка,документы,фильмы,видео,картинки +X-KDE-Keywords[sk]=konqueror,dolphin,súbory,správca súborov,cesta,cesty,plocha,adresáre,automatické spustenie,sťahovania,hudba,dokumenty,filmy,obrázky +X-KDE-Keywords[sl]=konqueror,dolphin,datoteke,upravljalnik datotek,datotečni upravljalnik,poti,namizje,mape,samodejni zagon,prejeto,prejemi,glasba,dokumenti,filmi,slike +X-KDE-Keywords[sr]=konqueror,dolphin,files,filemanager,path,paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,К‑освајач,Делфин,фајлови,менаџер фајлова,путање,путања,површ,фасцикле,самопокретање,преузимање,музика,документи,филмови,слике +X-KDE-Keywords[sr@ijekavian]=konqueror,dolphin,files,filemanager,path,paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,К‑освајач,Делфин,фајлови,менаџер фајлова,путање,путања,површ,фасцикле,самопокретање,преузимање,музика,документи,филмови,слике +X-KDE-Keywords[sr@ijekavianlatin]=konqueror,dolphin,files,filemanager,path,paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,K‑osvajač,Dolphin,fajlovi,menadžer fajlova,putanje,putanja,površ,fascikle,samopokretanje,preuzimanje,muzika,dokumenti,filmovi,slike +X-KDE-Keywords[sr@latin]=konqueror,dolphin,files,filemanager,path,paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,K‑osvajač,Dolphin,fajlovi,menadžer fajlova,putanje,putanja,površ,fascikle,samopokretanje,preuzimanje,muzika,dokumenti,filmovi,slike +X-KDE-Keywords[sv]=konqueror,dolphin,filer,filhanterare,sökväg,sökvägar,skrivbord,kataloger,automatisk start,nerladdningar,musik,dokument,filmer,bilder +X-KDE-Keywords[tr]=konqueror,dolphin,dosyalar,dosya yöneticisi,yol, yollar,masaüstü,dizinler,otomatik başlat,indirmeler,müzik,belgeler,filmler,resimler +X-KDE-Keywords[uk]=konqueror,dolphin,files,filemanager,paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,файл,дельфін,керування,менеджер,шляхи,стільниця,каталог,каталоги,автоматично,автоматичний,запуск,звантаження,завантаження,музика,документи,фільми,відео,зображення,знімки,фотографії +X-KDE-Keywords[x-test]=xxkonqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,picturesxx +X-KDE-Keywords[zh_CN]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures,文件,文件管理器,路径,桌面,目录,自动启动,下载,音乐,文档,电影,图片 +X-KDE-Keywords[zh_TW]=konqueror,dolphin,files,filemanager,path, paths,desktop,directories,autostart,downloads,music,documents,movies,pictures diff --git a/kcms/desktoppaths/globalpaths.cpp b/kcms/desktoppaths/globalpaths.cpp new file mode 100644 index 0000000000..5cfd3594e5 --- /dev/null +++ b/kcms/desktoppaths/globalpaths.cpp @@ -0,0 +1,455 @@ +/** + * Copyright (c) Martin R. Jones 1996 + * Copyright (c) Bernd Wuebben 1998 + * Copyright (c) Christian Tibirna 1998 + * Copyright 1998-2007 David Faure + * Copyright (c) 2010 Matthias Fuchs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// +// "Desktop Options" Tab for KDesktop configuration +// +// (c) Martin R. Jones 1996 +// (c) Bernd Wuebben 1998 +// +// Layouts +// (c) Christian Tibirna 1998 +// Port to KControl, split from Misc Tab, Port to KControl2 +// (c) David Faure 1998 +// Desktop menus, paths +// (c) David Faure 2000 + + +// Own +#include "globalpaths.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KcmDesktopPathsFactory, registerPlugin();) + +//----------------------------------------------------------------------------- + +static QUrl desktopLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); +} + +static QUrl autostartLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/autostart")); +} + +static QUrl documentsLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); +} + +static QUrl downloadLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); +} + +static QUrl moviesLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)); +} + +static QUrl picturesLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); +} + +static QUrl musicLocation() +{ + return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)); +} + +DesktopPathConfig::DesktopPathConfig(QWidget *parent, const QVariantList &) + : KCModule( parent ) +{ + QFormLayout *lay = new QFormLayout(this); + lay->setVerticalSpacing(0); + lay->setMargin(0); + + setQuickHelp( i18n("

    Paths

    \n" + "This module allows you to choose where in the filesystem the " + "files on your desktop should be stored.\n" + "Use the \"Whats This?\" (Shift+F1) to get help on specific options.")); + + urDesktop = addRow(lay, i18n("Desktop path:"), + i18n("This folder contains all the files" + " which you see on your desktop. You can change the location of this" + " folder if you want to, and the contents will move automatically" + " to the new location as well.")); + + urAutostart = addRow(lay, i18n("Autostart path:"), + i18n("This folder contains applications or" + " links to applications (shortcuts) that you want to have started" + " automatically whenever KDE starts. You can change the location of this" + " folder if you want to, and the contents will move automatically" + " to the new location as well.")); + + urDocument = addRow(lay, i18n("Documents path:"), + i18n("This folder will be used by default to " + "load or save documents from or to.")); + + urDownload = addRow(lay, i18n("Downloads path:"), + i18n("This folder will be used by default to " + "save your downloaded items.")); + + urMovie = addRow(lay, i18n("Movies path:"), + i18n("This folder will be used by default to " + "load or save movies from or to.")); + + urPicture = addRow(lay, i18n("Pictures path:"), + i18n("This folder will be used by default to " + "load or save pictures from or to.")); + + urMusic = addRow(lay, i18n("Music path:"), + i18n("This folder will be used by default to " + "load or save music from or to.")); +} + +KUrlRequester* DesktopPathConfig::addRow(QFormLayout *lay, const QString& label, const QString& whatsThis) +{ + KUrlRequester* ur = new KUrlRequester(this); + ur->setMode(KFile::Directory | KFile::LocalOnly); + lay->addRow(label, ur); + connect(ur, SIGNAL(textChanged(QString)), this, SLOT(changed())); + lay->labelForField(ur)->setWhatsThis(whatsThis); + ur->setWhatsThis(whatsThis); + return ur; +} + +void DesktopPathConfig::load() +{ + // Desktop Paths + urDesktop->setUrl(desktopLocation()); + urAutostart->setUrl(autostartLocation()); + urDocument->setUrl(documentsLocation()); + urDownload->setUrl(downloadLocation()); + urMovie->setUrl(moviesLocation()); + urPicture->setUrl(picturesLocation()); + urMusic->setUrl(musicLocation()); + emit changed(false); +} + +void DesktopPathConfig::defaults() +{ + // Desktop Paths - keep defaults in sync with kglobalsettings.cpp + urDesktop->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Desktop"))); + urAutostart->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/.config/autostart"))); + urDocument->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Documents"))); + urDownload->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Downloads"))); + urMovie->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Movies"))); + urPicture->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Pictures"))); + urMusic->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Music"))); +} + +// the following method is copied from kdelibs/kdecore/config/kconfiggroup.cpp +static bool cleanHomeDirPath( QString &path, const QString &homeDir ) +{ +#ifdef Q_WS_WIN //safer + if (!QDir::convertSeparators(path).startsWith(QDir::convertSeparators(homeDir))) + return false; +#else + if (!path.startsWith(homeDir)) + return false; +#endif + + int len = homeDir.length(); + // replace by "$HOME" if possible + if (len && (path.length() == len || path[len] == '/')) { + path.replace(0, len, QString::fromLatin1("$HOME")); + return true; + } else + return false; +} + +// TODO this functionality is duplicated in libkonq - keep it only there and export + +static QString translatePath( QString path ) // krazy:exclude=passbyvalue +{ + // keep only one single '/' at the beginning - needed for cleanHomeDirPath() + while (path[0] == '/' && path[1] == '/') + path.remove(0,1); + + // we probably should escape any $ ` and \ characters that may occur in the path, but the Qt code that reads back + // the file doesn't unescape them so not much point in doing so + + // All of the 3 following functions to return the user's home directory + // can return different paths. We have to test all them. + const QString homeDir0 = QFile::decodeName(qgetenv("HOME")); + const QString homeDir1 = QDir::homePath(); + const QString homeDir2 = QDir(homeDir1).canonicalPath(); + if (cleanHomeDirPath(path, homeDir0) || + cleanHomeDirPath(path, homeDir1) || + cleanHomeDirPath(path, homeDir2) ) { + // kDebug() << "Path was replaced\n"; + } + + return path; +} + +void DesktopPathConfig::save() +{ + KSharedConfig::Ptr config = KSharedConfig::openConfig(); + KConfigGroup configGroup( config, "Paths" ); + + bool pathChanged = false; + bool autostartMoved = false; + + QUrl desktopURL(desktopLocation()); + + QUrl autostartURL(autostartLocation()); + QUrl newAutostartURL = urAutostart->url(); + + if ( !urDesktop->url().matches( desktopURL, QUrl::StripTrailingSlash ) ) + { + // Test which other paths were inside this one (as it is by default) + // and for each, test where it should go. + // * Inside destination -> let them be moved with the desktop (but adjust name if necessary) + // * Not inside destination -> move first + // !!! + kDebug() << "desktopURL=" << desktopURL; + QString urlDesktop = urDesktop->url().toLocalFile(); + if ( !urlDesktop.endsWith('/')) + urlDesktop+='/'; + + if ( desktopURL.isParentOf( autostartURL ) ) + { + kDebug() << "Autostart is on the desktop"; + + // Either the Autostart field wasn't changed (-> need to update it) + if ( newAutostartURL.matches(autostartURL, QUrl::StripTrailingSlash) ) + { + // Hack. It could be in a subdir inside desktop. Hmmm... Argl. + urAutostart->setUrl(QUrl::fromLocalFile(urlDesktop + QStringLiteral("Autostart/"))); + kDebug() << "Autostart is moved with the desktop"; + autostartMoved = true; + } + // or it has been changed (->need to move it from here) + else + { + QUrl futureAutostartURL = QUrl::fromLocalFile(urlDesktop + QStringLiteral("Autostart/")); + if ( newAutostartURL.matches( futureAutostartURL, QUrl::StripTrailingSlash ) ) + autostartMoved = true; + else + autostartMoved = moveDir( autostartLocation(), urAutostart->url(), i18n("Autostart") ); + } + } + + if ( moveDir( desktopLocation(), QUrl::fromLocalFile( urlDesktop ), i18n("Desktop") ) ) + { + //save in XDG path + const QString userDirsFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/user-dirs.dirs")); + KConfig xdgUserConf( userDirsFile, KConfig::SimpleConfig ); + KConfigGroup g( &xdgUserConf, "" ); + g.writeEntry( "XDG_DESKTOP_DIR", QString("\"" + translatePath( urlDesktop ) + "\"") ); + pathChanged = true; + } + } + + if ( !newAutostartURL.matches( autostartURL, QUrl::StripTrailingSlash ) ) + { + if (!autostartMoved) + autostartMoved = moveDir( autostartLocation(), urAutostart->url(), i18n("Autostart") ); + if (autostartMoved) + { + configGroup.writePathEntry( "Autostart", urAutostart->url().toLocalFile(), KConfigBase::Normal | KConfigBase::Global ); + pathChanged = true; + } + } + + config->sync(); + + if (xdgSavePath(urDocument, documentsLocation(), "XDG_DOCUMENTS_DIR", i18n("Documents"))) + pathChanged = true; + + if (xdgSavePath(urDownload, downloadLocation(), "XDG_DOWNLOAD_DIR", i18n("Downloads"))) + pathChanged = true; + + if (xdgSavePath(urMovie, moviesLocation(), "XDG_VIDEOS_DIR", i18n("Movies"))) + pathChanged = true; + + if (xdgSavePath(urPicture, picturesLocation(), "XDG_PICTURES_DIR", i18n("Pictures"))) + pathChanged = true; + + if (xdgSavePath(urMusic, musicLocation(), "XDG_MUSIC_DIR", i18n("Music"))) + pathChanged = true; + + if (pathChanged) { + kDebug() << "sending message SettingsChanged"; + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_PATHS); + } +} + +bool DesktopPathConfig::xdgSavePath(KUrlRequester* ur, const QUrl& currentUrl, const char* xdgKey, const QString& type) +{ + QUrl newUrl = ur->url(); + //url might be empty, use QDir::homePath (the default for xdg) then + if (!newUrl.isValid()) { + newUrl = QUrl(QUrl::fromLocalFile(QDir::homePath())); + } + if (!newUrl.matches(currentUrl, QUrl::StripTrailingSlash)) { + const QString path = newUrl.toLocalFile(); + if (!QDir(path).exists()) { + // Check permissions + if (QDir().mkpath(path)) { + QDir().rmdir(path); // rmdir again, so that we get a fast rename + } else { + KMessageBox::sorry(this, KIO::buildErrorString(KIO::ERR_COULD_NOT_MKDIR, path)); + ur->setUrl(currentUrl); // revert + return false; + } + } + if (moveDir(currentUrl, newUrl, type)) { + //save in XDG user-dirs.dirs config file, this is where KGlobalSettings/QDesktopServices reads from. + const QString userDirsFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/user-dirs.dirs")); + KConfig xdgUserConf(userDirsFile, KConfig::SimpleConfig); + KConfigGroup g(&xdgUserConf, ""); + g.writeEntry(xdgKey, QString("\"" + translatePath(path) + "\"")); + return true; + } + } + return false; +} + +bool DesktopPathConfig::moveDir( const QUrl & src, const QUrl & dest, const QString & type ) +{ + if (!src.isLocalFile() || !dest.isLocalFile()) + return true; + if (!QFile::exists(src.toLocalFile())) + return true; + // Do not move $HOME! #193057 + const QString translatedPath = translatePath(src.toLocalFile()); + if (translatedPath == "$HOME" || translatedPath == "$HOME/") { + return true; + } + + m_ok = true; + + QString question; + KGuiItem yesItem; + KGuiItem noItem; + if (QFile::exists(dest.toLocalFile())) { + // TODO: check if the src dir is empty? Nothing to move, then... + question = i18n("The path for '%1' has been changed.\nDo you want the files to be moved from '%2' to '%3'?", + type, src.toLocalFile(), + dest.toLocalFile()); + yesItem = KGuiItem(i18nc("Move files from old to new place", "Move")); + noItem = KGuiItem(i18nc("Use the new directory but do not move files", "Do not Move")); + } else { + question = i18n("The path for '%1' has been changed.\nDo you want to move the directory '%2' to '%3'?", + type, src.toLocalFile(), + dest.toLocalFile()); + yesItem = KGuiItem(i18nc("Move the directory", "Move")); + noItem = KGuiItem(i18nc("Use the new directory but do not move anything", "Do not Move")); + } + + // Ask for confirmation before moving the files + if (KMessageBox::questionYesNo(this, question, i18n("Confirmation Required"), + yesItem, noItem) + == KMessageBox::Yes ) + { + if (QFile::exists(dest.toLocalFile())) { + // Destination already exists -- should always be the case, for most types, + // but maybe not for the complex autostart case (to be checked...) + m_copyToDest = dest; + m_copyFromSrc = src; + KIO::ListJob* job = KIO::listDir( src ); + job->setAutoDelete(false); // see below + KJobWidgets::setWindow(job, this); + job->ui()->setAutoErrorHandlingEnabled(true); + connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), + this, SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList))); + // slotEntries will move every file/subdir individually into the dest + job->exec(); + if (m_ok) { + QDir().rmdir(src.toLocalFile()); // hopefully it's empty by now + } + delete job; + } + else + { + kDebug() << "Direct move from" << src << "to" << dest; + KIO::Job * job = KIO::move( src, dest ); + KJobWidgets::setWindow(job, this); + connect(job, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*))); + job->exec(); + } + } + kDebug() << "DesktopPathConfig::slotResult returning " << m_ok; + return m_ok; +} + +void DesktopPathConfig::slotEntries(KIO::Job*, const KIO::UDSEntryList& list) +{ + QListIterator it(list); + while (it.hasNext()) { + KFileItem file(it.next(), m_copyFromSrc, true, true); + kDebug() << file.url(); + if (file.url() == m_copyFromSrc || file.url().fileName() == "..") { + continue; + } + + KIO::Job * moveJob = KIO::move(file.url(), m_copyToDest); + KJobWidgets::setWindow(moveJob, this); + connect(moveJob, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*))); + moveJob->exec(); // sub-event loop here. : the main job is not autodeleted because it would be deleted here + } +} + +void DesktopPathConfig::slotResult( KJob * job ) +{ + if (job->error()) { + if ( job->error() != KIO::ERR_DOES_NOT_EXIST ) + m_ok = false; + + // If the source doesn't exist, no wonder we couldn't move the dir. + // In that case, trust the user and set the new setting in any case. + + static_cast(job)->ui()->showErrorMessage(); + } +} + +#include "globalpaths.moc" diff --git a/kcms/desktoppaths/globalpaths.h b/kcms/desktoppaths/globalpaths.h new file mode 100644 index 0000000000..ef3234afb1 --- /dev/null +++ b/kcms/desktoppaths/globalpaths.h @@ -0,0 +1,84 @@ +/** + * Copyright (c) Martin R. Jones 1996 + * Copyright 1998-2007 David Faure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// Summarized history: +// +// "Desktop Icons Options" Tab for KDesktop configuration +// Martin Jones +// +// Port to KControl, split from "Misc" Tab, Port to KControl2 +// (c) David Faure 1998 +// Desktop menus, paths +// (c) David Faure 2000 + +#ifndef __GLOBALPATHS_H +#define __GLOBALPATHS_H + +#include +#include +#include +#include + +class QFormLayout; +class KJob; +class KUrlRequester; +class QStringList; + +namespace KIO { class Job; } + +//----------------------------------------------------------------------------- +// The "Path" Tab contains : +// The paths for Desktop, Autostart and Documents + +class DesktopPathConfig : public KCModule +{ + Q_OBJECT +public: + DesktopPathConfig( QWidget *parent, const QVariantList &args ); + virtual void load(); + virtual void save(); + virtual void defaults(); + +private Q_SLOTS: + void slotEntries( KIO::Job * job, const KIO::UDSEntryList& list); + +private: + KUrlRequester* addRow(QFormLayout *lay, const QString& label, const QString& whatsThis); + bool xdgSavePath(KUrlRequester* ur, const QUrl& currentUrl, const char* xdgKey, const QString& type); + + // Desktop Paths + KUrlRequester *urDesktop; + KUrlRequester *urAutostart; + KUrlRequester *urDocument; + KUrlRequester *urDownload; + KUrlRequester *urMovie; + KUrlRequester *urPicture; + KUrlRequester *urMusic; + + bool moveDir( const QUrl & src, const QUrl & dest, const QString & type ); + bool m_ok; + QUrl m_copyToDest; // used when the destination directory already exists + QUrl m_copyFromSrc; + +private Q_SLOTS: + void slotResult( KJob * job ); +}; + +#endif + diff --git a/kcms/desktoptheme/CMakeLists.txt b/kcms/desktoptheme/CMakeLists.txt new file mode 100644 index 0000000000..b5d2bc57d3 --- /dev/null +++ b/kcms/desktoptheme/CMakeLists.txt @@ -0,0 +1,27 @@ +# Project Needs a name ofcourse +project(desktoptheme) + +set(kcmdesktoptheme_SRCS kcmdesktoptheme.cpp desktopthemedetails.cpp thememodel.cpp) +qt5_wrap_ui(kcmdesktoptheme_SRCS DesktopTheme.ui DesktopThemeDetails.ui) + +add_library(kcm_desktoptheme MODULE ${kcmdesktoptheme_SRCS}) +target_link_libraries(kcm_desktoptheme ${X11_LIBRARIES} + KF5::Archive + KF5::KCMUtils + KF5::I18n + KF5::Parts + KF5::Plasma + KF5::KIOCore + KF5::KIOWidgets + KF5::NewStuff + KF5::KDE4Support +) + +kservice_desktop_to_json(kcm_desktoptheme desktoptheme.desktop) + +install(TARGETS kcm_desktoptheme DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES plasma-themes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) + +########### install files ############### +install( FILES desktoptheme.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + diff --git a/kcms/desktoptheme/DesktopTheme.ui b/kcms/desktoptheme/DesktopTheme.ui new file mode 100644 index 0000000000..ccc040c212 --- /dev/null +++ b/kcms/desktoptheme/DesktopTheme.ui @@ -0,0 +1,104 @@ + + + DesktopTheme + + + + 0 + 0 + 669 + 460 + + + + + + + 0 + + + + Theme + + + + + + Insta&ll from File + + + + + + + + + + + + + true + + + Get New Themes... + + + + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 462 + 20 + + + + + + + + + Details + + + + + + + + + + + + + + KPushButton + QPushButton +
    kpushbutton.h
    +
    + + DesktopThemeDetails + QWidget +
    desktopthemedetails.h
    + 1 +
    +
    + + +
    diff --git a/kcms/desktoptheme/DesktopThemeDetails.ui b/kcms/desktoptheme/DesktopThemeDetails.ui new file mode 100644 index 0000000000..9df3536eab --- /dev/null +++ b/kcms/desktoptheme/DesktopThemeDetails.ui @@ -0,0 +1,395 @@ + + + DesktopThemeItems + + + + 0 + 0 + 661 + 487 + + + + + 0 + 0 + + + + + 200 + 700 + + + + Desktop Theme Details + + + + + + + + + + 0 + 0 + + + + Choose an item and customize it by assigning it a theme + + + You can set any theme to any item. The available themes for the item are in the dropdown box on the right. + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + false + + + false + + + + + + + + 0 + 0 + + + + + + + Enable more options + + + This will expand the dialog and give you options to export your custom theme as well as to remove a theme. + + + More + + + + + + + + + true + + + Remove the selected theme + + + Clicking this button will remove the selected theme. You will be asked for confirmation. + + + Remove Theme + + + + + + + true + + + Save your theme in a zip archive + + + You can save your theme in a zip archive in order to share it with the community. + + + Export Theme to File... + + + + + + + + + QFrame::HLine + + + QFrame::Sunken + + + Qt::Horizontal + + + + + + + + + New theme name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_newThemeName + + + + + + + Custom theme name + + + Give a name to your custom theme. + + + + + + + Author: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_newThemeAuthor + + + + + + + Custom theme author + + + Enter the author name of your custom theme. + + + + + + + Version: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_newThemeVersion + + + + + + + Custom theme version number + + + Enter the version number of your custom theme. + + + + + + + + + + + Description: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_newThemeDescription + + + + + + + + 0 + 0 + + + + Custom theme description + + + Enter a description which will explain your custom theme. + + + + + + + + + + + + Qt::Vertical + + + + + + + Select theme from above to customize + + + + + + + + 0 + 0 + + + + + + + + + Theme Author + + + Qt::AlignLeading + + + + + + + + 75 + true + + + + Theme Name + + + Qt::AlignCenter + + + + + + + Theme Version + + + Qt::AlignRight + + + + + + + + + The theme description goes here... + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 130 + + + + + + + + + 187 + 187 + 187 + + + + + + + + + 187 + 187 + 187 + + + + + + + + + 255 + 255 + 255 + + + + + + + + Available themes + + + Choose a start theme in this list then customize some items below. All less important items not listed below will use this start theme. + + + Qt::ScrollBarAlwaysOff + + + false + + + QListView::IconMode + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    +
    + + +
    diff --git a/kcms/desktoptheme/Messages.sh b/kcms/desktoptheme/Messages.sh new file mode 100755 index 0000000000..14b32ad903 --- /dev/null +++ b/kcms/desktoptheme/Messages.sh @@ -0,0 +1,4 @@ +#! /bin/sh +$EXTRACTRC *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcm_desktopthemedetails.pot +rm -f rc.cpp diff --git a/kcms/desktoptheme/desktoptheme.desktop b/kcms/desktoptheme/desktoptheme.desktop new file mode 100644 index 0000000000..602f04cfd5 --- /dev/null +++ b/kcms/desktoptheme/desktoptheme.desktop @@ -0,0 +1,189 @@ +[Desktop Entry] +Exec=kcmshell5 desktoptheme +Icon=preferences-desktop +Type=Service + +X-KDE-ServiceTypes=KCModule +X-KDE-Library=kcm_desktoptheme +X-KDE-ParentApp=kcontrol +X-KDE-System-Settings-Parent-Category=desktop-appearance +X-DocPath=kcontrol/desktopthemedetails/index.html + +Name=Desktop Theme +Name[ar]=سمة سطح المكتب +Name[ast]=Tema d'escritoriu +Name[bg]=Тема за работния плот +Name[bn]=ডেস্কটপ থীম +Name[bs]=Tema površi +Name[ca]=Tema d'escriptori +Name[ca@valencia]=Tema d'escriptori +Name[cs]=Motiv plochy +Name[da]=Skrivebordstema +Name[de]=Arbeitsflächen-Design +Name[el]=Θέμα επιφάνειας εργασίας +Name[en_GB]=Desktop Theme +Name[es]=Tema de escritorio +Name[et]=Töölauateema +Name[eu]=Mahaigaineko gaia +Name[fi]=Työpöytäteema +Name[fr]=Thème de bureau +Name[ga]=Téama Deisce +Name[gl]=Tema do escritorio +Name[gu]=ડેસ્કટોપ થીમ +Name[he]=ערכת־הנושא לשולחן־העבודה +Name[hi]=डेस्कटॉप प्रसंग +Name[hr]=Tema radne površine +Name[hu]=Asztali téma +Name[ia]=Thema de scriptorio +Name[id]=Tema Desktop +Name[is]=Skjáborðsþema +Name[it]=Tema del desktop +Name[ja]=デスクトップテーマ +Name[kk]=Үстел нақышы +Name[km]=ស្បែក​ផ្ទៃតុ +Name[kn]=ಗಣಕತೆರೆ ಪರಿಸರವಿನ್ಯಾಸ +Name[ko]=데스크톱 테마 +Name[lt]=Darbastalio tema +Name[lv]=Darbvirsmas tēma +Name[mr]=डेस्कटॉप शैली +Name[nb]=Skrivebordstema +Name[nds]=Schriefdischmuster +Name[nl]=Bureaubladthema +Name[nn]=Skrivebordstema +Name[pa]=ਡੈਸਕਟਾਪ ਥੀਮ +Name[pl]=Wystrój pulpitu +Name[pt]=Tema do Ambiente de Trabalho +Name[pt_BR]=Tema da área de trabalho +Name[ro]=Tematica de birou +Name[ru]=Тема рабочего стола +Name[si]=වැඩතල තේමාව +Name[sk]=Téma plochy +Name[sl]=Namizna tema +Name[sr]=Тема површи +Name[sr@ijekavian]=Тема површи +Name[sr@ijekavianlatin]=Tema površi +Name[sr@latin]=Tema površi +Name[sv]=Skrivbordstema +Name[tg]=Мавзӯъи мизи корӣ +Name[th]=ชุดตกแต่งพื้นที่ทำงาน +Name[tr]=Masaüstü Teması +Name[ug]=ئۈستەلئۈستى ئۆرنىكى +Name[uk]=Тема стільниці +Name[vi]=Sắc thái màn hình +Name[wa]=Tinme do scribanne +Name[x-test]=xxDesktop Themexx +Name[zh_CN]=桌面主题 +Name[zh_TW]=桌面主題 +Comment=Customize the desktop theme +Comment[ar]=خصص عناصر السمة سطح المكتب بشكل منفرد +Comment[ast]=Personalizar el tema d'escritoriu +Comment[bg]=Настройки на теми за работния плот +Comment[bn]=ডেস্কটপ থীম পছন্দমত বদলান +Comment[bs]=Prilagodite temu površi +Comment[ca]=Personalitza el tema d'escriptori +Comment[ca@valencia]=Personalitza el tema d'escriptori +Comment[cs]=Upravit vzhled plochy +Comment[da]=Tilpas skrivebordstemaet +Comment[de]=Arbeitsflächen-Design anpassen +Comment[el]=Προσαρμογή θέματος της επιφάνειας εργασίας +Comment[en_GB]=Customise the desktop theme +Comment[es]=Personalizar el tema de escritorio +Comment[et]=Töölauateema kohandamine +Comment[eu]=Pertsonalizatu mahaigaineko gaia +Comment[fi]=Työpöytäteeman asetukset +Comment[fr]=Personnaliser le thème de bureau +Comment[ga]=Saincheap an téama deisce +Comment[gl]=Personaliza o tema do escritorio +Comment[he]=התאמה אישית של ערכת־הנושא לשולחן־העבודה +Comment[hi]=डेस्कटॉप प्रसंग मनपसंद बनाएँ +Comment[hr]=Prilagodba teme radne površine +Comment[hu]=Az asztali téma testreszabása +Comment[ia]=Personalisar le thema de scriptorio +Comment[id]=Sesuaikan tema desktop +Comment[is]=Sérsníða skjáborðsþema +Comment[it]=Personalizza il tema del desktop +Comment[ja]=デスクトップテーマのカスタマイズ +Comment[kk]=Үстел нақышын өзінше баптап алу +Comment[km]=ប្ដូរ​ស្បែក​ផ្ទៃតុ​តាមបំណង +Comment[kn]=ಗಣಕತೆರೆ ಪರಿಸರವಿನ್ಯಾಸವನ್ನು ಇಚ್ಛೆಗೆ ತಕ್ಕಂತೆ ಹೊಂದಿಸಿ +Comment[ko]=테마 사용자 정의 +Comment[lt]=Derinti darbastalio temą +Comment[lv]=Pielāgot darbivrsmas tēmu +Comment[mr]=डेस्कटॉप शैलीत ऐच्छिक बदल करा +Comment[nb]=Still inn temaet for skrivebordet +Comment[nds]=Dat Schriefdischmuster topassen +Comment[nl]=Pas het bureaubladthema aan +Comment[nn]=Tilpass skrivebordstemaet +Comment[pa]=ਡੈਸਕਟਾਪ ਥੀਮ ਕਸਟਮਾਈਜ਼ ਕਰੋ +Comment[pl]=Dostosowywanie wystroju pulpitu +Comment[pt]=Personalizar o tema do ambiente de trabalho +Comment[pt_BR]=Personaliza o tema da área de trabalho +Comment[ro]=Particularizează tematica de birou +Comment[ru]=Настройка темы рабочего стола +Comment[si]=වැඩතල තේමාව රිසිකරණය කරන්න +Comment[sk]=Prispôsobenie témy plochy +Comment[sl]=Prilagodite namizno temo +Comment[sr]=Прилагодите тему површи +Comment[sr@ijekavian]=Прилагодите тему површи +Comment[sr@ijekavianlatin]=Prilagodite temu površi +Comment[sr@latin]=Prilagodite temu površi +Comment[sv]=Anpassa skrivbordstemat +Comment[tg]=Танзимоти мавзӯъи мизи корӣ +Comment[th]=ปรับแต่งชุดตกแต่งพื้นที่ทำงานด้วยตนเอง +Comment[tr]=Masaüstü temasını özelleştir +Comment[ug]=ئۈستەلئۈستى ئۆرنىكىنى ئۆزلەشتۈرىدۇ +Comment[uk]=Налаштування теми стільниці +Comment[vi]=Tuỳ chỉnh sắc thái màn hình +Comment[wa]=Mete a vosse môde li tinme do scribanne +Comment[x-test]=xxCustomize the desktop themexx +Comment[zh_CN]=定制桌面主题 +Comment[zh_TW]=自訂桌面主題 +X-KDE-Keywords=Desktop Theme +X-KDE-Keywords[ar]=سمة سطح المكتب +X-KDE-Keywords[bs]=Tema površi +X-KDE-Keywords[ca]=Tema d'escriptori +X-KDE-Keywords[ca@valencia]=Tema d'escriptori +X-KDE-Keywords[cs]=Motiv plochy +X-KDE-Keywords[da]=Skrivebordstema +X-KDE-Keywords[de]=Arbeitsflächen-Design +X-KDE-Keywords[el]=Θέμα επιφάνειας εργασίας +X-KDE-Keywords[es]=Tema de escritorio +X-KDE-Keywords[et]=Töölauateema +X-KDE-Keywords[eu]=Mahaigaineko gaia +X-KDE-Keywords[fi]=Työpöytäteema +X-KDE-Keywords[fr]=Thème du bureau +X-KDE-Keywords[ga]=Téama Deisce +X-KDE-Keywords[gl]=Tema do escritorio +X-KDE-Keywords[he]=ערכת־הנושא +X-KDE-Keywords[hu]=Asztali téma +X-KDE-Keywords[ia]=Thema de scriptorio +X-KDE-Keywords[is]=Skjáborðsþema +X-KDE-Keywords[it]=Tema del desktop +X-KDE-Keywords[kk]=Desktop Theme,Үстел нақышы +X-KDE-Keywords[km]=រូបរាង​ផ្ទៃតុ +X-KDE-Keywords[ko]=데스크톱 테마 +X-KDE-Keywords[lt]=Darbastalio tema +X-KDE-Keywords[mr]=डेस्कटॉप शैली +X-KDE-Keywords[nb]=Skrivebordstema +X-KDE-Keywords[nds]=Schriefdischmuster +X-KDE-Keywords[nl]=Bureaubladthema +X-KDE-Keywords[pa]=ਡੈਸਕਟਾਪ ਥੀਮ +X-KDE-Keywords[pl]=Wystrój pulpitu +X-KDE-Keywords[pt]=Tema do Ambiente de Trabalho +X-KDE-Keywords[pt_BR]=Tema da área de trabalho +X-KDE-Keywords[ro]=Tematica de birou +X-KDE-Keywords[ru]=Тема рабочего стола +X-KDE-Keywords[sk]=Téma plochy +X-KDE-Keywords[sl]=Namizna tema +X-KDE-Keywords[sr]=Desktop Theme,тема површи +X-KDE-Keywords[sr@ijekavian]=Desktop Theme,тема површи +X-KDE-Keywords[sr@ijekavianlatin]=Desktop Theme,tema površi +X-KDE-Keywords[sr@latin]=Desktop Theme,tema površi +X-KDE-Keywords[sv]=Skrivbordstema +X-KDE-Keywords[tr]=Masaüstü Teması +X-KDE-Keywords[ug]=ئۈستەلئۈستى ئۆرنىكى +X-KDE-Keywords[uk]=тема,стільниця +X-KDE-Keywords[vi]=Sắc thái màn hình +X-KDE-Keywords[x-test]=xxDesktop Themexx +X-KDE-Keywords[zh_CN]=Desktop Theme,桌面主题 +X-KDE-Keywords[zh_TW]=Desktop Theme diff --git a/kcms/desktoptheme/desktopthemedetails.cpp b/kcms/desktoptheme/desktopthemedetails.cpp new file mode 100644 index 0000000000..ab6973e255 --- /dev/null +++ b/kcms/desktoptheme/desktopthemedetails.cpp @@ -0,0 +1,590 @@ +/* + Copyright (c) 2008 Andrew Lake + Copyright (c) 2010 Jeremy Whiting + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +#include "desktopthemedetails.h" + +#include "thememodel.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct ThemeItemNameType { + const char* m_type; + const char* m_displayItemName; + const char* m_themeItemPath; + const char* m_iconName; +}; + +const ThemeItemNameType themeCollectionName[] = { + { "Color Scheme", I18N_NOOP2("plasma name", "Color Scheme"),"colors", "preferences-desktop-color"}, + { "Panel Background", I18N_NOOP2("plasma name", "Panel Background"),"widgets/panel-background", "plasma"}, + { "Kickoff", I18N_NOOP2("plasma name", "Kickoff"), "dialogs/kickoff", "kde"}, + { "Task Items", I18N_NOOP2("plasma name", "Task Items"), "widgets/tasks", "preferences-system-windows"}, + { "Widget Background", I18N_NOOP2("plasma name", "Widget Background"), "widgets/background", "plasma"}, + { "Translucent Background", I18N_NOOP2("plasma name", "Translucent Background"), "widgets/translucentbackground", "plasma"}, + { "Dialog Background", I18N_NOOP2("plasma name", "Dialog Background"), "dialogs/background", "plasma"}, + { "Analog Clock", I18N_NOOP2("plasma name", "Analog Clock"), "widgets/clock", "chronometer"}, + { "Notes", I18N_NOOP2("plasma name", "Notes"), "widgets/notes", "view-pim-notes"}, + { "Tooltip", I18N_NOOP2("plasma name", "Tooltip"), "widgets/tooltip", "plasma"}, + { "Pager", I18N_NOOP2("plasma name", "Pager"), "widgets/pager", "plasma"}, + { "Run Command Dialog", I18N_NOOP2("plasma name", "Run Command Dialog"), "dialogs/krunner", "system-run"}, + { "Shutdown Dialog", I18N_NOOP2("plasma name", "Shutdown Dialog"), "dialogs/shutdowndialog", "system-shutdown"}, + { "Icons", I18N_NOOP2("plasma name", "Icons"), "icons/", "preferences-desktop-icons"}, + { 0, 0,0,0 } // end of data +}; + + +DesktopThemeDetails::DesktopThemeDetails(QWidget* parent) + : QWidget(parent), + m_themeModel(0) + +{ + setWindowIcon(QIcon::fromTheme("preferences-desktop")); + setupUi(this); + + QFont font = QFont(); + //font.setBold(true); + font.setPointSize(1.2*font.pointSize()); + m_themeInfoName->setFont(font); + + m_enableAdvanced->setChecked(false); + toggleAdvancedVisible(); + + + m_themeModel = new ThemeModel(this); + m_theme->setModel(m_themeModel); + m_theme->setItemDelegate(new ThemeDelegate(m_theme)); + + reloadConfig(); + + connect(m_theme->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(themeSelectionChanged(QItemSelection,QItemSelection))); + + connect(m_enableAdvanced, SIGNAL(toggled(bool)), this, SLOT(toggleAdvancedVisible())); + connect(m_removeThemeButton, SIGNAL(clicked()), this, SLOT(removeTheme())); + connect(m_exportThemeButton, SIGNAL(clicked()), this, SLOT(exportTheme())); + + connect(m_newThemeName, SIGNAL(editingFinished()), this, SLOT(newThemeInfoChanged())); + + m_baseTheme = "default"; + m_themeCustomized = false; + resetThemeDetails(); + adjustSize(); +} + +DesktopThemeDetails::~DesktopThemeDetails() +{ + cleanup(); +} + +void DesktopThemeDetails::cleanup() +{ +} + +void DesktopThemeDetails::reloadConfig() +{ + // Theme + KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig("plasmarc"), "Theme"); + const QString theme = cfg.readEntry("name", "default"); + m_theme->setCurrentIndex(m_themeModel->indexOf(theme)); + +} + +void DesktopThemeDetails::save() +{ + QString themeRoot; + KStandardDirs dirs; + if (m_newThemeName->text().isEmpty()) { + themeRoot = ".customized"; + //Toggle customized theme directory name to ensure theme reload + if (QDir(dirs.locateLocal("data", "desktoptheme/" + themeRoot + '/', false)).exists()) { + themeRoot = themeRoot + '1'; + } + } else { + themeRoot = m_newThemeName->text().replace(' ',"_").remove(QRegExp("[^A-Za-z0-9_]")); + } + + //Save theme items + QFile customSettingsFile; + bool customSettingsFileOpen = false; + + if (m_themeCustomized || !m_newThemeName->text().isEmpty()) { + + clearCustomized(themeRoot); + + //Copy all files from the base theme + QString baseSource = dirs.locate("data", "desktoptheme/" + m_baseTheme + "/metadata.desktop"); + baseSource = baseSource.left(baseSource.lastIndexOf('/', -1)); + KIO::CopyJob *copyBaseTheme = KIO::copyAs(QUrl::fromLocalFile(baseSource), QUrl::fromLocalFile(dirs.locateLocal("data", "desktoptheme/" + themeRoot, true)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(copyBaseTheme, this); + + //Prepare settings file for customized theme + if (isCustomized(themeRoot)) { + customSettingsFile.setFileName(dirs.locateLocal("data", "desktoptheme/" + themeRoot + "/settings")); + customSettingsFileOpen = customSettingsFile.open(QFile::WriteOnly); + if (customSettingsFileOpen) { + QTextStream out(&customSettingsFile); + out << "baseTheme=" + m_baseTheme + "\r\n";; + } + } + QString settingsSource; + + //Copy each item theme file to new theme folder + QHashIterator i(m_itemThemeReplacements); + while (i.hasNext()) { + i.next(); + //Get root directory where item should reside (relative to theme root) + QString itemRoot = ""; + if (m_itemPaths[i.key()].lastIndexOf('/', -1) != -1) { + itemRoot= m_itemPaths[i.key()].left(m_itemPaths[i.key()].lastIndexOf('/', -1)); + } + //Setup source and destination + QString source; + QString dest; + if (i.value() != -1) { + //Source is a theme + source = "desktoptheme/" + m_themeRoots[i.value()] + '/' + m_itemPaths[i.key()] + '*'; + dest = "desktoptheme/" + themeRoot + '/' + itemRoot + '/'; + settingsSource = m_themeRoots[i.value()]; + } else { + //Source is a file + source = m_itemFileReplacements[i.key()]; + dest = "desktoptheme/" + themeRoot + '/' + itemRoot + '/'; + settingsSource = m_itemFileReplacements[i.key()]; + } + + + //Delete item files at destination before copying (possibly there from base theme copy) + const QStringList deleteFiles = dirs.findAllResources("data", "desktoptheme/" + themeRoot + '/' + m_itemPaths[i.key()] + '*', + KStandardDirs::NoDuplicates); + for (int j = 0; j < deleteFiles.size(); ++j) { + KIO::DeleteJob *dj = KIO::del(QUrl::fromLocalFile(deleteFiles.at(j)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(dj, this); + } + + //Copy item(s) + dest = dirs.locateLocal("data", dest, true); + QStringList copyFiles; + if (i.value() != -1) { + copyFiles = dirs.findAllResources("data", source, KStandardDirs::NoDuplicates); //copy from theme + } else { + copyFiles << source; //copy from file + } + for (int j = 0; j < copyFiles.size(); ++j) { + KIO::CopyJob *cj = KIO::copy(QUrl::fromLocalFile(copyFiles.at(j)), QUrl::fromLocalFile(dest), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(cj, this); + } + + //Record settings file + if (customSettingsFileOpen) { + QTextStream out(&customSettingsFile); + out << m_items.key(i.key()) + "=" + settingsSource +"\r\n"; + } + } + if (customSettingsFileOpen) customSettingsFile.close(); + + // Create new theme FDO desktop file + QFile::remove(dirs.locateLocal("data", "desktoptheme/" + themeRoot + "/metadata.desktop", false)); + KDesktopFile df(dirs.locateLocal("data", "desktoptheme/" + themeRoot + "/metadata.desktop")); + KConfigGroup cg = df.desktopGroup(); + if (isCustomized(themeRoot)) { + cg.writeEntry("Name",i18n("(Customized)")); + cg.writeEntry("Comment", i18n("User customized theme")); + cg.writeEntry("X-KDE-PluginInfo-Name", themeRoot); + } else { + cg.writeEntry("Name", m_newThemeName->text()); + cg.writeEntry("Comment", m_newThemeDescription->text()); + cg.writeEntry("X-KDE-PluginInfo-Author", m_newThemeAuthor->text()); + cg.writeEntry("X-KDE-PluginInfo-Version", m_newThemeVersion->text()); + } + cg.sync(); + + m_themeCustomized = false; + } + + m_themeModel->reload(); + if (m_themeModel->indexOf(themeRoot).isValid()) { + m_theme->setCurrentIndex(m_themeModel->indexOf(themeRoot)); + QString themeName = m_theme->currentIndex().data(Qt::DisplayRole).toString(); + setDesktopTheme(themeRoot); + } + resetThemeDetails(); +} + +void DesktopThemeDetails::removeTheme() +{ + bool removeTheme = true; + KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig("plasmarc"), "Theme"); + QString activeTheme = cfg.readEntry("name", "default"); + const QString theme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + const QString themeName = m_theme->currentIndex().data(Qt::DisplayRole).toString(); + + + if (m_themeCustomized) { + if(KMessageBox::questionYesNo(this, i18n("Theme items have been changed. Do you still wish remove the \"%1\" theme?", themeName), i18n("Remove Desktop Theme")) == KMessageBox::No) { + removeTheme = false; + } + } else { + if (theme == "default") { + KMessageBox::information(this, i18n("Removal of the default desktop theme is not allowed."), i18n("Remove Desktop Theme")); + removeTheme = false; + } else { + if(KMessageBox::questionYesNo(this, i18n("Are you sure you wish remove the \"%1\" theme?", themeName), i18n("Remove Desktop Theme")) == KMessageBox::No) { + removeTheme = false; + } + } + + } + KStandardDirs dirs; + if (removeTheme) { + if (theme == activeTheme) { + setDesktopTheme("default"); + activeTheme = "default"; + } + if (QDir(dirs.locateLocal("data", "desktoptheme/" + theme, false)).exists()) { + KIO::DeleteJob *deleteTheme = KIO::del(QUrl::fromLocalFile(dirs.locateLocal("data", "desktoptheme/" + theme, false)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(deleteTheme, this); + } + } + m_themeModel->reload(); + reloadConfig(); + m_theme->setCurrentIndex(m_themeModel->indexOf(activeTheme)); +} + +void DesktopThemeDetails::exportTheme() +{ + const QString theme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + + if (m_themeCustomized || + (isCustomized(theme) && m_newThemeName->text() == "")) { + KMessageBox::information(this, i18n("Please apply theme item changes (with a new theme name) before attempting to export theme."), i18n("Export Desktop Theme")); + } else { + QString themeStoragePath = theme; + + KStandardDirs dirs; + const QString themePath = dirs.findResource("data", "desktoptheme/" + themeStoragePath + "/metadata.desktop"); + if (!themePath.isEmpty()){ + QString expFileName = QFileDialog::getSaveFileName(this, i18n("Export theme to file"), QString(), i18n("Archive (*.zip)")); + if (!expFileName.endsWith(".zip")) + expFileName = expFileName + ".zip"; + if (!expFileName.isEmpty()) { + KUrl path(themePath); + KZip expFile(expFileName); + expFile.open(QIODevice::WriteOnly); + expFile.addLocalDirectory(path.directory (), themeStoragePath); + expFile.close(); + } + } + } + +} + +void DesktopThemeDetails::loadThemeItems() +{ + // Set up items, item paths and icons + m_items.clear(); // clear theme items + m_itemPaths.clear(); // clear theme item paths + m_itemIcons.clear(); + for (int i = 0; themeCollectionName[i].m_type; ++i) { + m_items[themeCollectionName[i].m_type] = i; + m_itemPaths[i] = themeCollectionName[i].m_themeItemPath; + m_itemIcons[i] = themeCollectionName[i].m_iconName; + } + + // Get installed themes + m_themes.clear(); // clear installed theme list + m_themeRoots.clear(); // clear installed theme root paths + KStandardDirs dirs; + QStringList themes = dirs.findAllResources("data", "desktoptheme/*/metadata.desktop", + KStandardDirs::NoDuplicates); + themes.sort(); + int j=0; + for (int i = 0; i < themes.size(); ++i) { + QString theme = themes.at(i); + int themeSepIndex = theme.lastIndexOf('/', -1); + QString themeRoot = theme.left(themeSepIndex); + int themeNameSepIndex = themeRoot.lastIndexOf('/', -1); + QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); + KDesktopFile df(theme); + QString name = df.readName(); + if (name.isEmpty()) { + name = packageName; + } + + if (!isCustomized(packageName) && (m_themeRoots.key(packageName, -1) == -1)) { + m_themes[name] = j; + m_themeRoots[j] = packageName; + ++j; + } + } + + // Set up default item replacements + m_itemThemeReplacements.clear(); + m_itemFileReplacements.clear(); + QString currentTheme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + + if (!isCustomized(currentTheme)) { + // Set default replacements to current theme + QHashIterator i(m_items); + while (i.hasNext()) { + i.next(); + m_itemThemeReplacements[i.value()] = m_themeRoots.key(currentTheme); + } + m_baseTheme = currentTheme; + } else { + // Set default replacements to customized theme settings + QFile customSettingsFile(dirs.locateLocal("data", "desktoptheme/" + currentTheme +"/settings")); + if (customSettingsFile.open(QFile::ReadOnly)) { + QTextStream in(&customSettingsFile); + QString line; + QStringList settingsPair; + while (!in.atEnd()) { + line = in.readLine(); + settingsPair = line.split('='); + if (settingsPair.at(0) == "baseTheme") { + m_baseTheme = settingsPair.at(1); + } else { + if (m_themeRoots.key(settingsPair.at(1), -1) != -1) { // theme for current item exists + m_itemThemeReplacements[m_items[settingsPair.at(0)]] = m_themeRoots.key(settingsPair.at(1)); + } else if (QFile::exists(settingsPair.at(1))) { + m_itemThemeReplacements[m_items[settingsPair.at(0)]] = -1; + m_itemFileReplacements[m_items[settingsPair.at(0)]] = settingsPair.at(1); + } + } + } + customSettingsFile.close(); + } + } + + // Build displayed list of theme items + m_themeItemList->setRowCount(m_items.size()); + m_themeItemList->setColumnCount(2); + m_themeItemList->setHorizontalHeaderLabels(QStringList()<< i18n("Theme Item")< i(m_items); + while (i.hasNext()) { + i.next(); + displayedItem = displayedItemText(i.value()); + m_themeItemList->setItem(i.value(), 0, new QTableWidgetItem(displayedItem)); + m_themeItemList->item(i.value(),0)->setIcon(QIcon::fromTheme(m_itemIcons[i.value()])); + m_themeItemList->setCellWidget(i.value(), 1, new QComboBox()); + updateReplaceItemList(i.value()); + m_themeItemList->resizeColumnToContents(1); + } + m_themeItemList->setSelectionBehavior(QAbstractItemView::SelectRows); + m_themeItemList->verticalHeader()->hide(); + m_themeItemList->horizontalHeader()->setStretchLastSection(true); + m_themeItemList->horizontalHeader()->setMinimumSectionSize(120); + m_themeItemList->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents);; + m_themeItemList->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);; + m_themeItemList->setCurrentCell(0, 1); +} + +void DesktopThemeDetails::updateReplaceItemList(const int& item) +{ + QString currentTheme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + + + // Repopulate combobox droplist + QComboBox *itemComboBox = static_cast(m_themeItemList->cellWidget(item,1)); + disconnect(itemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(replacementItemChanged())); + itemComboBox->clear(); + for (int i = 0; i < m_themes.size(); ++i) { + QString displayedDropListItem = i18n("%1 %2", m_themes.key(i), displayedItemText(item)); + itemComboBox->addItem(displayedDropListItem); + } + itemComboBox->addItem(i18n("File...")); + + // Set combobox value to current replacement + if (m_itemThemeReplacements[item] != -1) { + itemComboBox->setCurrentIndex(m_itemThemeReplacements[item]); + } else { + itemComboBox->addItem(m_itemFileReplacements[item]); + itemComboBox->setCurrentIndex(itemComboBox->findText(m_itemFileReplacements[item])); + } + connect(itemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(replacementItemChanged())); +} + +void DesktopThemeDetails::replacementItemChanged() +{ + //Check items to see if theme has been customized + m_themeCustomized = true; + QHashIterator i(m_items); + while (i.hasNext()) { + i.next(); + QComboBox *itemComboBox = static_cast(m_themeItemList->cellWidget(i.value(), 1)); + int replacement = itemComboBox->currentIndex(); + if (replacement <= (m_themes.size() - 1)) { + // Item replacement source is a theme + m_itemThemeReplacements[i.value()] = itemComboBox->currentIndex(); + } else if (replacement > (m_themes.size() - 1)) { + // Item replacement source is a file + if (itemComboBox->currentText() == i18n("File...")) { + //Get the filename for the replacement item + QString translated_key = i18nc("plasma name", qPrintable( i.key() ) ); + QString fileReplacement = QFileDialog::getOpenFileName(this, i18n("Select File to Use for %1",translated_key)); + if (!fileReplacement.isEmpty()) { + m_itemFileReplacements[i.value()] = fileReplacement; + int index = itemComboBox->findText(fileReplacement); + if (index == -1) itemComboBox->addItem(fileReplacement); + disconnect(itemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(replacementItemChanged())); + itemComboBox->setCurrentIndex(itemComboBox->findText(fileReplacement)); + connect(itemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(replacementItemChanged())); + m_itemThemeReplacements[i.value()] = -1; //source is not a theme + m_itemFileReplacements[i.value()] = itemComboBox->currentText(); + } else { + // Reset combobox to previous value if no file is selected + if (m_itemThemeReplacements[i.value()] != -1) { + itemComboBox->setCurrentIndex(m_itemThemeReplacements[i.value()]); + } else { + itemComboBox->setCurrentIndex(itemComboBox->findText(m_itemFileReplacements[i.value()])); + } + m_themeCustomized = false; + } + } else { + m_itemThemeReplacements[i.value()] = -1; //source is not a theme + m_itemFileReplacements[i.value()] = itemComboBox->currentText(); + } + } + } + + if (m_themeCustomized) emit changed(); +} + +void DesktopThemeDetails::newThemeInfoChanged() +{ + emit changed(); +} + +void DesktopThemeDetails::resetThemeDetails() +{ + QString theme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + + m_themeInfoName->setText(m_theme->currentIndex().data(Qt::DisplayRole).toString()); + m_themeInfoDescription->setText(m_theme->currentIndex().data(ThemeModel::PackageDescriptionRole).toString()); + QString author = m_theme->currentIndex().data(ThemeModel::PackageAuthorRole).toString(); + + if (!author.isEmpty()) { + m_themeInfoAuthor->setText(i18n(" Author: %1",author)); + } else { + m_themeInfoAuthor->setText(""); + } + QString version = m_theme->currentIndex().data(ThemeModel::PackageVersionRole).toString(); + if (!version.isEmpty()) { + m_themeInfoVersion->setText(i18n("Version: %1",version)); + } else { + m_themeInfoVersion->setText(""); + } + + loadThemeItems(); + + m_newThemeName->clear(); + m_newThemeAuthor->clear(); + m_newThemeVersion->clear(); + m_newThemeDescription->clear(); + m_themeCustomized = false; + } + +void DesktopThemeDetails::toggleAdvancedVisible() +{ + m_newThemeNameLabel->setVisible(m_enableAdvanced->isChecked()); + m_newThemeName->setVisible(m_enableAdvanced->isChecked()); + m_newThemeAuthor->setVisible(m_enableAdvanced->isChecked()); + m_newThemeAuthorLabel->setVisible(m_enableAdvanced->isChecked()); + m_newThemeVersion->setVisible(m_enableAdvanced->isChecked()); + m_newThemeVersionLabel->setVisible(m_enableAdvanced->isChecked()); + m_newThemeDescriptionLabel->setVisible(m_enableAdvanced->isChecked()); + m_newThemeDescription->setVisible(m_enableAdvanced->isChecked()); + m_exportThemeButton->setVisible(m_enableAdvanced->isChecked()); + m_removeThemeButton->setVisible(m_enableAdvanced->isChecked()); + m_advancedLine->setVisible(m_enableAdvanced->isChecked()); +} + +bool DesktopThemeDetails::isCustomized(const QString& theme) { + if (theme == ".customized" || theme == ".customized1") { + return true; + } else { + return false; + } +} + +void DesktopThemeDetails::clearCustomized(const QString& themeRoot) { + KStandardDirs dirs; + + if ((isCustomized(themeRoot))) { + // Remove both possible unnamed customized directories + if (QDir(dirs.locateLocal("data", "desktoptheme/.customized", false)).exists()) { + KIO::DeleteJob *clearCustom = KIO::del(QUrl::fromLocalFile(dirs.locateLocal("data", "desktoptheme/.customized", false)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(clearCustom, this); + } + if (QDir(dirs.locateLocal("data", "desktoptheme/.customized1", false)).exists()) { + KIO::DeleteJob *clearCustom1 = KIO::del(QUrl::fromLocalFile(dirs.locateLocal("data", "desktoptheme/.customized1", false)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(clearCustom1, this); + } + } else { + if (QDir(dirs.locateLocal("data", "desktoptheme/" + themeRoot, false)).exists()) { + KIO::DeleteJob *clearCustom = KIO::del(QUrl::fromLocalFile(dirs.locateLocal("data", "desktoptheme/" + themeRoot, false)), KIO::HideProgressInfo); + KIO::NetAccess::synchronousRun(clearCustom, this); + } + } +} + +QString DesktopThemeDetails::displayedItemText(int item) { + QString displayedText = m_items.key(item); + for (int i = 0; themeCollectionName[i].m_type; ++i) { + if (themeCollectionName[i].m_type == m_items.key(item)) { + displayedText = i18nc("plasma name", themeCollectionName[i].m_displayItemName); + } + } + return displayedText; +} + +void DesktopThemeDetails::themeSelectionChanged(const QItemSelection newSelection, const QItemSelection oldSelection) +{ + QString theme = m_theme->currentIndex().data(ThemeModel::PackageNameRole).toString(); + if (theme == "default") { + m_removeThemeButton->setEnabled(false); + } else { + m_removeThemeButton->setEnabled(true); + } + resetThemeDetails(); + Q_UNUSED(newSelection); + Q_UNUSED(oldSelection); +} + +void DesktopThemeDetails::setDesktopTheme(QString themeRoot) +{ + KConfig config(QStandardPaths::locate(QStandardPaths::ConfigLocation, "plasmarc")); + KConfigGroup cg = KConfigGroup(&config, "Theme"); + if (themeRoot == "default") { + cg.deleteEntry("name"); + } else { + cg.writeEntry("name", themeRoot); + } + cg.sync(); +} diff --git a/kcms/desktoptheme/desktopthemedetails.h b/kcms/desktoptheme/desktopthemedetails.h new file mode 100644 index 0000000000..5eab98ebad --- /dev/null +++ b/kcms/desktoptheme/desktopthemedetails.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007 Paolo Capriotti + Copyright (c) 2008 by Petri Damsten + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +#ifndef DESKTOPTHEMEDETAILS_H +#define DESKTOPTHEMEDETAILS_H + +#include "ui_DesktopThemeDetails.h" + +#include + +class ThemeModel; + +class DesktopThemeDetails : public QWidget, public Ui::DesktopThemeItems +{ + Q_OBJECT +public: + DesktopThemeDetails(QWidget* parent); + ~DesktopThemeDetails(); + + void reloadConfig(); + +public Q_SLOTS: + void replacementItemChanged(); + void resetThemeDetails(); + void toggleAdvancedVisible(); + void save(); + void removeTheme(); + void exportTheme(); + void newThemeInfoChanged(); + +Q_SIGNALS: + void changed(); + +private: + void updateReplaceItemList(const int& item); + void loadThemeItems(); + QString displayedItemText(int item); + bool isCustomized(const QString& theme); + void clearCustomized(const QString& themeRoot); + void setDesktopTheme(QString themeRoot); + +private Q_SLOTS: + void cleanup(); + void themeSelectionChanged(const QItemSelection newSelection, const QItemSelection oldSelection); + +private: + ThemeModel* m_themeModel; + + QHash m_items; // theme items + QHash m_itemPaths; // theme item paths + QHash m_itemIcons; //theme item icons + QHash m_themes; // installed themes + QHash m_themeRoots; // installed themes root paths + QHash m_itemThemeReplacements; // source theme for item replacements + QHashm_itemFileReplacements; //non-theme source files for item replacements + + bool m_themeCustomized; + QString m_baseTheme; +}; + +#endif // DESKTOPTHEMEDETAILS_H diff --git a/kcms/desktoptheme/kcmdesktoptheme.cpp b/kcms/desktoptheme/kcmdesktoptheme.cpp new file mode 100644 index 0000000000..1d82ef16a3 --- /dev/null +++ b/kcms/desktoptheme/kcmdesktoptheme.cpp @@ -0,0 +1,260 @@ +/* + * KCMDesktopTheme + * Copyright (C) 2002 Karol Szwed + * Copyright (C) 2002 Daniel Molkentin + * Copyright (C) 2007 Urs Wolfer + * Copyright (C) 2009 by Davide Bettio + + * Portions Copyright (C) 2007 Paolo Capriotti + * Portions Copyright (C) 2007 Ivan Cukic + * Portions Copyright (C) 2008 by Petri Damsten + * Portions Copyright (C) 2000 TrollTech AS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kcmdesktoptheme.h" + +#include "thememodel.h" + +#include + +#include +#include +#include +#include +#include + +#include + +/**** DLL Interface for kcontrol ****/ + +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KCMDesktopThemeFactory, "desktoptheme.json", registerPlugin();) + + +KCMDesktopTheme::KCMDesktopTheme( QWidget* parent, const QVariantList& ) + : KCModule( parent ) + , m_dialog(0) + , m_installProcess(0) + , m_defaultTheme(new Plasma::Theme(this)) +{ + setQuickHelp( i18n("

    Desktop Theme

    " + "This module allows you to modify the visual appearance " + "of the desktop.")); + + setupUi(this); + + m_bDesktopThemeDirty = false; + m_bDetailsDirty = false; + + KAutostart plasmaNetbookAutoStart("plasma-netbook"); + m_isNetbook = plasmaNetbookAutoStart.autostarts(); + + KGlobal::dirs()->addResourceType("themes", "data", "kstyle/themes"); + + KAboutData *about = + new KAboutData( I18N_NOOP("KCMDesktopTheme"), 0, + i18n("KDE Desktop Theme Module"), + 0, QString(), KAboutData::License_GPL, + i18n("(c) 2002 Karol Szwed, Daniel Molkentin")); + + about->addAuthor(i18n("Karol Szwed"), QString(), QStringLiteral("gallium@kde.org")); + about->addAuthor(i18n("Daniel Molkentin"), QString(), QStringLiteral("molkentin@kde.org")); + about->addAuthor(i18n("Ralf Nolden"), QString(), QStringLiteral("nolden@kde.org")); + setAboutData( about ); + + m_newThemeButton->setIcon(QIcon::fromTheme("get-hot-new-stuff")); + + m_themeModel = new ThemeModel(this); + m_theme->setModel(m_themeModel); + m_theme->setItemDelegate(new ThemeDelegate(m_theme)); + m_theme->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + + connect(m_detailsWidget, SIGNAL(changed()), this, SLOT(detailChanged())); + + connect(m_theme->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(setDesktopThemeDirty())); + connect(m_newThemeButton, SIGNAL(clicked()), this, SLOT(getNewThemes())); + connect(m_fileInstallButton, &QPushButton::clicked, this, &KCMDesktopTheme::showFileDialog); +} + + +KCMDesktopTheme::~KCMDesktopTheme() +{ +} + +void KCMDesktopTheme::load() +{ + KConfig config( "kdeglobals", KConfig::FullConfig ); + + loadDesktopTheme(); + + m_bDesktopThemeDirty = false; + m_bDetailsDirty = false; + + emit changed( false ); +} + + +void KCMDesktopTheme::save() +{ + qDebug() << "Save!"; + // Don't do anything if we don't need to. + if ( !( m_bDesktopThemeDirty) && !(m_bDetailsDirty) ) + return; + + //Desktop theme + if ( m_bDesktopThemeDirty ) + { + QString theme = m_themeModel->data(m_theme->currentIndex(), ThemeModel::PackageNameRole).toString(); + qDebug() << "theme changed to " << theme; + if (m_isNetbook) { + KConfigGroup cg(KSharedConfig::openConfig("plasmarc"), "Theme-plasma-netbook"); + cg.writeEntry("name", theme); + } else { + qDebug() << "m-defaultTheme" << theme; + m_defaultTheme->setThemeName(theme); + } + } + + if (m_bDetailsDirty) + { + m_detailsWidget->save(); + } + + // Clean up + m_bDesktopThemeDirty = false; + m_bDetailsDirty = false; + emit changed( false ); + qDebug() << "saved."; +} + +void KCMDesktopTheme::defaults() +{ + // TODO: reset back to default theme? +} + +void KCMDesktopTheme::setDesktopThemeDirty() +{ + m_bDesktopThemeDirty = true; + emit changed(true); +} + +void KCMDesktopTheme::getNewThemes() +{ + KNS3::DownloadDialog dialog("plasma-themes.knsrc", this); + dialog.exec(); + KNS3::Entry::List entries = dialog.changedEntries(); + + if (entries.size() > 0) { + loadDesktopTheme(); + } +} + +void KCMDesktopTheme::loadDesktopTheme() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + m_themeModel->reload(); + QString themeName; + KConfigGroup cg(KSharedConfig::openConfig("plasmarc"), "Theme"); + themeName = cg.readEntry("name", m_defaultTheme->themeName()); + + m_theme->setCurrentIndex(m_themeModel->indexOf(themeName)); + QApplication::restoreOverrideCursor(); +} + +void KCMDesktopTheme::detailChanged() +{ + m_bDetailsDirty = true; + emit changed(true); +} + +void KCMDesktopTheme::showFileDialog() +{ + if (!m_dialog) { + QUrl baseUrl; + m_dialog = new QFileDialog(m_fileInstallButton, i18n("Open Theme"), + QDir::homePath(), + i18n("Theme Files (*.zip *.tar.gz *.tar.bz2)")); + m_dialog->setFileMode(QFileDialog::ExistingFile); + connect(m_dialog, &QDialog::accepted, this, &KCMDesktopTheme::fileBrowserCompleted); + } + + m_dialog->exec(); + m_dialog->raise(); + m_dialog->activateWindow(); +} + +void KCMDesktopTheme::fileBrowserCompleted() +{ + if (m_dialog && m_dialog->selectedFiles().count() > 0) { + foreach (const QString &file, m_dialog->selectedFiles()) { + qDebug() << "INSTALL Theme file: " << file; + m_newThemeButton->setEnabled(false); + installTheme(file); + } + } +} + +void KCMDesktopTheme::installTheme(const QString &file) +{ + qDebug() << "Installing ... " << file; + + QString program = "plasmapkg2"; + QStringList arguments; + arguments << "-t" << "theme" << "-i" << file; + + if (!m_installProcess) { + qDebug() << program << arguments.join(" "); + QProcess *myProcess = new QProcess(this); + //connect(myProcess, &QProcess::finished, this, &KCMDesktopTheme::installFinished); + connect(myProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(installFinished(int, QProcess::ExitStatus))); + connect(myProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(installError(QProcess::ProcessError))); + + myProcess->start(program, arguments); + } else { + qWarning() << "theme install process already running, refusing to install simultaneously"; + } +} + +void KCMDesktopTheme::installFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + //int exitCode = m_installProcess->exitCode(); + if (exitCode == 0) { + qDebug() << "Theme installed successfully :)"; + m_themeModel->reload(); + m_statusLabel->setText(i18n("Theme installed successfully.")); + } else { + qDebug() << "Theme installation failed."; + m_statusLabel->setText(i18n("Theme installation failed. (%1)", exitCode)); + } + m_newThemeButton->setEnabled(true); +} + +void KCMDesktopTheme::installError(QProcess::ProcessError e) +{ + qDebug() << "Theme installation failed. :("; + m_statusLabel->setText(i18n("Theme installation failed.")); + m_newThemeButton->setEnabled(true); + +} + + +#include "kcmdesktoptheme.moc" + +// vim: set noet ts=4: diff --git a/kcms/desktoptheme/kcmdesktoptheme.h b/kcms/desktoptheme/kcmdesktoptheme.h new file mode 100644 index 0000000000..ad32b4a585 --- /dev/null +++ b/kcms/desktoptheme/kcmdesktoptheme.h @@ -0,0 +1,90 @@ +/* + * KCMDesktopTheme + * Copyright (C) 2002 Karol Szwed + * Copyright (C) 2002 Daniel Molkentin + * Copyright (C) 2007 Urs Wolfer + * + * Portions Copyright (C) TrollTech AS. + * + * Based on kcmstyle which is based on kcmdisplay + * Copyright (C) 1997-2002 kcmdisplay Authors. + * (see Help -> About Style Settings) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCMDESKTOPTHEME_H +#define KCMDESKTOPTHEME_H + +#include + + +#include "ui_DesktopTheme.h" + +#include + +class QFileDialog; +class ThemeModel; + +namespace Plasma +{ + class Theme; +} + +class KCMDesktopTheme : public KCModule, public Ui::DesktopTheme +{ + Q_OBJECT + +public: + KCMDesktopTheme( QWidget* parent, const QVariantList& ); + ~KCMDesktopTheme(); + + virtual void load(); + virtual void save(); + virtual void defaults(); + +protected Q_SLOTS: + void loadDesktopTheme(); + + void setDesktopThemeDirty(); + + void getNewThemes(); + + void detailChanged(); + void installFinished(int exitCode, QProcess::ExitStatus exitStatus); + void installError(QProcess::ProcessError e); + +private: + static QString toolbarButtonText(int index); + static int toolbarButtonIndex(const QString &text); + + bool m_bDesktopThemeDirty; + bool m_bDetailsDirty; + + void showFileDialog(); + QFileDialog *m_dialog; + void installTheme(const QString &file); + void fileBrowserCompleted(); + + ThemeModel* m_themeModel; + Plasma::Theme *m_defaultTheme; + QProcess *m_installProcess; + + bool m_isNetbook; +}; + +#endif // KCMDESKTOPTHEME_H + +// vim: set noet ts=4: diff --git a/kcms/desktoptheme/plasma-themes.knsrc b/kcms/desktoptheme/plasma-themes.knsrc new file mode 100644 index 0000000000..6f85777a5c --- /dev/null +++ b/kcms/desktoptheme/plasma-themes.knsrc @@ -0,0 +1,6 @@ +[KNewStuff3] +ProvidersUrl=http://download.kde.org/ocs/providers.xml +Categories=Plasma Theme +StandardResource=tmp +InstallationCommand=plasmapkg2 -t theme -i %f +UninstallCommand=plasmapkg2 -t theme -r %f diff --git a/kcms/desktoptheme/thememodel.cpp b/kcms/desktoptheme/thememodel.cpp new file mode 100644 index 0000000000..e712489bbb --- /dev/null +++ b/kcms/desktoptheme/thememodel.cpp @@ -0,0 +1,224 @@ +/* + * ThemeModel + * Copyright (C) 2002 Karol Szwed + * Copyright (C) 2002 Daniel Molkentin + * Copyright (C) 2007 Urs Wolfer + * Copyright (C) 2009 by Davide Bettio + + * Portions Copyright (C) 2007 Paolo Capriotti + * Portions Copyright (C) 2007 Ivan Cukic + * Portions Copyright (C) 2008 by Petri Damsten + * Portions Copyright (C) 2000 TrollTech AS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "thememodel.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +ThemeModel::ThemeModel( QObject *parent ) +: QAbstractListModel( parent ) +{ + reload(); +} + +ThemeModel::~ThemeModel() +{ + clearThemeList(); +} + +void ThemeModel::clearThemeList() +{ + foreach (const ThemeInfo& themeInfo, m_themes) { + delete themeInfo.svg; + } + m_themes.clear(); +} + +void ThemeModel::reload() +{ + reset(); + clearThemeList(); + + // get all desktop themes + QStringList themes; + const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "desktoptheme", QStandardPaths::LocateDirectory); + foreach (const QString &ppath, packs) { + const QDir cd(ppath); + const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden); + foreach (const QString pack, entries) { + const QString _metadata = ppath+QLatin1Char('/')+pack+QStringLiteral("/metadata.desktop"); + if ((pack != "." && pack != "..") && + (QFile::exists(_metadata))) { + themes << _metadata; + } + } + } + + foreach (const QString &theme, themes) { + int themeSepIndex = theme.lastIndexOf('/', -1); + QString themeRoot = theme.left(themeSepIndex); + int themeNameSepIndex = themeRoot.lastIndexOf('/', -1); + QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); + + KDesktopFile df(theme); + + if (df.noDisplay()) { + continue; + } + + QString name = df.readName(); + if (name.isEmpty()) { + name = packageName; + } + const QString comment = df.readComment(); + const QString author = df.desktopGroup().readEntry("X-KDE-PluginInfo-Author",QString()); + const QString version = df.desktopGroup().readEntry("X-KDE-PluginInfo-Version",QString()); + + + Plasma::FrameSvg *svg = new Plasma::FrameSvg(this); + const QString svgFile = themeRoot + QStringLiteral("/widgets/background.svg"); + if (QFile::exists(svgFile)) { + svg->setImagePath(svgFile); + } else { + svg->setImagePath(svgFile + "z"); + } + svg->setEnabledBorders(Plasma::FrameSvg::AllBorders); + ThemeInfo info; + info.package = packageName; + info.description = comment; + info.author = author; + info.version = version; + info.svg = svg; + info.themeRoot = themeRoot; + m_themes[name] = info; + } + + beginInsertRows(QModelIndex(), 0, m_themes.size()); + endInsertRows(); +} + +int ThemeModel::rowCount(const QModelIndex &) const +{ + return m_themes.size(); +} + +QVariant ThemeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (index.row() >= m_themes.size()) { + return QVariant(); + } + + QMap::const_iterator it = m_themes.constBegin(); + for (int i = 0; i < index.row(); ++i) { + ++it; + } + + switch (role) { + case Qt::DisplayRole: + return it.key(); + case PackageNameRole: + return (*it).package; + case SvgRole: + return qVariantFromValue((void*)(*it).svg); + case PackageDescriptionRole: + return (*it).description; + case PackageAuthorRole: + return (*it).author; + case PackageVersionRole: + return (*it).version; + default: + return QVariant(); + } +} + +QModelIndex ThemeModel::indexOf(const QString &name) const +{ + QMapIterator it(m_themes); + int i = -1; + while (it.hasNext()) { + ++i; + if (it.next().value().package == name) { + return index(i, 0); + } + } + + return QModelIndex(); +} + +ThemeDelegate::ThemeDelegate(QObject* parent) +: QAbstractItemDelegate(parent) +{ +} + +void ThemeDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QString title = index.model()->data(index, Qt::DisplayRole).toString(); + QString package = index.model()->data(index, ThemeModel::PackageNameRole).toString(); + + QStyleOptionViewItemV4 opt(option); + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); + + // draw image + Plasma::FrameSvg *svg = static_cast( + index.model()->data(index, ThemeModel::SvgRole).value()); + svg->resizeFrame(QSize(option.rect.width() - (2 * MARGIN), 100 - (2 * MARGIN))); + QRect imgRect = QRect(option.rect.topLeft(), + QSize(option.rect.width() - (2 * MARGIN), 100 - (2 * MARGIN))) + .translated(MARGIN, MARGIN); + svg->paintFrame(painter, QPoint(option.rect.left() + MARGIN, option.rect.top() + MARGIN)); + + // draw text + painter->save(); + QFont font = painter->font(); + //font.setWeight(QFont::Bold); + const QString colorFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "desktoptheme/" + package + "/colors"); + if (!colorFile.isEmpty()) { + KSharedConfigPtr colors = KSharedConfig::openConfig(colorFile); + KColorScheme colorScheme(QPalette::Active, KColorScheme::Window, colors); + painter->setPen(colorScheme.foreground(KColorScheme::NormalText).color()); + } + painter->setFont(font); + painter->drawText(option.rect, Qt::AlignCenter | Qt::TextWordWrap, title); + painter->restore(); +} + +QSize ThemeDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const +{ + return QSize(152, 100); +} + + diff --git a/kcms/desktoptheme/thememodel.h b/kcms/desktoptheme/thememodel.h new file mode 100644 index 0000000000..bf0d9ebcc7 --- /dev/null +++ b/kcms/desktoptheme/thememodel.h @@ -0,0 +1,87 @@ +/* + * ThemeModel + * Copyright (C) 2002 Karol Szwed + * Copyright (C) 2002 Daniel Molkentin + * Copyright (C) 2007 Urs Wolfer + * Copyright (C) 2009 by Davide Bettio + + * Portions Copyright (C) 2007 Paolo Capriotti + * Portions Copyright (C) 2007 Ivan Cukic + * Portions Copyright (C) 2008 by Petri Damsten + * Portions Copyright (C) 2000 TrollTech AS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THEMEMODEL_H +#define THEMEMODEL_H + +#include + +namespace Plasma +{ + class FrameSvg; +} + +//Theme selector code by Andre Duffeck (modified to add package description) +class ThemeInfo +{ +public: + QString package; + Plasma::FrameSvg *svg; + QString description; + QString author; + QString version; + QString themeRoot; +}; + +class ThemeModel : public QAbstractListModel +{ +public: + enum { PackageNameRole = Qt::UserRole, + SvgRole = Qt::UserRole + 1, + PackageDescriptionRole = Qt::UserRole + 2, + PackageAuthorRole = Qt::UserRole + 3, + PackageVersionRole = Qt::UserRole + 4 + }; + + ThemeModel(QObject *parent = 0); + virtual ~ThemeModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QModelIndex indexOf(const QString &path) const; + void reload(); + void clearThemeList(); +private: + QMap m_themes; +}; + +class ThemeDelegate : public QAbstractItemDelegate +{ +public: + ThemeDelegate(QObject * parent = 0); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; +private: + static const int MARGIN = 10; +}; + + +#endif diff --git a/kcms/fonts/CMakeLists.txt b/kcms/fonts/CMakeLists.txt new file mode 100644 index 0000000000..f1a800a68f --- /dev/null +++ b/kcms/fonts/CMakeLists.txt @@ -0,0 +1,37 @@ +if(FONTCONFIG_FOUND) + include_directories(${FONTCONFIG_INCLUDE_DIR}) +endif() +include_directories(${FREETYPE_INCLUDE_DIRS}) + +########### next target ############### + +set(kcm_fonts_PART_SRCS ../krdb/krdb.cpp fonts.cpp) + +if(X11_FOUND) + set(kcm_fonts_PART_SRCS ${kcm_fonts_PART_SRCS} ${libkxftconfig_SRCS}) +endif() + +set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) +qt5_add_dbus_interface(kcm_fonts_PART_SRCS ${klauncher_xml} klauncher_iface) + +add_library(kcm_fonts MODULE ${kcm_fonts_PART_SRCS}) + + +target_link_libraries(kcm_fonts Qt5::DBus Qt5::Xml KF5::KCMUtils KF5::I18n KF5::KDE4Support ${FREETYPE_LIBRARIES}) + +if(X11_FOUND) + set_target_properties(kcm_fonts PROPERTIES COMPILE_FLAGS -DHAVE_X11=1) + target_link_libraries(kcm_fonts Qt5::X11Extras) + if(FONTCONFIG_FOUND) + target_link_libraries(kcm_fonts ${FONTCONFIG_LIBRARIES}) + endif() + + target_link_libraries(kcm_fonts ${X11_LIBRARIES}) +endif() + +install(TARGETS kcm_fonts DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES fonts.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcms/fonts/Messages.sh b/kcms/fonts/Messages.sh new file mode 100644 index 0000000000..c49832c6a0 --- /dev/null +++ b/kcms/fonts/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kcmfonts.pot diff --git a/kcms/fonts/fonts.cpp b/kcms/fonts/fonts.cpp new file mode 100644 index 0000000000..974776146f --- /dev/null +++ b/kcms/fonts/fonts.cpp @@ -0,0 +1,850 @@ +// KDE Display fonts setup tab +// +// Copyright (c) Mark Donohoe 1997 +// lars Knoll 1999 +// Rik Hemsley 2000 +// +// Ported to kcontrol2 by Geert Jansen. + +#include + +#include "fonts.h" + +#include + +#include +#include +#include +#include +#include +#include + +//Added by qt3to4: +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../krdb/krdb.h" + +#ifdef HAVE_FREETYPE +#include +#ifdef FT_LCD_FILTER_H +#include FT_FREETYPE_H +#include FT_LCD_FILTER_H +#endif +#endif + +#if HAVE_X11 +#include +#endif + +#if HAVE_X11 +// X11 headers +#undef Bool +#undef Unsorted +#undef None +#endif + +static const char * const aa_rgb_xpm[]={ +"12 12 3 1", +"a c #0000ff", +"# c #00ff00", +". c #ff0000", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa"}; +static const char * const aa_bgr_xpm[]={ +"12 12 3 1", +". c #0000ff", +"# c #00ff00", +"a c #ff0000", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa", +"....####aaaa"}; +static const char * const aa_vrgb_xpm[]={ +"12 12 3 1", +"a c #0000ff", +"# c #00ff00", +". c #ff0000", +"............", +"............", +"............", +"............", +"############", +"############", +"############", +"############", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa"}; +static const char * const aa_vbgr_xpm[]={ +"12 12 3 1", +". c #0000ff", +"# c #00ff00", +"a c #ff0000", +"............", +"............", +"............", +"............", +"############", +"############", +"############", +"############", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa", +"aaaaaaaaaaaa"}; + +static const char* const * const aaPixmaps[]={ aa_rgb_xpm, aa_bgr_xpm, aa_vrgb_xpm, aa_vbgr_xpm }; + +/**** DLL Interface ****/ +K_PLUGIN_FACTORY(FontFactory, registerPlugin(); ) + +/**** FontUseItem ****/ + +FontUseItem::FontUseItem( + QWidget * parent, + const QString &name, + const QString &grp, + const QString &key, + const QString &rc, + const QFont &default_fnt, + bool f +) + : KFontRequester(parent, f), + _rcfile(rc), + _rcgroup(grp), + _rckey(key), + _default(default_fnt) +{ + KAcceleratorManager::setNoAccel( this ); + setTitle( name ); + readFont(); +} + +void FontUseItem::setDefault() +{ + setFont( _default, isFixedOnly() ); +} + +void FontUseItem::readFont() +{ + KConfig *config; + + bool deleteme = false; + if (_rcfile.isEmpty()) + config = KSharedConfig::openConfig().data(); + else + { + config = new KConfig(_rcfile); + deleteme = true; + } + + KConfigGroup group(config, _rcgroup); + QFont tmpFnt(_default); + setFont( group.readEntry(_rckey, tmpFnt), isFixedOnly() ); + if (deleteme) delete config; +} + +void FontUseItem::writeFont() +{ + KConfig *config; + + if (_rcfile.isEmpty()) { + config = KSharedConfig::openConfig().data(); + KConfigGroup(config, _rcgroup).writeEntry(_rckey, font(), KConfig::Normal|KConfig::Global); + } else { + config = new KConfig(KStandardDirs::locateLocal("config", _rcfile)); + KConfigGroup(config, _rcgroup).writeEntry(_rckey, font()); + config->sync(); + delete config; + } +} + +void FontUseItem::applyFontDiff( const QFont &fnt, int fontDiffFlags ) +{ + QFont _font( font() ); + + if (fontDiffFlags & KFontChooser::FontDiffSize) { + _font.setPointSizeF( fnt.pointSizeF() ); + } + if (fontDiffFlags & KFontChooser::FontDiffFamily) { + if (!isFixedOnly() || QFontInfo(fnt).fixedPitch()) _font.setFamily( fnt.family() ); + } + if (fontDiffFlags & KFontChooser::FontDiffStyle) { + _font.setWeight( fnt.weight() ); + _font.setStyle( fnt.style() ); + _font.setUnderline( fnt.underline() ); +#if QT_VERSION >= 0x040800 + _font.setStyleName( fnt.styleName() ); +#endif + } + + setFont( _font, isFixedOnly() ); +} + +/**** FontAASettings ****/ +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) +FontAASettings::FontAASettings(QWidget *parent) + : KDialog( parent ), + changesMade(false) +{ + setObjectName( "FontAASettings" ); + setModal( true ); + setCaption( i18n("Configure Anti-Alias Settings") ); + setButtons( Ok|Cancel ); + + QWidget *mw=new QWidget(this); + QFormLayout *layout=new QFormLayout(mw); + layout->setVerticalSpacing(0); + layout->setMargin(0); + + excludeRange=new QCheckBox(i18n("E&xclude range:"), mw); + QHBoxLayout *rangeLayout = new QHBoxLayout(); + excludeFrom=new KDoubleNumInput(0, 72, 8.0, mw, 1, 1); + excludeFrom->setSuffix(i18n(" pt")); + rangeLayout->addWidget(excludeFrom); + excludeToLabel=new QLabel(i18n(" to "), mw); + rangeLayout->addWidget(excludeToLabel); + excludeTo=new KDoubleNumInput(0, 72, 15.0, mw, 1, 1); + excludeTo->setSuffix(i18n(" pt")); + rangeLayout->addWidget(excludeTo); + layout->addRow(excludeRange, rangeLayout); + + QString subPixelWhatsThis = i18n("

    If you have a TFT or LCD screen you" + " can further improve the quality of displayed fonts by selecting" + " this option.
    Sub-pixel rendering is also known as ClearType(tm).
    " + " In order for sub-pixel rendering to" + " work correctly you need to know how the sub-pixels of your display" + " are aligned.

    " + "

    On TFT or LCD displays a single pixel is actually composed of" + " three sub-pixels, red, green and blue. Most displays" + " have a linear ordering of RGB sub-pixel, some have BGR.
    " + " This feature does not work with CRT monitors.

    " ); + + useSubPixel=new QCheckBox(i18n("&Use sub-pixel rendering:"), mw); + useSubPixel->setWhatsThis( subPixelWhatsThis ); + + subPixelType=new QComboBox(mw); + layout->addRow(useSubPixel, subPixelType); + + subPixelType->setEditable(false); + subPixelType->setWhatsThis( subPixelWhatsThis ); + + for(int t=KXftConfig::SubPixel::None+1; t<=KXftConfig::SubPixel::Vbgr; ++t) + subPixelType->addItem(QPixmap(aaPixmaps[t-1]), i18n(KXftConfig::description((KXftConfig::SubPixel::Type)t).toUtf8())); + + QLabel *hintingLabel=new QLabel(i18n("Hinting style: "), mw); + hintingStyle=new QComboBox(mw); + hintingStyle->setEditable(false); + layout->addRow(hintingLabel, hintingStyle); + for(int s=KXftConfig::Hint::NotSet+1; s<=KXftConfig::Hint::Full; ++s) + hintingStyle->addItem(i18n(KXftConfig::description((KXftConfig::Hint::Style)s).toUtf8())); + + QString hintingText(i18n("Hinting is a process used to enhance the quality of fonts at small sizes.")); + hintingStyle->setWhatsThis( hintingText); + hintingLabel->setWhatsThis( hintingText); + load(); + enableWidgets(); + setMainWidget(mw); + + connect(excludeRange, SIGNAL(toggled(bool)), SLOT(changed())); + connect(useSubPixel, SIGNAL(toggled(bool)), SLOT(changed())); + connect(excludeFrom, SIGNAL(valueChanged(double)), SLOT(changed())); + connect(excludeTo, SIGNAL(valueChanged(double)), SLOT(changed())); + connect(subPixelType, SIGNAL(activated(QString)), SLOT(changed())); + connect(hintingStyle, SIGNAL(activated(QString)), SLOT(changed())); +} + +bool FontAASettings::load() +{ + double from, to; + KXftConfig xft; + + if(xft.getExcludeRange(from, to)) + excludeRange->setChecked(true); + else + { + excludeRange->setChecked(false); + from=8.0; + to=15.0; + } + + excludeFrom->setValue(from); + excludeTo->setValue(to); + + KXftConfig::SubPixel::Type spType; + + if(!xft.getSubPixelType(spType) || KXftConfig::SubPixel::None==spType) + useSubPixel->setChecked(false); + else + { + int idx=getIndex(spType); + + if(idx>-1) + { + useSubPixel->setChecked(true); + subPixelType->setCurrentIndex(idx); + } + else + useSubPixel->setChecked(false); + } + + KXftConfig::Hint::Style hStyle; + + if(!xft.getHintStyle(hStyle) || KXftConfig::Hint::NotSet==hStyle) + { + KConfig kglobals("kdeglobals", KConfig::NoGlobals); + + hStyle=KXftConfig::Hint::Medium; + xft.setHintStyle(hStyle); + xft.apply(); // Save this setting + KConfigGroup(&kglobals, "General").writeEntry("XftHintStyle", KXftConfig::toStr(hStyle)); + kglobals.sync(); + runRdb(KRdbExportXftSettings | KRdbExportGtkTheme); + } + + hintingStyle->setCurrentIndex(getIndex(hStyle)); + + enableWidgets(); + + return xft.getAntiAliasing(); +} + +bool FontAASettings::save( bool useAA ) +{ + KXftConfig xft; + KConfig kglobals("kdeglobals", KConfig::NoGlobals); + KConfigGroup grp(&kglobals, "General"); + + xft.setAntiAliasing( useAA ); + + if(excludeRange->isChecked()) + xft.setExcludeRange(excludeFrom->value(), excludeTo->value()); + else + xft.setExcludeRange(0, 0); + + KXftConfig::SubPixel::Type spType(useSubPixel->isChecked() + ? getSubPixelType() + : KXftConfig::SubPixel::None); + + xft.setSubPixelType(spType); + grp.writeEntry("XftSubPixel", KXftConfig::toStr(spType)); + grp.writeEntry("XftAntialias", useAA); + + bool mod=false; + KXftConfig::Hint::Style hStyle(getHintStyle()); + + xft.setHintStyle(hStyle); + + QString hs(KXftConfig::toStr(hStyle)); + + if(!hs.isEmpty() && hs!=grp.readEntry("XftHintStyle")) + { + grp.writeEntry("XftHintStyle", hs); + mod=true; + } + kglobals.sync(); + + if(!mod) + mod=xft.changed(); + + xft.apply(); + + return mod; +} + +void FontAASettings::defaults() +{ + excludeRange->setChecked(true); + excludeFrom->setValue(8.0); + excludeTo->setValue(15.0); + useSubPixel->setChecked(false); + hintingStyle->setCurrentIndex(getIndex(KXftConfig::Hint::Medium)); + enableWidgets(); +} + +int FontAASettings::getIndex(KXftConfig::SubPixel::Type spType) +{ + int pos=-1; + int index; + + for(index=0; indexcount(); ++index) + if(subPixelType->itemText(index)==i18n(KXftConfig::description(spType).toUtf8())) + { + pos=index; + break; + } + + return pos; +} + +KXftConfig::SubPixel::Type FontAASettings::getSubPixelType() +{ + int t; + + for(t=KXftConfig::SubPixel::None; t<=KXftConfig::SubPixel::Vbgr; ++t) + if(subPixelType->currentText()==i18n(KXftConfig::description((KXftConfig::SubPixel::Type)t).toUtf8())) + return (KXftConfig::SubPixel::Type)t; + + return KXftConfig::SubPixel::None; +} + +int FontAASettings::getIndex(KXftConfig::Hint::Style hStyle) +{ + int pos=-1; + int index; + + for(index=0; indexcount(); ++index) + if(hintingStyle->itemText(index)==i18n(KXftConfig::description(hStyle).toUtf8())) + { + pos=index; + break; + } + + return pos; +} + + +KXftConfig::Hint::Style FontAASettings::getHintStyle() +{ + int s; + + for(s=KXftConfig::Hint::NotSet; s<=KXftConfig::Hint::Full; ++s) + if(hintingStyle->currentText()==i18n(KXftConfig::description((KXftConfig::Hint::Style)s).toUtf8())) + return (KXftConfig::Hint::Style)s; + + return KXftConfig::Hint::Medium; +} + +void FontAASettings::enableWidgets() +{ + excludeFrom->setEnabled(excludeRange->isChecked()); + excludeTo->setEnabled(excludeRange->isChecked()); + excludeToLabel->setEnabled(excludeRange->isChecked()); + subPixelType->setEnabled(useSubPixel->isChecked()); +#ifdef FT_LCD_FILTER_H + static int ft_has_subpixel = -1; + if( ft_has_subpixel == -1 ) { + FT_Library ftLibrary; + if(FT_Init_FreeType(&ftLibrary) == 0) { + ft_has_subpixel = ( FT_Library_SetLcdFilter(ftLibrary, FT_LCD_FILTER_DEFAULT ) + == FT_Err_Unimplemented_Feature ) ? 0 : 1; + FT_Done_FreeType(ftLibrary); + } + } + useSubPixel->setEnabled(ft_has_subpixel); + subPixelType->setEnabled(ft_has_subpixel); +#endif +} +#endif + +void FontAASettings::changed() +{ +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + changesMade=true; + enableWidgets(); +#endif +} + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) +int FontAASettings::exec() +{ + int i=KDialog::exec(); + + if(!i) + load(); // Reset settings... + + return i && changesMade; +} +#endif + +/**** KFonts ****/ + +KFonts::KFonts(QWidget *parent, const QVariantList &args) + : KCModule(parent, args) +{ + QStringList nameGroupKeyRc; + + nameGroupKeyRc + << i18nc("font usage", "General") << "General" << "font" << "" + << i18nc("font usage", "Fixed width") << "General" << "fixed" << "" + << i18nc("font usage", "Small") << "General" << "smallestReadableFont" << "" + << i18nc("font usage", "Toolbar") << "General" << "toolBarFont" << "" + << i18nc("font usage", "Menu") << "General" << "menuFont" << "" + << i18nc("font usage", "Window title") << "WM" << "activeFont" << "" + << i18nc("font usage", "Taskbar") << "General" << "taskbarFont" << "" + << i18nc("font usage", "Desktop") << "General" << "desktopFont" << ""; + + QList defaultFontList; + + // NOTE: keep in sync with kdelibs/kdeui/kernel/kglobalsettings.cpp + +#ifdef Q_WS_MAC + QFont f0("Lucida Grande", 13); // general/menu/desktop + QFont f1("Monaco", 10); + QFont f2("Lucida Grande", 11); // toolbar +#elif defined(Q_WS_MAEMO_5) || defined(MEEGO_EDITION_HARMATTAN) + QFont f0("Sans Serif", 16); // general/menu/desktop + QFont f1("Monospace", 16; + QFont f2("Sans Serif", 16); // toolbar +#else + QFont f0("Sans Serif", 9); // general/menu/desktop + QFont f1("Monospace", 9); + QFont f2("Sans Serif", 8); // toolbar +#endif + QFont f3("Sans Serif", 8); // window title + QFont f4("Sans Serif", 9); // taskbar + QFont f5("Sans Serif", 8); // smallestReadableFont + + defaultFontList << f0 << f1 << f5 << f2 << f0 << f3 << f4 << f0; + + QList fixedList; + + fixedList + << false + << true + << false + << false + << false + << false + << false + << false; + + QStringList quickHelpList; + + quickHelpList + << i18n("Used for normal text (e.g. button labels, list items).") + << i18n("A non-proportional font (i.e. typewriter font).") + << i18n("Smallest font that is still readable well.") + << i18n("Used to display text beside toolbar icons.") + << i18n("Used by menu bars and popup menus.") + << i18n("Used by the window titlebar.") + << i18n("Used by the taskbar.") + << i18n("Used for desktop icons."); + + QVBoxLayout * layout = new QVBoxLayout(this ); + layout->setMargin(0); + + QGridLayout * fontUseLayout = new QGridLayout( ); + layout->addLayout( fontUseLayout ); + fontUseLayout->setColumnStretch(0, 0); + fontUseLayout->setColumnStretch(1, 1); + fontUseLayout->setColumnStretch(2, 0); + + QList::ConstIterator defaultFontIt(defaultFontList.begin()); + QList::ConstIterator fixedListIt(fixedList.begin()); + QStringList::ConstIterator quickHelpIt(quickHelpList.begin()); + QStringList::ConstIterator it(nameGroupKeyRc.begin()); + + unsigned int count = 0; + + while (it != nameGroupKeyRc.constEnd()) { + + QString name = *it; it++; + QString group = *it; it++; + QString key = *it; it++; + QString file = *it; it++; + + FontUseItem * i = + new FontUseItem( + this, + name, + group, + key, + file, + *defaultFontIt++, + *fixedListIt++ + ); + + fontUseList.append(i); + connect(i, SIGNAL(fontSelected(QFont)), SLOT(fontSelected())); + + QLabel * fontUse = new QLabel(i18nc("Font role", "%1: ", name), this); + fontUse->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + fontUse->setWhatsThis( *quickHelpIt++); + + fontUseLayout->addWidget(fontUse, count, 0); + fontUseLayout->addWidget(i, count, 1); + + ++count; + } + + QHBoxLayout *hblay = new QHBoxLayout(); + layout->addLayout(hblay); + hblay->addStretch(); + QPushButton * fontAdjustButton = new QPushButton(i18n("Ad&just All Fonts..."), this); + fontAdjustButton->setWhatsThis( i18n("Click to change all fonts")); + hblay->addWidget( fontAdjustButton ); + connect(fontAdjustButton, SIGNAL(clicked()), SLOT(slotApplyFontDiff())); + + layout->addSpacing(KDialog::spacingHint()); + + QGridLayout* lay = new QGridLayout(); + layout->addLayout(lay); + lay->setColumnStretch( 3, 10 ); +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + QLabel* label=0L; + label = new QLabel( i18n( "Use a&nti-aliasing:" ), this ); + label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + lay->addWidget( label, 0, 0 ); + cbAA = new QComboBox( this ); + cbAA->insertItem( AAEnabled, i18nc( "Use anti-aliasing", "Enabled" )); // change AASetting type if order changes + cbAA->insertItem( AASystem, i18nc( "Use anti-aliasing", "System Settings" )); + cbAA->insertItem( AADisabled, i18nc( "Use anti-aliasing", "Disabled" )); + cbAA->setWhatsThis( i18n("If this option is selected, KDE will smooth the edges of curves in " + "fonts.")); + aaSettingsButton = new QPushButton( i18n( "Configure..." ), this); + connect(aaSettingsButton, SIGNAL(clicked()), SLOT(slotCfgAa())); + label->setBuddy( cbAA ); + lay->addWidget( cbAA, 0, 1 ); + lay->addWidget( aaSettingsButton, 0, 2 ); + connect(cbAA, SIGNAL(activated(int)), SLOT(slotUseAntiAliasing())); +#endif +#if HAVE_X11 + checkboxForceDpi = new QCheckBox( i18n( "Force fonts DPI:" ), this ); + lay->addWidget( checkboxForceDpi, 1, 0 ); + spinboxDpi = new QSpinBox( this ); + spinboxDpi->setRange(1, 1000); + spinboxDpi->setSingleStep(24); // The common DPI values 72, 96 and 120 are multiples of 24 + QString whatsthis = i18n( + "

    This option forces a specific DPI value for fonts. It may be useful" + " when the real DPI of the hardware is not detected properly and it" + " is also often misused when poor quality fonts are used that do not" + " look well with DPI values other than 96 or 120 DPI.

    " + "

    The use of this option is generally discouraged. For selecting proper DPI" + " value a better option is explicitly configuring it for the whole X server if" + " possible (e.g. DisplaySize in xorg.conf or adding -dpi value to" + " ServerLocalArgs= in $KDEDIR/share/config/kdm/kdmrc). When fonts do not render" + " properly with real DPI value better fonts should be used or configuration" + " of font hinting should be checked.

    " ); + spinboxDpi->setWhatsThis(whatsthis); + checkboxForceDpi->setChecked(false); + spinboxDpi->setEnabled(false); + connect( spinboxDpi, SIGNAL( valueChanged(int)), SLOT( changed())); + connect( checkboxForceDpi, SIGNAL( toggled(bool)), SLOT( changed())); + connect( checkboxForceDpi, SIGNAL( toggled(bool)), spinboxDpi, SLOT( setEnabled(bool))); + lay->addWidget( spinboxDpi, 1, 1 ); +#endif + layout->addStretch(1); + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + aaSettings=new FontAASettings(this); +#endif + +} + +KFonts::~KFonts() +{ + QList::Iterator it(fontUseList.begin()), + end(fontUseList.end()); + + for(; it!=end; ++it) + delete (*it); + fontUseList.clear(); +} + +void KFonts::fontSelected() +{ + emit changed(true); +} + +void KFonts::defaults() +{ + for ( int i = 0; i < (int) fontUseList.count(); i++ ) + fontUseList.at( i )->setDefault(); + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + useAA = AASystem; + cbAA->setCurrentIndex( useAA ); + aaSettings->defaults(); +#endif +#if HAVE_X11 + checkboxForceDpi->setChecked( false ); + spinboxDpi->setValue( 96 ); +#endif + emit changed(true); +} + +void KFonts::load() +{ + QList::Iterator it(fontUseList.begin()), + end(fontUseList.end()); + + for(; it!=end; ++it) + (*it)->readFont(); + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + useAA_original = useAA = aaSettings->load() ? AAEnabled : AADisabled; + cbAA->setCurrentIndex( useAA ); +#endif + + KConfig _cfgfonts( "kcmfonts" ); + KConfigGroup cfgfonts(&_cfgfonts, "General"); +#if HAVE_X11 + int dpicfg = cfgfonts.readEntry( "forceFontDPI", 0 ); + if (dpicfg <= 0) { + checkboxForceDpi->setChecked(false); + spinboxDpi->setValue(96); + dpi_original = 0; + } else { + checkboxForceDpi->setChecked(true); + spinboxDpi->setValue(dpicfg); + dpi_original = dpicfg; + }; +#endif +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + if( cfgfonts.readEntry( "dontChangeAASettings", true )) { + useAA_original = useAA = AASystem; + cbAA->setCurrentIndex( useAA ); + } + aaSettingsButton->setEnabled( cbAA->currentIndex() == AAEnabled ); +#endif + + emit changed(false); +} + +void KFonts::save() +{ + QList::Iterator it(fontUseList.begin()), + end(fontUseList.end()); + + for(; it!=end; ++it) + (*it)->writeFont(); + + KSharedConfig::openConfig()->sync(); + + KConfig _cfgfonts( "kcmfonts" ); + KConfigGroup cfgfonts(&_cfgfonts, "General"); +#if HAVE_X11 + int dpi = ( checkboxForceDpi->isChecked() ? spinboxDpi->value() : 0 ); + cfgfonts.writeEntry( "forceFontDPI", dpi ); +#endif +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + cfgfonts.writeEntry( "dontChangeAASettings", cbAA->currentIndex() == AASystem ); +#endif + cfgfonts.sync(); +#if HAVE_X11 + // if the setting is reset in the module, remove the dpi value, + // otherwise don't explicitly remove it and leave any possible system-wide value + if( dpi == 0 && dpi_original != 0 ) { + KProcess proc; + proc << "xrdb" << "-quiet" << "-remove" << "-nocpp"; + proc.start(); + if (proc.waitForStarted()) { + proc.write( QByteArray( "Xft.dpi\n" ) ); + proc.closeWriteChannel(); + proc.waitForFinished(); + } + } +#endif + + KGlobalSettings::self()->emitChange(KGlobalSettings::FontChanged); + + kapp->processEvents(); // Process font change ourselves + + + // Don't overwrite global settings unless explicitly asked for - e.g. the system + // fontconfig setup may be much more complex than this module can provide. + // TODO: With AASystem the changes already made by this module should be reverted somehow. +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + bool aaSave = false; + if( cbAA->currentIndex() != AASystem ) + aaSave = aaSettings->save( useAA == AAEnabled ); + + if( aaSave || (useAA != useAA_original) || dpi != dpi_original) { + KMessageBox::information(this, + i18n( + "

    Some changes such as anti-aliasing or DPI will only affect newly started applications.

    " + ), i18n("Font Settings Changed"), "FontSettingsChanged"); + useAA_original = useAA; + dpi_original = dpi; + } +#else +#if HAVE_X11 + if(dpi != dpi_original) { + KMessageBox::information(this, + i18n( + "

    Some changes such as DPI will only affect newly started applications.

    " + ), i18n("Font Settings Changed"), "FontSettingsChanged"); + dpi_original = dpi; + } +#endif +#endif + runRdb(KRdbExportXftSettings | KRdbExportGtkTheme); + + emit changed(false); +} + + +void KFonts::slotApplyFontDiff() +{ + QFont font = QFont(fontUseList.first()->font()); + KFontChooser::FontDiffFlags fontDiffFlags = 0; + int ret = KFontDialog::getFontDiff(font,fontDiffFlags,KFontChooser::NoDisplayFlags,this); + + if (ret == KDialog::Accepted && fontDiffFlags) + { + for ( int i = 0; i < (int) fontUseList.count(); i++ ) + fontUseList.at( i )->applyFontDiff( font,fontDiffFlags ); + emit changed(true); + } +} + +void KFonts::slotUseAntiAliasing() +{ +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + useAA = static_cast< AASetting >( cbAA->currentIndex()); + aaSettingsButton->setEnabled( cbAA->currentIndex() == AAEnabled ); + emit changed(true); +#endif +} + +void KFonts::slotCfgAa() +{ +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + if(aaSettings->exec()) + { + emit changed(true); + } +#endif +} + +#include "fonts.moc" + +// vim:ts=2:sw=2:tw=78 diff --git a/kcms/fonts/fonts.desktop b/kcms/fonts/fonts.desktop new file mode 100644 index 0000000000..2a5a7a7111 --- /dev/null +++ b/kcms/fonts/fonts.desktop @@ -0,0 +1,241 @@ +[Desktop Entry] +Exec=kcmshell5 fonts +Icon=preferences-desktop-font +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/fonts/index.html + +X-KDE-Library=kcm_fonts +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=application-appearance +X-KDE-Weight=80 + +Name=Fonts +Name[af]=Skriftipes +Name[ar]=الخطوط +Name[ast]=Tribes de lletra +Name[be]=Шрыфты +Name[be@latin]=Šryfty +Name[bg]=Шрифтове +Name[bn]=ফন্ট +Name[bn_IN]=ফন্ট +Name[br]=Fontoù +Name[bs]=Fontovi +Name[ca]=Tipus de lletra +Name[ca@valencia]=Tipus de lletra +Name[cs]=Písma +Name[csb]=Fòntë +Name[cy]=Ffontiau +Name[da]=Skrifttyper +Name[de]=Schriftarten +Name[el]=Γραμματοσειρές +Name[en_GB]=Fonts +Name[eo]=Tiparoj +Name[es]=Tipos de letra +Name[et]=Fondid +Name[eu]=Letra-tipoak +Name[fa]=قلمها +Name[fi]=Kirjasimet +Name[fr]=Polices de caractères +Name[fy]=Lettertypen +Name[ga]=Clónna +Name[gl]=Tipos de letra +Name[gu]=ફોન્ટ્સ +Name[he]=גופנים +Name[hi]=फ़ॉन्ट्स +Name[hne]=फोंट +Name[hr]=Fontovi +Name[hsb]=Pisma +Name[hu]=Betűtípusok +Name[ia]=Fontes +Name[id]=Fonta +Name[is]=Letur +Name[it]=Caratteri +Name[ja]=フォント +Name[ka]=ფონტები +Name[kk]=Қаріптер +Name[km]=ពុម្ព​អក្សរ +Name[kn]=ಅಕ್ಷರಶೈಲಿಗಳು +Name[ko]=글꼴 +Name[ku]=Curenivîs +Name[lt]=Šriftai +Name[lv]=Fonti +Name[mai]=फान्ट +Name[mk]=Фонтови +Name[ml]=അക്ഷരസഞ്ചയങ്ങള്‍ +Name[mr]=फॉन्ट +Name[ms]=Fon +Name[nb]=Skrifttype +Name[nds]=Schriftoorden +Name[ne]=फन्ट +Name[nl]=Lettertypen +Name[nn]=Skrifter +Name[oc]=Poliças +Name[or]=ଅକ୍ଷର ରୂପ +Name[pa]=ਫੋਂਟ +Name[pl]=Czcionki +Name[pt]=Tipos de Letra +Name[pt_BR]=Fontes +Name[ro]=Fonturi +Name[ru]=Шрифты +Name[se]=Fonttat +Name[si]=අකුරු +Name[sk]=Písma +Name[sl]=Pisave +Name[sr]=Фонтови +Name[sr@ijekavian]=Фонтови +Name[sr@ijekavianlatin]=Fontovi +Name[sr@latin]=Fontovi +Name[sv]=Teckensnitt +Name[ta]=எழுத்துருக்கள் +Name[te]=ఫాంట్‍స్ +Name[tg]=Ҳарфҳо +Name[th]=แบบอักษรต่างๆ +Name[tr]=Yazı Tipleri +Name[ug]=خەت نۇسخىلىرى +Name[uk]=Шрифти +Name[uz]=Shriftlar +Name[uz@cyrillic]=Шрифтлар +Name[vi]=Phông chữ +Name[wa]=Fontes +Name[xh]=Uhlobo lwamagama +Name[x-test]=xxFontsxx +Name[zh_CN]=字体 +Name[zh_TW]=字型 + +Comment=Font settings +Comment[af]=Skrif tipe instellings +Comment[ar]=إعدادات الخطوط +Comment[ast]=Preferencies de les tribes de lletra +Comment[be]=Настаўленні шрыфтоў +Comment[be@latin]=Nałady šryftoŭ +Comment[bg]=Настройки на системните шрифтове +Comment[bn]=ফন্ট সেটিংস +Comment[bn_IN]=ফন্ট সংক্রান্ত বৈশিষ্ট্য +Comment[br]=Kefluniañ ar fontoù +Comment[bs]=Postavke fonta +Comment[ca]=Arranjament del tipus de lletra +Comment[ca@valencia]=Arranjament del tipus de lletra +Comment[cs]=Nastavení písem +Comment[csb]=Nastôw fòntów +Comment[cy]=Gosodiadau Ffontiau +Comment[da]=Skrifttypeindstillinger +Comment[de]=Schriftarten-Einstellungen +Comment[el]=Ρυθμίσεις γραμματοσειρών +Comment[en_GB]=Font settings +Comment[eo]=Tiparagordo +Comment[es]=Preferencias de los tipos de letra +Comment[et]=Fontide seadistused +Comment[eu]=Letra-tipoen ezarpenak +Comment[fa]=تنظیمات قلم +Comment[fi]=Kirjasinten asetukset +Comment[fr]=Configuration des polices de caractères +Comment[fy]=Lettertypen ynstellings +Comment[ga]=Socruithe na gclónna +Comment[gl]=Configuración dos tipos de letra +Comment[gu]=ફોન્ટ ગોઠવણીઓ +Comment[he]=הגדרות גופנים +Comment[hi]=फ़ॉन्ट्स विन्यास +Comment[hne]=फोंट सेटिंग +Comment[hr]=Postavke fontova +Comment[hsb]=Nastajenja za pisma +Comment[hu]=Betűtípus-beállítások +Comment[ia]=Preferentias de font +Comment[id]=Pengaturan fonta +Comment[is]=Stillingar leturs +Comment[it]=Impostazioni dei caratteri +Comment[ja]=フォントの設定 +Comment[ka]=ფონტების კონფიგურაცია +Comment[kk]=Қаріптерін орнату +Comment[km]=ការ​កំណត់​ពុម្ព​អក្សរ +Comment[kn]=ಅಕ್ಷರಶೈಲಿ ಸಂಯೋಜನೆಗಳು +Comment[ko]=글꼴 설정 +Comment[ku]=Mîhengên curenivîsan +Comment[lt]=Šriftų nustatymai +Comment[lv]=Fontu parametri +Comment[mai]=फान्ट्स बिन्यास +Comment[mk]=Поставувања на фонтовите +Comment[ml]=അക്ഷരസഞ്ചയത്തിന്റെ സജ്ജീകരണങ്ങള്‍ +Comment[mr]=फॉन्ट संयोजना +Comment[ms]=Seting Fon +Comment[nb]=Skriftinnstillinger +Comment[nds]=Schriftoorden instellen +Comment[ne]=फन्ट सेटिङ +Comment[nl]=Lettertypeninstellingen +Comment[nn]=Skriftinnstillingar +Comment[oc]=Paramètres de poliça +Comment[or]=ଅକ୍ଷର ରୂପ ସଂଚଚନା +Comment[pa]=ਫੋਂਟ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia czcionek +Comment[pt]=Configuração dos tipos de letra +Comment[pt_BR]=Configurações das fontes +Comment[ro]=Configurări font +Comment[ru]=Настройка шрифтов +Comment[se]=Fontaheivehusat +Comment[si]=අකුරු සැකසුම් +Comment[sk]=Nastavenie písiem +Comment[sl]=Nastavitve pisav +Comment[sr]=Поставке фонта +Comment[sr@ijekavian]=Поставке фонта +Comment[sr@ijekavianlatin]=Postavke fonta +Comment[sr@latin]=Postavke fonta +Comment[sv]=Anpassa teckensnitt +Comment[ta]=எழுத்துரு அமைப்புகள் +Comment[te]=ఫాంట్ అమరికలు +Comment[tg]=Танзимоти ҳарфҳо +Comment[th]=ตั้งค่าแบบอักษรต่างๆ +Comment[tr]=Yazı tipi ayarları +Comment[ug]=خەت نۇسخا تەڭشەك +Comment[uk]=Налаштування шрифтів +Comment[uz]=Shriftlarni moslash +Comment[uz@cyrillic]=Шрифтларни мослаш +Comment[vi]=Thiết lập phông chữ +Comment[wa]=Apontiaedje des fontes +Comment[xh]=Izicwangciso zohlobo lwamagama +Comment[x-test]=xxFont settingsxx +Comment[zh_CN]=字体设置 +Comment[zh_TW]=字型設定 + +X-KDE-Keywords=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts +X-KDE-Keywords[bs]=slova,veličina slova,stil,znakovi,postaviti znakove,ploča,kontrolna ploča,pozadina,upravitelj datoteka,alatne trake,meni,naslov prozora,naslov,DPI,niskopropusni,slova pozadine,slova na alatnoj traci,obilježja,opći fontovi +X-KDE-Keywords[ca]=tipus de lletres,mida de tipus de lletra,estils,joc de caràcters,jocs de caràcters,plafó,plafó de control,escriptoris,Gestor de fitxers,Barres d'eines,Menú,Títol de finestra,Títol,DPI,antialiàsing,tipus de lletra d'escriptori,tipus de lletra de barra d'eines,caràcter,tipus de lletra general +X-KDE-Keywords[ca@valencia]=tipus de lletres,mida de tipus de lletra,estils,joc de caràcters,jocs de caràcters,plafó,plafó de control,escriptoris,Gestor de fitxers,Barres d'eines,Menú,Títol de finestra,Títol,DPI,antialiàsing,tipus de lletra d'escriptori,tipus de lletra de barra d'eines,caràcter,tipus de lletra general +X-KDE-Keywords[da]=skrifttyper,skriftstørrelse,stile,tegnsæt,panel,kontrolpanel,skriveborde,filhåndtering,værktøjslinjer,menu,vinduestitel,titel,DPI,anti-aliasing,værktøjslinjer,tegn +X-KDE-Keywords[de]=Schriftarten,Schriftgrößen,Stile,Zeichensätze,Kontrollleiste,Stile,Dateiverwaltung,Arbeitsflächen,Werkzeugleisten,Menüs,Fenstertitel,Titel,DPI,Antialiasing,Arbeitsflächeschriften,Werkzeugleistenschriften,Zeichen,Allgemeine Schriftarten +X-KDE-Keywords[el]=γραμματοσειρές,μέγεθος γραμματοσειράς,στιλ,σύνολα χαρακτήρων,πίνακας,πίνακας ελέγχου,επιφάνειες εργασίας,διαχειριστής αρχείων,γραμμές εργαλείων,μενού,τίτλος παραθύρου,τίτλος,DPI,εξομάλυνση,γραμματοσειρές επιφάνειας εργασίας,γραμματοσειρές γραμμής εργαλείων,χαρακτήρας,γενικές γραμματοσειρές +X-KDE-Keywords[es]=tipos de letra,tamaño del tipo de letra,estilos,juegos de caracteres,panel,panel de control panel,escritorios,Gestor de archivos,Barras de herramientas,Menú,Título de la ventana,Título,DPI,suavizado de texto,tipos de letra del escritorio,tipos de letra de las barras de herramientas,carácter,tipos de letra generales +X-KDE-Keywords[et]=fondid,fondi suurus,kodeering,kooditabel,paneel,juhtpaneel,töölauad,failihaldur,tööriistaribad,menüü,aken,tiitel,nimetus,nimi,DPI,antialias,töölaua fondid,tööriistariba fondid,märk,märgid,sümbolid,üldised fondid +X-KDE-Keywords[eu]=letra-tipoak,letra-tamaina,estiloak,karaktere-jokoak,panela,kontrol-panela,mahaigain,fitxategi-kudeatzaile,tresna-barra,menu,leihoaren titulu,titulu,DPI,antialiasing,mahaigaineko letra-tripo,tresna-barrako letra-tipo,karaktere,letra-tipo orokor +X-KDE-Keywords[fi]=kirjasimet,fontit,kirjasinkoko,fonttikoko,tyylit,merkistöt,paneeli,ohjauskeskus,työpöydät,Tiedostonhallinta,Työkalurivit,Työkalupalkit,Valikko,Ikkunan otsikko,Otsikko,DPI,antialiasointi,työpöytäkirjasimet,työpöytäfontit,työkalurivikirjasimet, työkalurivien kirjasimet, työkalurivifontit, työkalurivien fontit,merkki,yleiskirjasimet,yleiset kirjasimet,yleisfontit,yleiset fontit +X-KDE-Keywords[fr]=polices, taille de police, styles, tables de caractères, jeux de caractères, tableau de bord, bureaux, Gestionnaire de Fichiers, Barre d'outils, Menu, Titre de fenêtre, Titre, DPI, anti crénelage, polices du bureau, police de barre d'outils, caractère, polices générales +X-KDE-Keywords[ga]=clónna,clófhoirne,clómhéid,stíleanna,tacair charachtar,painéal,painéal rialaithe,deasca,Bainisteoir Comhad,Barraí Uirlisí,Roghchlár,Teideal Fuinneoige,Teideal,PSO,frithailiasáil,clónna deisce,clófhoirne deisce,clónna barra uirlisí,carachtar,clónna ginearálta,clófhoirne ginearálta +X-KDE-Keywords[gl]=tipo de letra, letra, tamaño da letra, carácter, caracteres, conxunto de caracteres, glifo, panel, control, panel de control, antialiasing, suavizado +X-KDE-Keywords[hu]=betűkészletek,betűméret,stílusok,karakterkészletek,karakterkészletek,panel,beállítópanel,asztalok,Fájlkezelő,Eszköztárak,Menü,Ablakcím,Cím,DPI,élsimítás,asztal betűkészletek,eszköztár betűkészletek,karakter,általános betűkészletek +X-KDE-Keywords[ia]=fonts,grandor de font,stilos,insimules de characteres,insimules de characteres,pannello,pannello de controlo,scriptorios,Gerente de File,Barra de instrumentos,menu,Titulo de Fenestra,Titulo,DPI,anti-aliasing,fonts de scriptorio, fonts de barra de titulo, character,fonts general +X-KDE-Keywords[it]=caratteri,dimensione dei caratteri,stili,codifiche,insiemi di caratteri,pannello,pannello di controllo,desktop,gestore dei file,barre degli strumenti,menu,titolo della finestra,titolo,DPI,anti-aliasing,caratteri del desktop,caratteri della barra degli strumenti,carattere,caratteri generali +X-KDE-Keywords[kk]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts +X-KDE-Keywords[km]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts +X-KDE-Keywords[ko]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,글꼴,스타일,창 제목,글자 +X-KDE-Keywords[mr]=फॉन्ट्स, फॉन्ट साईज, कॅरेक्टर, कॅरेक्टर सेटस, पँनल, कन्ट्रोल पँनल, डेस्कटॉप्स, फाईल मेनेजर, टूलबार्स, मेन्यू, चौकट टाईटल, टाईटल, डी पी आय , अन्टी अलायझिंग, डेस्कटॉप फॉन्टस, टूलबार फॉन्टस, कॅरेक्टर, जनरल फॉन्ट्स +X-KDE-Keywords[nb]=skrifter,skriftstørrelse,stiler,tegnsett,panel,kontrollpanel,skrivebord,filbehandler,Verktøylinje,Meny,Vindiustittel,tittel,DPI,kantutjevning,skrivebordsskrifter,verktøylinjeskrifter,generelle skrifter +X-KDE-Keywords[nds]=Schriftoorden,Schrift,Stilen,Tekensetten,Paneel,Stüerpaneel,Schriefdisch,Dateipleger,Warktüüchbalkens,Menü,Finstertitel,Titel,DPI,Kantstreken,anti-aliasing,Schriefdisch,Teken +X-KDE-Keywords[nl]=lettertypes,lettertype,tekengrootte,stijlen,tekensets,paneel,besturingspaneel,bureaubladen,bestandsbeheerder,werkbalken,menu,venstertitel,titel,DPI,anti-aliasing,lettertypen van bureaublad,lettertypen van werkbalk,teken,algemene lettertypes +X-KDE-Keywords[pl]=czcionki,rozmiar czcionki,style,zestaw znaków,panel,panel sterowania,pulpity,Menadżer plików,Paski narzędzi,Menu,Tytuł okna,Tytuł,DPI,wygładzanie,czcionki pulpitu,czcionki pasków narzędzi,znak,czcionki ogólne +X-KDE-Keywords[pt]=tipos de letra,tamanho de letra,estilos,codificações,codificações de caracteres,painel,painel de controlo,ecrãs,gestor de ficheiros,barras de ferramentas,menu,título da janela,título,PPP,suavização,tipos de letra do ecrã,tipos de letra da barra de ferramentas,carácter,tipos de letra gerais +X-KDE-Keywords[pt_BR]=fontes,tamanho da fonte,estilos,codificações,codificações de caracteres,painel,painel de controle,áreas de trabalho,gerenciador de arquivos,barras de ferramentas,Menu,Título da janela,Título,PPP,anti-aliasing,fontes da área de trabalho,fontes da barra de ferramentas,caractere,fontes gerais +X-KDE-Keywords[ru]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,шрифты,размер шрифтов,стили,кодировки,панель,панель управления,рабочие столы,диспетчер файлов,панель инструментов,меню,заголовок окна,заголовок,сглаживание,шрифты рабочего стола,шрифты панели инструментов,символы,общие шрифты +X-KDE-Keywords[sk]=písmo,veľkosť písma,štýly,znakové sady,sady znakov,panel,ovládací panel,plochy,Správca súborov,Panely nástrojov,Ponuka,Titulok okna,Titulok,DPI,anti-aliasing,písma plochy,písma nástrojových panelov,znak,všeobecné písma +X-KDE-Keywords[sl]=pisave,velikost pisav,slogi,nabori znakov,znakovni nabori,pult,nadzorna plošča,namizja,upravljalnik datotek,datotečni upravljalnik,orodjarne,orodne vrstice,meni,naslov okna,naslovna vrstica,naslov,ločljivost,točk na palec,glajenje robov,namizne pisave,pisave namizja,pisave orodjarne, pisave orodne vrstice,znak,splošne pisave +X-KDE-Keywords[sr]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,фонт,величина фонта,стил,кодирање,панел,контролни панел,површ,менаџер фајлова,траке алатки,мени,наслов прозора,тпи,омекшавање,фонт површи,фонт траке алатки,знакови,општи фонт +X-KDE-Keywords[sr@ijekavian]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,фонт,величина фонта,стил,кодирање,панел,контролни панел,површ,менаџер фајлова,траке алатки,мени,наслов прозора,тпи,омекшавање,фонт површи,фонт траке алатки,знакови,општи фонт +X-KDE-Keywords[sr@ijekavianlatin]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,font,veličina fonta,stil,kodiranje,panel,kontrolni panel,površ,menadžer fajlova,trake alatki,meni,naslov prozora,tpi,omekšavanje,font površi,font trake alatki,znakovi,opšti font +X-KDE-Keywords[sr@latin]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,font,veličina fonta,stil,kodiranje,panel,kontrolni panel,površ,menadžer fajlova,trake alatki,meni,naslov prozora,tpi,omekšavanje,font površi,font trake alatki,znakovi,opšti font +X-KDE-Keywords[sv]=teckensnitt,teckenstorlek,stil,teckenuppsättningar,panel,kontrollpanel,skrivbord,Filhanterare,Verktygsrader,Meny,Fönsternamn,Namn,Punkter/tum,kantutjämning,skrivbordsteckensnitt,verktygsradsteckensnitt,tecken,allmänna teckensnitt +X-KDE-Keywords[tr]=yazı tipi,yazı tipi boyutu,karakterler,karakter setleri,panel,denetim paneli,masaüstleri,Dosya Yöneticisi,Araç Çubukları,Menü,Pencere Başlığı,Başlık,DPI,yumuşatma,anti-aliasing,masaüstü yazı tipleri,araç çubuğu yazı tipleri,karakter,genel yazı tipleri +X-KDE-Keywords[uk]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,шрифт,шрифти,розмір,розмір шрифту,стиль,стилі,гарнітура,гарнітури,кодування,набір,символ,символи,набір символів,панель,панель керування,стільниця,стільниці,файл,керування,керування файлами,менеджер,панель інструментів,меню,заголовок,заголовок вікна,роздільність,згладжування,шрифти стільниці,шрифти панелі,символ,загальні шрифти +X-KDE-Keywords[x-test]=xxfonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fontsxx +X-KDE-Keywords[zh_CN]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts,字体字体大小,样式,字符集,面板,控制面板,桌面,文件管理器,工具栏,菜单,窗口标题,标题,反锯齿,桌面字体,工具栏字体,字符,常规字体 +X-KDE-Keywords[zh_TW]=fonts,font size,styles,charsets,character sets,panel,control panel,desktops,FileManager,Toolbars,Menu,Window Title,Title,DPI,anti-aliasing,desktop fonts,toolbar fonts,character,general fonts + +Categories=Qt;KDE;X-KDE-settings-looknfeel; diff --git a/kcms/fonts/fonts.h b/kcms/fonts/fonts.h new file mode 100644 index 0000000000..241fbfb192 --- /dev/null +++ b/kcms/fonts/fonts.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// +// kdisplay, fonts tab +// +// Copyright (c) Mark Donohoe 1997 +// Lars Knoll 1999 + +#ifndef FONTS_H +#define FONTS_H + +#include +#include + +#include +#include +#include + +#include "kxftconfig.h" + +class QCheckBox; +class QComboBox; +class QSpinBox; +class KDoubleNumInput; +class FontAASettings; + +class FontUseItem : public KFontRequester +{ + Q_OBJECT + +public: + FontUseItem(QWidget * parent, const QString &name, const QString &grp, + const QString &key, const QString &rc, const QFont &default_fnt, + bool fixed = false); + + void readFont(); + void writeFont(); + void setDefault(); + void applyFontDiff(const QFont &fnt, int fontDiffFlags); + + const QString& rcFile() { return _rcfile; } + const QString& rcGroup() { return _rcgroup; } + const QString& rcKey() { return _rckey; } + +private: + QString _rcfile; + QString _rcgroup; + QString _rckey; + QFont _default; +}; + +class FontAASettings : public KDialog +{ + Q_OBJECT + +public: + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + FontAASettings(QWidget *parent); + + bool save( bool useAA ); + bool load(); + void defaults(); + int getIndex(KXftConfig::SubPixel::Type spType); + KXftConfig::SubPixel::Type getSubPixelType(); + int getIndex(KXftConfig::Hint::Style hStyle); + KXftConfig::Hint::Style getHintStyle(); + void enableWidgets(); + int exec(); +#endif + +protected Q_SLOTS: + + void changed(); + +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) +private: + + QCheckBox *excludeRange; + QCheckBox *useSubPixel; + KDoubleNumInput *excludeFrom; + KDoubleNumInput *excludeTo; + QComboBox *subPixelType; + QComboBox *hintingStyle; + QLabel *excludeToLabel; + bool changesMade; +#endif +}; + +/** + * The Desktop/fonts tab in kcontrol. + */ +class KFonts : public KCModule +{ + Q_OBJECT + +public: + KFonts(QWidget *parent, const QVariantList &); + ~KFonts(); + + virtual void load(); + virtual void save(); + virtual void defaults(); + +protected Q_SLOTS: + void fontSelected(); + void slotApplyFontDiff(); + void slotUseAntiAliasing(); + void slotCfgAa(); + +private: +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + enum AASetting { AAEnabled, AASystem, AADisabled }; + AASetting useAA, useAA_original; + QComboBox *cbAA; + QPushButton *aaSettingsButton; + FontAASettings *aaSettings; +#endif +#if HAVE_X11 + int dpi_original; + QCheckBox *checkboxForceDpi; + QSpinBox* spinboxDpi; +#endif + QList fontUseList; +}; + +#endif + diff --git a/kcms/fonts/kxftconfig.cpp b/kcms/fonts/kxftconfig.cpp new file mode 100644 index 0000000000..6fc02336e8 --- /dev/null +++ b/kcms/fonts/kxftconfig.cpp @@ -0,0 +1,847 @@ +/* + Copyright (c) 2002 Craig Drummond + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kxftconfig.h" +#ifdef HAVE_FONTCONFIG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace std; + +static int point2Pixel(double point) +{ + return (int)(((point*QX11Info::appDpiY())/72.0)+0.5); +} + +static int pixel2Point(double pixel) +{ + return (int)(((pixel*72.0)/(double)QX11Info::appDpiY())+0.5); +} + +static bool equal(double d1, double d2) +{ + return (fabs(d1 - d2) < 0.0001); +} + +static QString dirSyntax(const QString &d) +{ + if(!d.isNull()) + { + QString ds(d); + + ds.replace("//", "/"); + + int slashPos=ds.lastIndexOf('/'); + + if(slashPos!=(((int)ds.length())-1)) + ds.append('/'); + + return ds; + } + + return d; +} + +static bool check(const QString &path, unsigned int fmt, bool checkW=false) +{ + KDE_struct_stat info; + QByteArray pathC(QFile::encodeName(path)); + + return 0==KDE_lstat(pathC, &info) && (info.st_mode&S_IFMT)==fmt && + (!checkW || 0==::access(pathC, W_OK)); +} + +inline bool fExists(const QString &p) +{ + return check(p, S_IFREG, false); +} + +inline bool dWritable(const QString &p) +{ + return check(p, S_IFDIR, true); +} + +static QString getDir(const QString &f) +{ + QString d(f); + + int slashPos=d.lastIndexOf('/'); + + if(-1!=slashPos) + d.remove(slashPos+1, d.length()); + + return dirSyntax(d); +} + +static time_t getTimeStamp(const QString &item) +{ + KDE_struct_stat info; + + return !item.isNull() && 0==KDE_lstat(QFile::encodeName(item), &info) ? info.st_mtime : 0; +} + +// +// Obtain location of config file to use. +QString getConfigFile() +{ + FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent()); + QStringList files; + FcChar8 *file; + QString home(dirSyntax(QDir::homePath())); + + while((file=FcStrListNext(list))) + { + QString f((const char *)file); + + if(fExists(f) && 0==f.indexOf(home)) + files.append(f); + } + FcStrListDone(list); + + // + // Go through list of files, looking for the preferred one... + if(files.count()) + { + QStringList::const_iterator it(files.begin()), + end(files.end()); + + for(; it!=end; ++it) + if(-1!=(*it).indexOf(QRegExp("/\\.?fonts\\.conf$"))) + return *it; + return files.front(); // Just return the 1st one... + } + else // Hmmm... no known files? + { + if(FcGetVersion() >= 21000) + { + QString targetPath(KGlobal::dirs()->localxdgconfdir()+"fontconfig"); + QDir target(targetPath); + if(!target.exists()) + target.mkpath(targetPath); + return targetPath+"/fonts.conf"; + } + else + return home+"/.fonts.conf"; + } +} + +static QString getEntry(QDomElement element, const char *type, unsigned int numAttributes, ...) +{ + if(numAttributes==element.attributes().length()) + { + va_list args; + unsigned int arg; + bool ok=true; + + va_start(args, numAttributes); + + for(arg=0; arg"; + static const char qtDocTypeLine[] = ""; + static const char docTypeLine[] = ""; + + QString str(m_doc.toString()); + int idx; + + if(0!=str.indexOf("=0 || to>=0) && foundFalse) + { + m_excludeRange.from=from < to ? from : to; + m_excludeRange.to =from < to ? to : from; + m_excludeRange.node=n; + } + else if((pixelFrom>=0 || pixelTo>=0) && foundFalse) + { + m_excludePixelRange.from=pixelFrom < pixelTo ? pixelFrom : pixelTo; + m_excludePixelRange.to =pixelFrom < pixelTo ? pixelTo : pixelFrom; + m_excludePixelRange.node=n; + } + } + break; + default: + break; + } + } + } + n=n.nextSibling(); + } +} + +void KXftConfig::applySubPixelType() +{ + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("const"), + editNode = m_doc.createElement("edit"); + QDomText typeText = m_doc.createTextNode(toStr(m_subPixel.type)); + + matchNode.setAttribute("target", "font"); + editNode.setAttribute("mode", "assign"); + editNode.setAttribute("name", "rgba"); + editNode.appendChild(typeNode); + typeNode.appendChild(typeText); + matchNode.appendChild(editNode); + if(m_subPixel.node.isNull()) + m_doc.documentElement().appendChild(matchNode); + else + m_doc.documentElement().replaceChild(matchNode, m_subPixel.node); + m_subPixel.node=matchNode; +} + +void KXftConfig::applyHintStyle() +{ + applyHinting(); + + if(Hint::NotSet==m_hint.style || m_hint.toBeRemoved) + { + if(!m_hint.node.isNull()) + { + m_doc.documentElement().removeChild(m_hint.node); + m_hint.node.clear(); + } + } + else + { + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("const"), + editNode = m_doc.createElement("edit"); + QDomText typeText = m_doc.createTextNode(toStr(m_hint.style)); + + matchNode.setAttribute("target", "font"); + editNode.setAttribute("mode", "assign"); + editNode.setAttribute("name", "hintstyle"); + editNode.appendChild(typeNode); + typeNode.appendChild(typeText); + matchNode.appendChild(editNode); + if(m_hint.node.isNull()) + m_doc.documentElement().appendChild(matchNode); + else + m_doc.documentElement().replaceChild(matchNode, m_hint.node); + m_hint.node=matchNode; + } +} + +void KXftConfig::applyHinting() +{ + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("bool"), + editNode = m_doc.createElement("edit"); + QDomText typeText = m_doc.createTextNode(m_hinting.set ? "true" : "false"); + + matchNode.setAttribute("target", "font"); + editNode.setAttribute("mode", "assign"); + editNode.setAttribute("name", "hinting"); + editNode.appendChild(typeNode); + typeNode.appendChild(typeText); + matchNode.appendChild(editNode); + if(m_hinting.node.isNull()) + m_doc.documentElement().appendChild(matchNode); + else + m_doc.documentElement().replaceChild(matchNode, m_hinting.node); + m_hinting.node=matchNode; +} + +void KXftConfig::applyExcludeRange(bool pixel) +{ + Exclude &range=pixel ? m_excludePixelRange : m_excludeRange; + + if(equal(range.from, 0) && equal(range.to, 0)) + { + if(!range.node.isNull()) + { + m_doc.documentElement().removeChild(range.node); + range.node.clear(); + } + } + else + { + QString fromString, + toString; + + fromString.setNum(range.from); + toString.setNum(range.to); + + QDomElement matchNode = m_doc.createElement("match"), + fromTestNode = m_doc.createElement("test"), + fromNode = m_doc.createElement("double"), + toTestNode = m_doc.createElement("test"), + toNode = m_doc.createElement("double"), + editNode = m_doc.createElement("edit"), + boolNode = m_doc.createElement("bool"); + QDomText fromText = m_doc.createTextNode(fromString), + toText = m_doc.createTextNode(toString), + boolText = m_doc.createTextNode("false"); + + matchNode.setAttribute("target", "font"); // CPD: Is target "font" or "pattern" ???? + fromTestNode.setAttribute("qual", "any"); + fromTestNode.setAttribute("name", pixel ? "pixelsize" : "size"); + fromTestNode.setAttribute("compare", "more_eq"); + fromTestNode.appendChild(fromNode); + fromNode.appendChild(fromText); + toTestNode.setAttribute("qual", "any"); + toTestNode.setAttribute("name", pixel ? "pixelsize" : "size"); + toTestNode.setAttribute("compare", "less_eq"); + toTestNode.appendChild(toNode); + toNode.appendChild(toText); + editNode.setAttribute("mode", "assign"); + editNode.setAttribute("name", "antialias"); + editNode.appendChild(boolNode); + boolNode.appendChild(boolText); + matchNode.appendChild(fromTestNode); + matchNode.appendChild(toTestNode); + matchNode.appendChild(editNode); + + if(!m_antiAliasing.node.isNull()) + m_doc.documentElement().removeChild(range.node); + m_doc.documentElement().appendChild(matchNode); + range.node=matchNode; + } +} + +bool KXftConfig::getAntiAliasing() const +{ + return m_antiAliasing.set; +} + +void KXftConfig::setAntiAliasing( bool set ) +{ + if(set!=m_antiAliasing.set) + { + m_antiAliasing.set = set; + m_madeChanges = true; + } +} + +void KXftConfig::applyAntiAliasing() +{ + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("bool"), + editNode = m_doc.createElement("edit"); + QDomText typeText = m_doc.createTextNode(m_antiAliasing.set ? "true" : "false"); + + matchNode.setAttribute("target", "font"); + editNode.setAttribute("mode", "assign"); + editNode.setAttribute("name", "antialias"); + editNode.appendChild(typeNode); + typeNode.appendChild(typeText); + matchNode.appendChild(editNode); + if(!m_antiAliasing.node.isNull()) + m_doc.documentElement().removeChild(m_antiAliasing.node); + m_doc.documentElement().appendChild(matchNode); + m_antiAliasing.node=matchNode; +} + +// KXftConfig only parses one config file, user's .fonts.conf usually. +// If that one doesn't exist, then KXftConfig doesn't know if antialiasing +// is enabled or not. So try to find out the default value from the default font. +// Maybe there's a better way *shrug*. +bool KXftConfig::aliasingEnabled() +{ + FcPattern *pattern = FcPatternCreate(); + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + FcResult result; + FcPattern *f = FcFontMatch( 0, pattern, &result ); + FcBool antialiased = FcTrue; + FcPatternGetBool( f, FC_ANTIALIAS, 0, &antialiased ); + FcPatternDestroy( f ); + FcPatternDestroy( pattern ); + return antialiased == FcTrue; +} + +#endif diff --git a/kcms/fonts/kxftconfig.h b/kcms/fonts/kxftconfig.h new file mode 100644 index 0000000000..0df6164591 --- /dev/null +++ b/kcms/fonts/kxftconfig.h @@ -0,0 +1,167 @@ +#ifndef __KXFTCONFIG_H__ +#define __KXFTCONFIG_H__ + +/* + Copyright (c) 2002 Craig Drummond + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#ifdef HAVE_FONTCONFIG + +#include + +#include +#include + +class KXftConfig +{ + public: + + struct Item + { + Item(QDomNode &n) : node(n), toBeRemoved(false) {} + Item() : toBeRemoved(false) {} + virtual void reset() { node.clear(); toBeRemoved=false; } + bool added() { return node.isNull(); } + + QDomNode node; + + virtual ~Item() {} + bool toBeRemoved; + }; + + struct SubPixel : public Item + { + enum Type + { + None, + Rgb, + Bgr, + Vrgb, + Vbgr + }; + + SubPixel(Type t, QDomNode &n) : Item(n), type(t) {} + SubPixel(Type t=None) : type(t) {} + + void reset() { Item::reset(); type=None; } + + Type type; + }; + + struct Exclude : public Item + { + Exclude(double f, double t, QDomNode &n) : Item(n), from(f), to(t) {} + Exclude(double f=0, double t=0) : from(f), to(t) {} + + void reset() { Item::reset(); from=to=0; } + + double from, + to; + }; + + struct Hint : public Item + { + enum Style + { + NotSet, + None, + Slight, + Medium, + Full + }; + + Hint(Style s, QDomNode &n) : Item(n), style(s) {} + Hint(Style s=NotSet) : style(s) {} + + void reset() { Item::reset(); style=NotSet; } + + Style style; + }; + + struct Hinting : public Item + { + Hinting(bool s, QDomNode &n) : Item(n), set(s) {} + Hinting(bool s=true) : set(s) {} + + void reset() { Item::reset(); set=true; } + + bool set; + }; + + struct AntiAliasing : public Item + { + AntiAliasing(bool s, QDomNode &n) : Item(n), set(s) {} + AntiAliasing(bool s=true) : set(s) {} + + void reset() { Item::reset(); set=true; } + + bool set; + }; + + public: + + explicit KXftConfig(); + + virtual ~KXftConfig(); + + bool reset(); + bool apply(); + bool getSubPixelType(SubPixel::Type &type); + void setSubPixelType(SubPixel::Type type); // SubPixel::None => turn off sub-pixel rendering + bool getExcludeRange(double &from, double &to); + void setExcludeRange(double from, double to); // from:0, to:0 => turn off exclude range + bool getHintStyle(Hint::Style &style); + void setHintStyle(Hint::Style style); + void setAntiAliasing(bool set); + bool getAntiAliasing() const; + bool changed() { return m_madeChanges; } + static QString description(SubPixel::Type t); + static const char * toStr(SubPixel::Type t); + static QString description(Hint::Style s); + static const char * toStr(Hint::Style s); + + private: + + void readContents(); + void applySubPixelType(); + void applyHintStyle(); + void applyAntiAliasing(); + void setHinting(bool set); + void applyHinting(); + void applyExcludeRange(bool pixel); + bool aliasingEnabled(); + + private: + + SubPixel m_subPixel; + Exclude m_excludeRange, + m_excludePixelRange; + Hint m_hint; + Hinting m_hinting; + AntiAliasing m_antiAliasing; + QDomDocument m_doc; + QString m_file; + bool m_madeChanges; + time_t m_time; +}; + +#endif + +#endif diff --git a/kcms/hardware/CMakeLists.txt b/kcms/hardware/CMakeLists.txt new file mode 100644 index 0000000000..ab22293590 --- /dev/null +++ b/kcms/hardware/CMakeLists.txt @@ -0,0 +1,9 @@ + +add_subdirectory( display ) + +if (CMAKE_SYSTEM_NAME MATCHES Linux) + # this one doesn't seem to be very portable, Alex + add_subdirectory( joystick ) +endif () + + diff --git a/kcms/hardware/display/CMakeLists.txt b/kcms/hardware/display/CMakeLists.txt new file mode 100644 index 0000000000..f3c0efd52a --- /dev/null +++ b/kcms/hardware/display/CMakeLists.txt @@ -0,0 +1,23 @@ + + + +########### next target ############### + +set(kcm_display_PART_SRCS display.cpp ) + + +add_library(kcm_display MODULE ${kcm_display_PART_SRCS}) + + +target_link_libraries(kcm_display + KF5::KCMUtils + KF5::I18n + KF5::KDE4Support +) + +install(TARGETS kcm_display DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES display.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcms/hardware/display/Messages.sh b/kcms/hardware/display/Messages.sh new file mode 100644 index 0000000000..561ccd0d8b --- /dev/null +++ b/kcms/hardware/display/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/display.pot diff --git a/kcms/hardware/display/display.cpp b/kcms/hardware/display/display.cpp new file mode 100644 index 0000000000..c24d1429bd --- /dev/null +++ b/kcms/hardware/display/display.cpp @@ -0,0 +1,117 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "display.h" +#include +#include + +K_PLUGIN_FACTORY(DisplayFactory, + registerPlugin(); + ) + +KCMDisplay::KCMDisplay( QWidget *parent, const QVariantList & ) + : KCModule( parent ) + , m_changed(false) +{ + m_tabs = new QTabWidget( this ); + + addTab( "randr", i18n( "Size && Orientation" ) ); + addTab( "nvidiadisplay", i18n( "Graphics Adaptor" ) ); + addTab( "nvidia3d", i18n( "3D Options" ) ); + addTab( "kgamma", i18n( "Monitor Gamma" ) ); + if ( QApplication::desktop()->isVirtualDesktop() ) + addTab( "xinerama", i18n( "Multiple Monitors" ) ); + addTab( "energy", i18n( "Power Control" ) ); + + QVBoxLayout *top = new QVBoxLayout( this ); + top->setMargin( 0 ); + top->setSpacing( KDialog::spacingHint() ); + top->addWidget( m_tabs ); + + setButtons( Apply ); + load(); +} + +void KCMDisplay::addTab( const QString &name, const QString &label ) +{ + QWidget *page = new QWidget( m_tabs ); + QVBoxLayout *top = new QVBoxLayout( page ); + top->setMargin( KDialog::marginHint() ); + + KCModule *kcm = KCModuleLoader::loadModule( name, KCModuleLoader::None,page ); + + if ( kcm ) + { + top->addWidget( kcm ); + m_tabs->addTab( page, label ); + + connect( kcm, SIGNAL(changed(bool)), SLOT(moduleChanged(bool)) ); + m_modules.insert(kcm, false); + } + else + delete page; +} + +void KCMDisplay::load() +{ + for (QMap::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it) + it.key()->load(); +} + +void KCMDisplay::save() +{ + for (QMap::Iterator it = m_modules.begin(); it != m_modules.end(); ++it) + if (it.value()) + it.key()->save(); +} + +void KCMDisplay::moduleChanged( bool isChanged ) +{ + QMap::Iterator currentModule = m_modules.find(static_cast(const_cast(sender()))); + Q_ASSERT(currentModule != m_modules.end()); + if (currentModule.value() == isChanged) + return; + + currentModule.value() = isChanged; + + bool c = false; + + for (QMap::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it) { + if (it.value()) { + c = true; + break; + } + } + + if (m_changed != c) { + m_changed = c; + emit changed(c); + } +} + +#include "display.moc" diff --git a/kcms/hardware/display/display.desktop b/kcms/hardware/display/display.desktop new file mode 100644 index 0000000000..fad2e876d2 --- /dev/null +++ b/kcms/hardware/display/display.desktop @@ -0,0 +1,191 @@ +[Desktop Entry] +Exec=kcmshell5 display +Icon=preferences-desktop-display +Type=Service +X-KDE-ServiceTypes=KCModule + +X-KDE-Library=kcm_display +X-KDE-ParentApp=kcontrol + +Name=Display +Name[af]=Skerm +Name[ar]=العرض +Name[ast]=Pantalla +Name[be]=Экран +Name[be@latin]=Ekran +Name[bg]=Екран +Name[bn]=ডিসপ্লে +Name[bn_IN]=প্রদর্শন +Name[br]=Diskwel +Name[bs]=Ekran +Name[ca]=Pantalla +Name[ca@valencia]=Pantalla +Name[cs]=Obrazovka +Name[csb]=Ekran +Name[cy]= Arddangos +Name[da]=Skærm +Name[de]=Anzeige +Name[el]=Οθόνη +Name[en_GB]=Display +Name[eo]=Vidigilo +Name[es]=Pantalla +Name[et]=Monitor +Name[eu]=Pantaila +Name[fa]=نمایش +Name[fi]=Näyttö +Name[fr]=Affichage +Name[fy]=Byldskerm +Name[ga]=Scáileán +Name[gl]=Pantalla +Name[gu]=ડિસ્પ્લે +Name[he]=תצוגה +Name[hi]=प्रदर्शक +Name[hne]=प्रदर्सक +Name[hr]=Zaslon +Name[hsb]=Wobrazowka +Name[hu]=Megjelenítés +Name[ia]=Monstrator +Name[id]=Tampilan +Name[is]=Skjár +Name[it]=Schermo +Name[ja]=ディスプレイ +Name[ka]=ჩვენება +Name[kk]=Дисплей +Name[km]=ការ​បង្ហាញ +Name[kn]=ಪ್ರದರ್ಶಕ +Name[ko]=디스플레이 +Name[ku]=Dîmender +Name[lt]=Ekranas +Name[lv]=Ekrāns +Name[mai]=देखाबू +Name[mk]=Приказ +Name[ml]=പ്രദര്‍ശനം +Name[mr]=डिस्प्ले +Name[ms]=Paparan +Name[nb]=Skjerm +Name[nds]=Dorstellen +Name[ne]=प्रदर्शन गर्नुहोस् +Name[nl]=Beeldscherm +Name[nn]=Skjerm +Name[oc]=Visualizar +Name[or]=ପ୍ରଦର୍ଶକ +Name[pa]=ਡਿਸਪਲੇਅ +Name[pl]=Ekran +Name[pt]=Ecrã +Name[pt_BR]=Tela +Name[ro]=Afișare +Name[ru]=Экран +Name[se]=Šearbma +Name[si]=සංදර්ශනය +Name[sk]=Obrazovka +Name[sl]=Zaslon +Name[sr]=Екран +Name[sr@ijekavian]=Екран +Name[sr@ijekavianlatin]=Ekran +Name[sr@latin]=Ekran +Name[sv]=Bildskärm +Name[ta]=காட்சி +Name[te]=ప్రదర్శన +Name[tg]=Экран +Name[th]=การแสดงผล +Name[tr]=Ekran +Name[ug]=كۆرسەتكۈچ +Name[uk]=Дисплей +Name[uz]=Displey +Name[uz@cyrillic]=Дисплей +Name[vi]=Màn hình +Name[wa]=Håynaedje +Name[x-test]=xxDisplayxx +Name[zh_CN]=显示 +Name[zh_TW]=顯示 +Comment=Display Settings +Comment[af]=Skerm Instellings +Comment[ar]=إعدادات العرض +Comment[ast]=Preferencies de la pantalla +Comment[be]=Настаўленні экрану +Comment[be@latin]=Nałady ekrana +Comment[bg]=Настройки на екрана +Comment[bn]=ডিসপ্লে সেটিংস +Comment[bn_IN]=প্রদর্শন সংক্রান্ত বৈশিষ্ট্য +Comment[br]=Kefluniañ an diskwel +Comment[bs]=Postavke ekrana +Comment[ca]=Arranjament de la pantalla +Comment[ca@valencia]=Arranjament de la pantalla +Comment[cs]=Nastavení obrazovky +Comment[csb]=Ùstôw ekranu +Comment[cy]=Gosodiadau Arddangos +Comment[da]=Skærmindstillinger +Comment[de]=Anzeige-Einstellungen +Comment[el]=Ρυθμίσεις οθόνης +Comment[en_GB]=Display Settings +Comment[eo]=Vidigila agordo +Comment[es]=Preferencias de la pantalla +Comment[et]=Monitoride seadistused +Comment[eu]=Pantailaren ezarpenak +Comment[fa]=نمایش تنظیمات +Comment[fi]=Näytön asetukset +Comment[fr]=Configuration de l'affichage +Comment[fy]=Byldskerm ynstellings +Comment[ga]=Socruithe an Scáileáin +Comment[gl]=Configuración da pantalla +Comment[gu]=ડિસ્પ્લે ગોઠવણીઓ +Comment[he]=הגדרות תצוגה +Comment[hi]=प्रकटन विन्यास +Comment[hne]=देखइया सेटिंग +Comment[hr]=Postavke zaslona +Comment[hsb]=Nastajenja za wobrazowku +Comment[hu]=Megjelenési beállítások +Comment[ia]=Monstra preferentias +Comment[id]=Pengaturan Tampilan +Comment[is]=Stillingar skjás +Comment[it]=Impostazioni dello schermo +Comment[ja]=ディスプレイの設定 +Comment[ka]=დისპლეის კონფიგურირება +Comment[kk]=Дисплей баптаулары +Comment[km]=កំណត់​ការ​បង្ហាញ +Comment[kn]=ಪ್ರದರ್ಶಕದ ಸಂಯೋಜನೆಗಳು +Comment[ko]=디스플레이 설정 +Comment[ku]=Mîhengên Dîmenderê +Comment[lt]=Ekrano parametrai +Comment[lv]=Ekrāna parametri +Comment[mai]=जमावट देखाबू +Comment[mk]=Поставувања на приказот +Comment[ml]=പ്രദര്‍ശനത്തിന്റെ സജ്ജീകരണങ്ങള്‍ +Comment[mr]=डिस्प्ले संयोजना +Comment[ms]=Seting Paparan +Comment[nb]=Skjerminnstillinger +Comment[nds]=Dorstellen inrichten +Comment[ne]=सेटिङ प्रदर्शन गर्नुहोस् +Comment[nl]=Beeldscherminstellingen +Comment[nn]=Skjerminnstillingar +Comment[or]=ପ୍ରଦର୍ଶକ ସଂରଚନା +Comment[pa]=ਡਿਸਪਲੇਅ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia ekranu +Comment[pt]=Configuração do ecrã +Comment[pt_BR]=Configurações da tela +Comment[ro]=Configurări afișare +Comment[ru]=Настройка экрана +Comment[se]=Šearbmaheivehusat +Comment[si]=සංදර්ශන සැකසුම් +Comment[sk]=Nastavenie obrazovky +Comment[sl]=Nastavitve zaslona +Comment[sr]=Поставке екрана +Comment[sr@ijekavian]=Поставке екрана +Comment[sr@ijekavianlatin]=Postavke ekrana +Comment[sr@latin]=Postavke ekrana +Comment[sv]=Anpassa bildskärm +Comment[ta]=அமைப்புகளை காட்டு +Comment[te]=ప్రదర్శన అమరికలు +Comment[tg]=Танзимоти экран +Comment[th]=ตั้งค่าต่าง ๆ ของการแสดงผล +Comment[tr]=Ekran Ayarları +Comment[ug]=كۆرۈنمە يۈز تەڭشىكى +Comment[uk]=Налаштування дисплея +Comment[uz]=Displeyning moslamalari +Comment[uz@cyrillic]=Дисплейнинг мосламалари +Comment[vi]=Thiết lập hiển thị +Comment[wa]=Apontiaedjes do Håynaedje +Comment[x-test]=xxDisplay Settingsxx +Comment[zh_CN]=显示设置 +Comment[zh_TW]=顯示設定 +Categories=Qt;KDE;X-KDE-settings-hardware; diff --git a/kcms/hardware/display/display.h b/kcms/hardware/display/display.h new file mode 100644 index 0000000000..7e0c342240 --- /dev/null +++ b/kcms/hardware/display/display.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Nadeem Hasan + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include + +class QTabWidget; + +class KCMDisplay : public KCModule +{ + Q_OBJECT + + public: + KCMDisplay( QWidget *parent, const QVariantList & ); + void load(); + void save(); + + private Q_SLOTS: + void moduleChanged(bool isChanged); + + private: + void addTab( const QString &name, const QString &label ); + + QTabWidget *m_tabs; + QMap m_modules; + bool m_changed; +}; + +#endif // DISPLAY_H + diff --git a/kcms/hardware/joystick/CMakeLists.txt b/kcms/hardware/joystick/CMakeLists.txt new file mode 100644 index 0000000000..8427b4c128 --- /dev/null +++ b/kcms/hardware/joystick/CMakeLists.txt @@ -0,0 +1,32 @@ + + + +########### next target ############### + +set(kcm_joystick_PART_SRCS + joystick.cpp + joywidget.cpp + poswidget.cpp + joydevice.cpp + caldialog.cpp ) + + +add_library(kcm_joystick MODULE ${kcm_joystick_PART_SRCS}) + + +target_link_libraries(kcm_joystick + KF5::Completion + KF5::KCMUtils + KF5::I18n + KF5::KIOWidgets + KF5::KDE4Support +) + +install(TARGETS kcm_joystick DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES joystick.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +# diff --git a/kcms/hardware/joystick/Messages.sh b/kcms/hardware/joystick/Messages.sh new file mode 100644 index 0000000000..1d116ceb76 --- /dev/null +++ b/kcms/hardware/joystick/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/joystick.pot diff --git a/kcms/hardware/joystick/caldialog.cpp b/kcms/hardware/joystick/caldialog.cpp new file mode 100644 index 0000000000..76e960b40f --- /dev/null +++ b/kcms/hardware/joystick/caldialog.cpp @@ -0,0 +1,200 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller + * kollix@aon.at + * This file is part of the KDE Control Center Module for Joysticks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "caldialog.h" +#include "joydevice.h" + +#include +#include +#include + +#include +#include +#include +#include +//-------------------------------------------------------------- + +CalDialog::CalDialog(QWidget *parent, JoyDevice *joy) + : KDialog( parent ), + joydev(joy) +{ + setObjectName( "calibrateDialog" ); + setModal( true ); + setCaption( i18n("Calibration") ); + setButtons( Cancel | User1 ); + setDefaultButton( User1 ); + setButtonGuiItem( User1, KGuiItem( i18n("Next") ) ); + + KVBox *main = new KVBox( this ); + setMainWidget( main ); + + text = new QLabel(main); + text->setMinimumHeight(200); + valueLbl = new QLabel(main); + connect(this,SIGNAL(user1Clicked()),this,SLOT(slotUser1())); +} + +//-------------------------------------------------------------- + +void CalDialog::calibrate() +{ + text->setText(i18n("Please wait a moment to calculate the precision")); + setResult(-1); + show(); + + // calibrate precision (which min,max delivers the joystick in its center position) + // get values through the normal idle procedure + QTimer ti; + ti.setSingleShot(true); // single shot + ti.start(2000); // in 2 seconds + + // normally I'd like to hide the 'Next' button in this step, + // but it does not work - which means: in the steps after the first, + // the 'Next' button does not have the focus (to be the default button) + + do + { + qApp->processEvents(QEventLoop::AllEvents, 2000); + } + while ( ti.isActive() && (result() != QDialog::Rejected) ); + + if ( result() == QDialog::Rejected ) return; // user cancelled the dialog + + joydev->calcPrecision(); + + int i, lastVal; + int min[2], center[2], max[2]; + QString hint; + + for (i = 0; i < joydev->numAxes(); i++) + { + if ( i == 0 ) + hint = i18n("(usually X)"); + else if ( i == 1 ) + hint = i18n("(usually Y)"); + else + hint = ""; + + // minimum position + text->setText(i18n("Calibration is about to check the value range your device delivers.

    " + "Please move axis %1 %2 on your device to the minimum position.

    " + "Press any button on the device or click on the 'Next' button " + "to continue with the next step.
    ", i+1, hint)); + waitButton(i, true, lastVal); + + if ( result() == QDialog::Rejected ) return; // user cancelled the dialog + + joydev->resetMinMax(i, lastVal); + if ( result() != -2 ) waitButton(i, false, lastVal); + + if ( result() == QDialog::Rejected ) return; // user cancelled the dialog + + min[0] = joydev->axisMin(i); + min[1] = joydev->axisMax(i); + + // center position + text->setText(i18n("Calibration is about to check the value range your device delivers.

    " + "Please move axis %1 %2 on your device to the center position.

    " + "Press any button on the device or click on the 'Next' button " + "to continue with the next step.
    ", i+1, hint)); + waitButton(i, true, lastVal); + + if ( result() == QDialog::Rejected ) return; // user cancelled the dialog + + joydev->resetMinMax(i, lastVal); + if ( result() != -2 ) waitButton(i, false, lastVal); + + if ( result() == QDialog::Rejected ) return; // user canceled the dialog + + center[0] = joydev->axisMin(i); + center[1] = joydev->axisMax(i); + + // maximum position + text->setText(i18n("Calibration is about to check the value range your device delivers.

    " + "Please move axis %1 %2 on your device to the maximum position.

    " + "Press any button on the device or click on the 'Next' button " + "to continue with the next step.
    ", i+1, hint)); + waitButton(i, true, lastVal); + + if ( result() == QDialog::Rejected ) return; // user cancelled the dialog + + joydev->resetMinMax(i, lastVal); + if ( result() != -2 ) waitButton(i, false, lastVal); + + if ( result() == QDialog::Rejected ) return; // user canceled the dialog + + max[0] = joydev->axisMin(i); + max[1] = joydev->axisMax(i); + + joydev->calcCorrection(i, min, center, max); + } + + JoyDevice::ErrorCode ret = joydev->applyCalibration(); + + if ( ret != JoyDevice::SUCCESS ) + { + KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); + reject(); + } + + KMessageBox::information(this, i18n("You have successfully calibrated your device"), i18n("Calibration Success")); + accept(); +} + +//-------------------------------------------------------------- + +void CalDialog::waitButton(int axis, bool press, int &lastVal) +{ + JoyDevice::EventType type; + int number, value; + bool button = false; + lastVal = 0; + + setResult(-1); + // loop until the user presses a button on the device or on the dialog + do + { + qApp->processEvents(QEventLoop::AllEvents, 100); + + if ( joydev->getEvent(type, number, value) ) + { + button = ( (type == JoyDevice::BUTTON) && (press ? (value == 1) : (value == 0)) ); + + if ( (type == JoyDevice::AXIS) && (number == axis) ) + valueLbl->setText(i18n("Value Axis %1: %2", axis+1, lastVal = value)); + } + } + while ( !button && (result() == -1) ); +} + +//-------------------------------------------------------------- +// Next button + +void CalDialog::slotUser1() +{ + setResult(-2); +} + +//-------------------------------------------------------------- + +#include "caldialog.moc" + +//-------------------------------------------------------------- diff --git a/kcms/hardware/joystick/caldialog.h b/kcms/hardware/joystick/caldialog.h new file mode 100644 index 0000000000..e1a1fd7f7f --- /dev/null +++ b/kcms/hardware/joystick/caldialog.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef _CALDIALOG_H_ +#define _CALDIALOG_H_ + +#include + +#include + +class JoyDevice; + +// the dialog which tells the user all steps to calibrate the device + +class CalDialog : public KDialog +{ + Q_OBJECT + + public: + CalDialog(QWidget *parent, JoyDevice *joy); + + void calibrate(); + + private: + void waitButton(int axis, bool press, int &lastVal); + + private Q_SLOTS: + virtual void slotUser1(); + + private: + JoyDevice *joydev; + + QLabel *text; + QLabel *valueLbl; +}; + +#endif diff --git a/kcms/hardware/joystick/joydevice.cpp b/kcms/hardware/joystick/joydevice.cpp new file mode 100644 index 0000000000..7dffb10faf --- /dev/null +++ b/kcms/hardware/joystick/joydevice.cpp @@ -0,0 +1,403 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "joydevice.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------- + +JoyDevice::JoyDevice(const QString &devicefile) + : devName(devicefile), joyFd(-1), buttons(0), axes(0), + amin(0), amax(0), corr(0), origCorr(0) +{ +} + +//-------------------------------------------------------------- + +QString JoyDevice::errText(ErrorCode code) const +{ + switch ( code ) + { + case SUCCESS: return ""; + + case OPEN_FAILED: + { + return i18n("The given device %1 could not be opened: %2", + devName, strerror(errno)); + } + + case NO_JOYSTICK: + { + return i18n("The given device %1 is not a joystick.", devName); + } + + case ERR_GET_VERSION: + { + return i18n("Could not get kernel driver version for joystick device %1: %2", + devName, strerror(errno)); + } + + case WRONG_VERSION: + { + int version = 0; + int fd = ::open(devName.toLatin1(), O_RDONLY); + if ( fd != -1 ) + { + ::ioctl(fd, JSIOCGVERSION, &version); + ::close(fd); + } + + KLocalizedString loc = ki18n("The current running kernel driver version (%1.%2.%3) is not the one this module was compiled for (%4.%5.%6)."); + loc.subs(version >> 16); + loc.subs((version >> 8) & 0xFF); + loc.subs(version & 0xFF); + loc.subs(JS_VERSION >> 16); + loc.subs((JS_VERSION >> 8) & 0xFF); + loc.subs(JS_VERSION & 0xFF); + return loc.toString(); + } + + case ERR_GET_BUTTONS: + { + return i18n("Could not get number of buttons for joystick device %1: %2", + devName, strerror(errno)); + } + + case ERR_GET_AXES: + { + return i18n("Could not get number of axes for joystick device %1: %2", + devName, strerror(errno)); + } + + case ERR_GET_CORR: + { + return i18n("Could not get calibration values for joystick device %1: %2", + devName, strerror(errno)); + } + + case ERR_RESTORE_CORR: + { + return i18n("Could not restore calibration values for joystick device %1: %2", + devName, strerror(errno)); + } + + case ERR_INIT_CAL: + { + return i18n("Could not initialize calibration values for joystick device %1: %2", + devName, strerror(errno)); + } + + case ERR_APPLY_CAL: + { + return i18n("Could not apply calibration values for joystick device %1: %2", + devName, strerror(errno)); + } + + default: return i18n("internal error - code %1 unknown", int(code)); + } +} + +//-------------------------------------------------------------- + +JoyDevice::ErrorCode JoyDevice::open() +{ + if ( joyFd != -1 ) return JoyDevice::SUCCESS; // already open + + int fd = ::open(devName.toLatin1(), O_RDONLY); + + if ( fd == -1 ) + return JoyDevice::OPEN_FAILED; + + // we could open the devicefile, now check if a joystick is attached + char name[128]; + + if ( ::ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1 ) + { + ::close(fd); + return JoyDevice::NO_JOYSTICK; + } + + // check the kernel driver version + int version; + if ( ::ioctl(fd, JSIOCGVERSION, &version) == -1 ) + { + ::close(fd); + return JoyDevice::ERR_GET_VERSION; + } + + if ( version != JS_VERSION ) + { + ::close(fd); + return JoyDevice::WRONG_VERSION; + } + + char bt = 0, ax = 0; + if ( ::ioctl(fd, JSIOCGBUTTONS, &bt) == -1 ) + { + ::close(fd); + return JoyDevice::ERR_GET_BUTTONS; + } + + if ( ::ioctl(fd, JSIOCGAXES, &ax) == -1 ) + { + ::close(fd); + return JoyDevice::ERR_GET_AXES; + } + + struct js_corr *oldCorr = new struct js_corr[ax]; + + if ( ::ioctl(fd, JSIOCGCORR, oldCorr) == -1 ) + { + ::close(fd); + delete [] oldCorr; + return JoyDevice::ERR_GET_CORR; + } + + descr = name; + joyFd = fd; + axes = ax; + buttons = bt; + origCorr = oldCorr; + corr = new struct js_corr[axes]; + + amin = new int[axes]; + amax = new int[axes]; + + int i; + + for (i = 0; i < axes; i++) + resetMinMax(i); + + return JoyDevice::SUCCESS; +} + +//-------------------------------------------------------------- + +void JoyDevice::close() +{ + if ( joyFd == -1 ) return; + + ::close(joyFd); + + joyFd = -1; + descr = ""; + + delete [] amin; + delete [] amax; + amin = 0; + amax = 0; + + delete [] corr; + corr = 0; + delete [] origCorr; + origCorr = 0; +} + +//-------------------------------------------------------------- + +int JoyDevice::axisMin(int axis) const +{ + if ( (axis < 0) || (axis >= axes) ) return 0; + + return amin[axis]; +} + +//-------------------------------------------------------------- + +int JoyDevice::axisMax(int axis) const +{ + if ( (axis < 0) || (axis >= axes) ) return 0; + + return amax[axis]; +} + +//-------------------------------------------------------------- + +JoyDevice::ErrorCode JoyDevice::initCalibration() +{ + if ( joyFd == -1 ) return JoyDevice::ERR_INIT_CAL; + + int i; + + // Reset all current correction values + for (i = 0; i < axes; i++) + { + corr[i].type = JS_CORR_NONE; + corr[i].prec = 0; + } + + if ( ::ioctl(joyFd, JSIOCSCORR, corr) == -1 ) + return JoyDevice::ERR_INIT_CAL; + + for (i = 0; i < axes; i++) + corr[i].type = JS_CORR_BROKEN; + + return JoyDevice::SUCCESS; +} + +//-------------------------------------------------------------- + +JoyDevice::ErrorCode JoyDevice::applyCalibration() +{ + if ( joyFd == -1 ) return JoyDevice::ERR_APPLY_CAL; + + if ( ::ioctl(joyFd, JSIOCSCORR, corr) == -1 ) + return JoyDevice::ERR_APPLY_CAL; + + return JoyDevice::SUCCESS; +} + +//-------------------------------------------------------------- + +void JoyDevice::resetMinMax(int axis, int value) +{ + amin[axis] = value; + amax[axis] = value; +} + +//-------------------------------------------------------------- + +void JoyDevice::calcPrecision() +{ + if ( !corr ) return; + + int i; + + for (i = 0; i < axes; i++) + { + corr[i].prec = amax[i] - amin[i]; + kDebug() << "Precision for axis: " << i << ": " << corr[i].prec; + } +} + +//-------------------------------------------------------------- + +JoyDevice::ErrorCode JoyDevice::restoreCorr() +{ + if ( joyFd == -1 ) return JoyDevice::SUCCESS; + + if ( ::ioctl(joyFd, JSIOCSCORR, origCorr) == -1 ) + return JoyDevice::ERR_RESTORE_CORR; + else + return JoyDevice::SUCCESS; +} + +//-------------------------------------------------------------- + +JoyDevice::~JoyDevice() +{ + close(); +} + +//-------------------------------------------------------------- + +bool JoyDevice::getEvent(JoyDevice::EventType &type, int &number, int &value) +{ + number = value = 0; + + int ret; + + fd_set readSet; + + FD_ZERO(&readSet); + FD_SET(joyFd, &readSet); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + + ret = ::select(joyFd + 1, &readSet, 0, 0, &timeout); + + if ( ret == 1 ) // got an event from the joystick + { + struct js_event e; + + if ( ::read(joyFd, &e, sizeof(struct js_event)) == sizeof(struct js_event) ) + { + if ( e.type & JS_EVENT_BUTTON ) + { + type = JoyDevice::BUTTON; + value = e.value; + number = e.number; + + return true; + } + + if ( e.type & JS_EVENT_AXIS ) + { + type = JoyDevice::AXIS; + value = e.value; + number = e.number; + + // store min, max values + if ( e.value < amin[number] ) amin[number] = e.value; + if ( e.value > amax[number] ) amax[number] = e.value; + + return true; + } + } + } + + return false; // no event +} + +//-------------------------------------------------------------- + +void JoyDevice::calcCorrection(int axis, int *min, int *center, int *max) +{ + const int MIN = 0; + const int MAX = 1; + + double a, b, c, d; + + a = center[MIN]; // inputs.cmin[1]; + b = center[MAX]; // inputs.cmax[1]; + c = 32767.0 / (center[MIN] - min[MAX]); // (inputs.cmin[1] - inputs.cmax[0]); + d = 32767.0 / (max[MIN] - center[MAX]); // (inputs.cmin[2] - inputs.cmax[1]); + + corr[axis].coef[0] = (int)rint(a); + corr[axis].coef[1] = (int)rint(b); + corr[axis].coef[2] = (int)rint(c*16384.0); + corr[axis].coef[3] = (int)rint(d*16384.0); + + kDebug() << "min min: " << min[0] << " max: " << min[1] ; + kDebug() << "max min: " << max[0] << " max: " << max[1] ; + kDebug() << "Correction values for axis: " << axis << ": " + << corr[axis].coef[0] << ", " + << corr[axis].coef[1] << ", " + << corr[axis].coef[2] << ", " + << corr[axis].coef[3] << endl; +} + +//-------------------------------------------------------------- diff --git a/kcms/hardware/joystick/joydevice.h b/kcms/hardware/joystick/joydevice.h new file mode 100644 index 0000000000..4c26581e44 --- /dev/null +++ b/kcms/hardware/joystick/joydevice.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef _JOYDEVICE_H_ +#define _JOYDEVICE_H_ + +#include + +#include + +#ifdef Q_OS_LINUX +#undef __STRICT_ANSI__ +#include +#define __STRICT_ANSI__ +#endif + +#ifdef Q_OS_FREEBSD +#include +#endif + +// helper class which holds all current values, file descriptor, etc. for +// one device +class JoyDevice +{ + public: + enum ErrorCode + { + SUCCESS, + OPEN_FAILED, + NO_JOYSTICK, + WRONG_VERSION, + ERR_GET_VERSION, + ERR_GET_BUTTONS, + ERR_GET_AXES, + ERR_GET_CORR, + ERR_RESTORE_CORR, + ERR_INIT_CAL, + ERR_APPLY_CAL + }; + + enum EventType + { + BUTTON, + AXIS + }; + + // devicefile to use, e.g. "/dev/js0" + JoyDevice(const QString &devicefile); + ~JoyDevice(); + + // returns one of the error-codes from above + ErrorCode open(); + + // return descriptive error text for given error code + QString errText(ErrorCode code) const; + + int fd() const { return joyFd; } + void close(); + ErrorCode restoreCorr(); + + // return devicefilename from constructor + const QString &device() const { return devName; } + + // descriptive text for this device read from the driver + QString text() const { return descr; } + + int numButtons() const { return buttons; } + int numAxes() const { return axes; } + int axisMin(int axis) const; + int axisMax(int axis) const; + + // read next event from device; returns true if there was an event during the short timeout + bool getEvent(JoyDevice::EventType &type, int &number, int &value); + + // methods for calibration + ErrorCode initCalibration(); // must be called first + void calcPrecision(); + + void resetMinMax(int axis, int value = 0); + + // calculate correction values + // min[2], center[2], max[2], index 0 == minimum, index 1 == maximum + void calcCorrection(int axis, int *min, int *center, int *max); + ErrorCode applyCalibration(); + + private: + QString devName; // device filename + QString descr; // descriptive text + int joyFd; + + int buttons; + int axes; + int *amin; // axes min values + int *amax; // axes max values + + struct js_corr *corr; // calibration values during the calib. steps + struct js_corr *origCorr; // original calibration correction values +}; + +#endif diff --git a/kcms/hardware/joystick/joystick.cpp b/kcms/hardware/joystick/joystick.cpp new file mode 100644 index 0000000000..c162211c09 --- /dev/null +++ b/kcms/hardware/joystick/joystick.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "joystick.h" +#include "joywidget.h" +#include "joydevice.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +//--------------------------------------------------------------------------------------------- + +K_PLUGIN_FACTORY(JoystickFactory, registerPlugin();) + +//--------------------------------------------------------------------------------------------- + +Joystick::Joystick(QWidget *parent, const QVariantList &) + : KCModule(parent) +{ + setButtons(Help); + setAboutData(new KAboutData("kcmjoystick", QString(), i18n("KDE Joystick Control Module"), "1.0", + i18n("KDE System Settings Module to test Joysticks"), + KAboutData::License_GPL, i18n("(c) 2004, Martin Koller"), + QString(), "kollix@aon.at")); + + setQuickHelp( i18n("

    Joystick

    " + "This module helps to check if your joystick is working correctly.
    " + "If it delivers wrong values for the axes, you can try to solve this with " + "the calibration.
    " + "This module tries to find all available joystick devices " + "by checking /dev/js[0-4] and /dev/input/js[0-4]
    " + "If you have another device file, enter it in the combobox.
    " + "The Buttons list shows the state of the buttons on your joystick, the Axes list " + "shows the current value for all axes.
    " + "NOTE: the current Linux device driver (Kernel 2.4, 2.6) can only autodetect" + "
      " + "
    • 2-axis, 4-button joystick
    • " + "
    • 3-axis, 4-button joystick
    • " + "
    • 4-axis, 4-button joystick
    • " + "
    • Saitek Cyborg 'digital' joysticks
    • " + "
    " + "(For details you can check your Linux source/Documentation/input/joystick.txt)" + )); + + joyWidget = new JoyWidget(this); + + QVBoxLayout *top = new QVBoxLayout(this); + top->setMargin(0); + top->setSpacing(KDialog::spacingHint()); + top->addWidget(joyWidget); +} + +//--------------------------------------------------------------------------------------------- + +void Joystick::load() +{ + joyWidget->init(); +} + +//--------------------------------------------------------------------------------------------- + +void Joystick::defaults() +{ + joyWidget->resetCalibration(); + + emit changed(true); +} + +//--------------------------------------------------------------------------------------------- + +#include "joystick.moc" diff --git a/kcms/hardware/joystick/joystick.desktop b/kcms/hardware/joystick/joystick.desktop new file mode 100644 index 0000000000..e5ff24a8b9 --- /dev/null +++ b/kcms/hardware/joystick/joystick.desktop @@ -0,0 +1,228 @@ +[Desktop Entry] +Exec=kcmshell5 joystick + +X-DocPath=kcontrol/joystick/index.html +Terminal=false +Type=Service +Icon=preferences-desktop-gaming +Categories=Qt;KDE;X-KDE-settings-hardware; + +X-KDE-ServiceTypes=KCModule +X-KDE-Library=kcm_joystick +X-KDE-Test-Module=true +X-KDE-System-Settings-Parent-Category=input-devices +X-KDE-ParentApp=kcontrol +X-KDE-Weight=70 + +Comment=Joystick settings +Comment[ar]=إعدادات عصى الألعاب +Comment[ast]=Preferencies del Joystick +Comment[be@latin]=Nałady džojstyka +Comment[bg]=Настройки на джойстик +Comment[bn]=জয়স্টিক সেটিংস +Comment[bn_IN]=জয়-স্টিক সংক্রান্ত বৈশিষ্ট্য +Comment[bs]=Postavke džojstika +Comment[ca]=Arranjament de la palanca de control +Comment[ca@valencia]=Arranjament de la palanca de control +Comment[cs]=Nastavení joysticku +Comment[csb]=Nastôw joysticka +Comment[da]=Joystick-indstillinger +Comment[de]=Joystick-Einstellungen +Comment[el]=Ρυθμίσεις χειριστηρίου +Comment[en_GB]=Joystick settings +Comment[eo]=Ludbastonaj agordoj +Comment[es]=Preferencias del Joystick +Comment[et]=Juhtpuldi seadistused +Comment[eu]=Kontrol-palankaren ezarpenak +Comment[fi]=Peliohjainten asetukset +Comment[fr]=Configuration du joystick +Comment[fy]=Joystick ynstellings +Comment[ga]=Socruithe an luamháin stiúrtha +Comment[gl]=Configuración do joystick +Comment[gu]=જોયસ્ટિક ગોઠવણીઓ +Comment[he]=הגדרות ג'ויסטיק +Comment[hi]=जॉयस्टिक विन्यास +Comment[hne]=जायस्टिक सेटिंग +Comment[hr]=Postavke igraće palice (joysticka) +Comment[hsb]=Nastajenja za joystick +Comment[hu]=Botkormány-beállítások +Comment[ia]=Preferentias de Joystick +Comment[id]=Pengaturan tongkat ria +Comment[is]=Stillingar stýripinna +Comment[it]=Impostazioni del joystick +Comment[ja]=ジョイスティックの設定 +Comment[kk]=Джойстик параметрлері +Comment[km]=ការ​កំណត់ Joystick +Comment[kn]=ಅಂಕೆಗೋಲು (ಜಾಯ್ ಸ್ಟಿಕ್) ಸಂಯೋಜನೆಗಳು +Comment[ko]=조이스틱 설정 +Comment[ku]=Mîhengên joystik +Comment[lt]=Valdymo svirties nustatymai +Comment[lv]=Kursorsviras iestatījumi +Comment[mai]=ज्वायस्टिक सेटिंग +Comment[mk]=Поставувања за палката за играње +Comment[ml]=ജോയ്സ്റ്റിക് സജ്ജീകരണങ്ങള്‍ +Comment[mr]=जॉयस्टीक् संयोजना +Comment[nb]=Styrespakinnstillinger +Comment[nds]=Joystick instellen +Comment[nl]=Joystick instellen +Comment[nn]=Innstillingar for styrespakar +Comment[or]=Joystick ସଂରଚନା +Comment[pa]=ਜਾਏਸਟਿੱਕ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia joysticka +Comment[pt]=Configuração do 'joystick' +Comment[pt_BR]=Configurações do joystick +Comment[ro]=Configurări joystick +Comment[ru]=Настройка джойстика +Comment[si]=Joystick සැකසුම් +Comment[sk]=Nastavenie joysticku +Comment[sl]=Nastavitve igralne palice +Comment[sr]=Поставке џојстика +Comment[sr@ijekavian]=Поставке џојстика +Comment[sr@ijekavianlatin]=Postavke džojstika +Comment[sr@latin]=Postavke džojstika +Comment[sv]=Anpassa styrspak +Comment[ta]=ஜாய்ஸ்டிக் அமைப்புகள் +Comment[te]=Joystick అమరికలు +Comment[tg]=Танзимоти ҷойстик +Comment[th]=ตั้งค่าต่าง ๆ ของแท่งควบคุม +Comment[tr]=Oyun çubuğu ayarları +Comment[ug]=ئويۇن دەستە تەڭشەكلەر +Comment[uk]=Параметри джойстика +Comment[vi]=Thiết lập tay điều khiển trò chơi +Comment[wa]=Apontiaedje del djîsse di djeu +Comment[x-test]=xxJoystick settingsxx +Comment[zh_CN]=游戏杆设置 +Comment[zh_TW]=搖桿設定 +Name=Joystick +Name[af]=Joystick +Name[ar]=عصا الألعاب +Name[ast]=Joystick +Name[be]=Джойстык +Name[be@latin]=Džojstyk +Name[bg]=Джойстик +Name[bn]=জয়-স্টিক +Name[bn_IN]=জয়-স্টিক +Name[br]=Lanker-c'hoari +Name[bs]=Džojstik +Name[ca]=Palanca de control +Name[ca@valencia]=Palanca de control +Name[cs]=Joystick +Name[csb]=Joystick +Name[cy]=Ffôn reoli +Name[da]=Joystick +Name[de]=Joystick +Name[el]=Χειριστήριο +Name[en_GB]=Joystick +Name[eo]=Stirstango +Name[es]=Joystick +Name[et]=Juhtpult +Name[eu]=Kontrol-palanka +Name[fa]=اهرم کنترل +Name[fi]=Peliohjain +Name[fr]=Joystick +Name[fy]=Joystick +Name[ga]=Luamhán Stiúrtha +Name[gl]=Joystick +Name[gu]=જોયસ્ટિક +Name[he]=ג'ויסטיק +Name[hi]=जॉयस्टिक +Name[hne]=जायस्टिक +Name[hr]=Igraća palica +Name[hsb]=Joystick +Name[hu]=Botkormány +Name[ia]=Joystick +Name[id]=Tongkat Ria +Name[is]=Stýripinnar +Name[it]=Joystick +Name[ja]=ジョイスティック +Name[ka]=ჯოისტიკი +Name[kk]=Джойстик +Name[km]=យ៉យស្ទីក +Name[kn]=ಅಂಕೆಗೋಲು (ಜಾಯ್ ಸ್ಟಿಕ್) +Name[ko]=조이스틱 +Name[ku]=Joystîk +Name[lt]=Valdymo svirtis +Name[lv]=Kursorsvira +Name[mai]=जायस्टिक +Name[mk]=Џојстик +Name[ml]=ജോയ്സ്റ്റിക് +Name[mr]=जॉयस्टिक +Name[ms]=Kayu Bidik +Name[nb]=Styrespak +Name[nds]=Joystick +Name[ne]=जोयस्टिक +Name[nl]=Joystick +Name[nn]=Styrespak +Name[or]=Joystick +Name[pa]=ਜਾਏਸਟਿੱਕ +Name[pl]=Joystick +Name[pt]=Joystick +Name[pt_BR]=Joystick +Name[ro]=Joystick +Name[ru]=Джойстик +Name[se]=Stivrensággi +Name[si]=ජෝයිස්ටික් +Name[sk]=Joystick +Name[sl]=Igralna palica +Name[sr]=Џојстик +Name[sr@ijekavian]=Џојстик +Name[sr@ijekavianlatin]=Džojstik +Name[sr@latin]=Džojstik +Name[sv]=Styrspak +Name[ta]=இயக்கு கருவி +Name[te]=జాయ్ స్టిక్ +Name[tg]=Ҷойстик +Name[th]=แท่งควบคุม +Name[tr]=Oyun Çubuğu +Name[ug]=ئويۇن دەستىسى(رول) +Name[uk]=Джойстик +Name[uz]=Joystik +Name[uz@cyrillic]=Жойстик +Name[vi]=Tay điều khiển +Name[wa]=Djîsse di djeu +Name[xh]=Uvuyo +Name[x-test]=xxJoystickxx +Name[zh_CN]=游戏杆 +Name[zh_TW]=搖桿 + +X-KDE-Keywords=joystick,gamepad,controller +X-KDE-Keywords[bs]=dzojstik +X-KDE-Keywords[ca]=palanca de control,comandament de joc,controlador +X-KDE-Keywords[ca@valencia]=palanca de control,comandament de joc,controlador +X-KDE-Keywords[da]=joystick,gamepad,controller +X-KDE-Keywords[de]=Joystick,Gamepad,Spielsteuerung +X-KDE-Keywords[el]=χειριστήριο,πληκτρολόγιο παιχνιδιών,ελεγκτής +X-KDE-Keywords[es]=joystick,gamepad,controlador +X-KDE-Keywords[et]=juhtpult,juhtkang,kontroller +X-KDE-Keywords[eu]=kontrol-palanka,joko-teklatua,kontroladorea +X-KDE-Keywords[fi]=peliohjain,sauvaohjain,ohjain +X-KDE-Keywords[fr]=joystick, gamepad, contrôleur +X-KDE-Keywords[ga]=luamhán stiúrtha,ceap cluiche,rialaitheoir +X-KDE-Keywords[gl]=joystick, gamepad, controlador +X-KDE-Keywords[hu]=botkormány,játéktábla,vezérlő +X-KDE-Keywords[ia]=joystick,gamepad,controlator +X-KDE-Keywords[it]=joystick,gamepad,controller +X-KDE-Keywords[kk]=joystick,gamepad,controller +X-KDE-Keywords[km]=joystick,gamepad,controller +X-KDE-Keywords[ko]=joystick,gamepad,controller,조이스틱,컨트롤러 +X-KDE-Keywords[mr]=जॉयस्टिक, गेमपॅड, कन्ट्रोलर +X-KDE-Keywords[nb]=styrespak,spillepute,kontroller +X-KDE-Keywords[nds]=Stüerknüppel,Speelknüppel,Speelstüer-Reedschap,Speelstüern,Stüern,Kontroller,Speelkuntrull +X-KDE-Keywords[nl]=joystick,spelpad,besturing +X-KDE-Keywords[pa]=ਜਾਏਸਟਿੱਕ,ਗੇਮਪੈਡ,ਕੰਟਰੋਲਰ +X-KDE-Keywords[pl]=joystick,gamepad,kontroler +X-KDE-Keywords[pt]=joystick,comando,controlador +X-KDE-Keywords[pt_BR]=joystick,gamepad,controle +X-KDE-Keywords[ru]=joystick,gamepad,controller,джойстик,геймпад,контроллер +X-KDE-Keywords[sk]=joystick,gamepad,ovládač +X-KDE-Keywords[sl]=igralna palica,igralni plošček,igralne naprave,krmilnik +X-KDE-Keywords[sr]=joystick,gamepad,controller,џојстик,гејмпад,управљач +X-KDE-Keywords[sr@ijekavian]=joystick,gamepad,controller,џојстик,гејмпад,управљач +X-KDE-Keywords[sr@ijekavianlatin]=joystick,gamepad,controller,džojstik,gejmpad,upravljač +X-KDE-Keywords[sr@latin]=joystick,gamepad,controller,džojstik,gejmpad,upravljač +X-KDE-Keywords[sv]=styrspak,handkontroll,styrning +X-KDE-Keywords[tr]=oyun çubuğu, denetleyici,joystick, oyun oynama kolu +X-KDE-Keywords[uk]=joystick,gamepad,джойстик,геймпад,контролер +X-KDE-Keywords[x-test]=xxjoystick,gamepad,controllerxx +X-KDE-Keywords[zh_CN]=joystick,gamepad,controller,手柄,摇杆,控制器 +X-KDE-Keywords[zh_TW]=joystick,gamepad,controller diff --git a/kcms/hardware/joystick/joystick.h b/kcms/hardware/joystick/joystick.h new file mode 100644 index 0000000000..fedf3a4b75 --- /dev/null +++ b/kcms/hardware/joystick/joystick.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2003 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef _JOYSTICK_H_ +#define _JOYSTICK_H_ + +#define KDE3_SUPPORT +#include +#undef KDE3_SUPPORT + +class JoyWidget; + + +/* on FreeBSD the header already has a struct joystick, so we can't use the same name here, Alex */ +class Joystick: public KCModule +{ + Q_OBJECT + + public: + explicit Joystick(QWidget *parent = 0, const QVariantList &list = QVariantList()); + + virtual void load(); + virtual void defaults(); + + private: + JoyWidget *joyWidget; +}; + +#endif diff --git a/kcms/hardware/joystick/joywidget.cpp b/kcms/hardware/joystick/joywidget.cpp new file mode 100644 index 0000000000..9140213cd0 --- /dev/null +++ b/kcms/hardware/joystick/joywidget.cpp @@ -0,0 +1,418 @@ +/*************************************************************************** + * Copyright (C) 2003,2012 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "joywidget.h" +#include "joydevice.h" +#include "poswidget.h" +#include "caldialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//-------------------------------------------------------------- +static QString PRESSED = I18N_NOOP("PRESSED"); +//-------------------------------------------------------------- + +class TableWidget : public QTableWidget +{ + public: + TableWidget(int row, int col) : QTableWidget(row, col) {} + + virtual QSize sizeHint() const + { + return QSize(150, 100); // return a smaller size than the Qt default(256, 192) + } +}; + +//-------------------------------------------------------------- + +JoyWidget::JoyWidget(QWidget *parent) + : QWidget(parent), idle(0), joydev(0) +{ + QVBoxLayout *mainVbox = new QVBoxLayout(this); + mainVbox->setSpacing(KDialog::spacingHint()); + mainVbox->setMargin(0); + + // create area to show an icon + message if no joystick was detected + { + messageBox = new KMessageWidget(this); + messageBox->setMessageType(KMessageWidget::Error); + messageBox->setCloseButtonVisible(false); + messageBox->hide(); + + mainVbox->addWidget(messageBox); + } + + QHBoxLayout *devHbox = new QHBoxLayout; + devHbox->setSpacing(KDialog::spacingHint()); + devHbox->addWidget(new QLabel(i18n("Device:"))); + devHbox->addWidget(device = new KComboBox(true)); + + device->setInsertPolicy(QComboBox::NoInsert); + KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion); + device->setCompletionObject(kc); + device->setAutoDeleteCompletionObject(true); + connect(device, SIGNAL(activated(QString)), this, SLOT(deviceChanged(QString))); + connect(device, SIGNAL(returnPressed(QString)), this, SLOT(deviceChanged(QString))); + devHbox->setStretchFactor(device, 3); + + QHBoxLayout *hbox = new QHBoxLayout; + hbox->setSpacing(KDialog::spacingHint()); + + mainVbox->addLayout(devHbox); + mainVbox->addLayout(hbox); + + QVBoxLayout *vboxLeft = new QVBoxLayout; + vboxLeft->setSpacing(KDialog::spacingHint()); + vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:"))); + vboxLeft->addWidget(xyPos = new PosWidget); + + vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace"))); + connect(trace, SIGNAL(toggled(bool)), this, SLOT(traceChanged(bool))); + + QVBoxLayout *vboxMid = new QVBoxLayout; + vboxMid->setSpacing(KDialog::spacingHint()); + + QVBoxLayout *vboxRight = new QVBoxLayout; + vboxRight->setSpacing(KDialog::spacingHint()); + + // calculate the column width we need + QFontMetrics fm(font()); + int colWidth = qMax(fm.width(PRESSED), fm.width("-32767")) + 10; // -32767 largest string + + vboxMid->addWidget(new QLabel(i18n("Buttons:"))); + buttonTbl = new TableWidget(0, 1); + buttonTbl->setSelectionMode(QAbstractItemView::NoSelection); + buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); + buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State"))); + buttonTbl->setSortingEnabled(false); + buttonTbl->horizontalHeader()->setClickable(false); + buttonTbl->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + buttonTbl->horizontalHeader()->resizeSection(0, colWidth); + buttonTbl->verticalHeader()->setClickable(false); + vboxMid->addWidget(buttonTbl); + + vboxRight->addWidget(new QLabel(i18n("Axes:"))); + axesTbl = new TableWidget(0, 1); + axesTbl->setSelectionMode(QAbstractItemView::NoSelection); + axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); + axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value"))); + axesTbl->setSortingEnabled(false); + axesTbl->horizontalHeader()->setClickable(false); + axesTbl->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + axesTbl->horizontalHeader()->resizeSection(0, colWidth); + axesTbl->verticalHeader()->setClickable(false); + vboxRight->addWidget(axesTbl); + + hbox->addLayout(vboxLeft); + hbox->addLayout(vboxMid); + hbox->addLayout(vboxRight); + + // calibrate button + calibrate = new QPushButton(i18n("Calibrate")); + connect(calibrate, SIGNAL(clicked()), this, SLOT(calibrateDevice())); + calibrate->setEnabled(false); + + vboxLeft->addStretch(); + vboxLeft->addWidget(calibrate); + + // set up a timer for idle processing of joystick events + idle = new QTimer(this); + connect(idle, SIGNAL(timeout()), this, SLOT(checkDevice())); + + // check which devicefiles we have + init(); +} + +//-------------------------------------------------------------- + +JoyWidget::~JoyWidget() +{ + delete joydev; +} + +//-------------------------------------------------------------- + +void JoyWidget::init() +{ + // check which devicefiles we have + int i; + bool first = true; + char dev[30]; + + device->clear(); + buttonTbl->setRowCount(0); + axesTbl->setRowCount(0); + + for (i = 0; i < 5; i++) // check the first 5 devices + { + sprintf(dev, "/dev/js%d", i); // first look in /dev + JoyDevice *joy = new JoyDevice(dev); + + if ( joy->open() != JoyDevice::SUCCESS ) + { + delete joy; + sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input + joy = new JoyDevice(dev); + + if ( joy->open() != JoyDevice::SUCCESS ) + { + delete joy; + continue; // try next number + } + } + + // we found one + + device->addItem(QString("%1 (%2)").arg(joy->text()).arg(joy->device())); + + // display values for first device + if ( first ) + { + showDeviceProps(joy); // this sets the joy object into this->joydev + first = false; + } + else + delete joy; + } + + /* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */ + if ( device->count() == 0 ) + { + messageBox->show(); + messageBox->setText(QString("%1").arg( + i18n("No joystick device automatically found on this computer.
    " + "Checks were done in /dev/js[0-4] and /dev/input/js[0-4]
    " + "If you know that there is one attached, please enter the correct device file."))); + } +} + +//-------------------------------------------------------------- + +void JoyWidget::traceChanged(bool state) +{ + xyPos->showTrace(state); +} + +//-------------------------------------------------------------- + +void JoyWidget::restoreCurrDev() +{ + if ( !joydev ) // no device open + { + device->setEditText(""); + calibrate->setEnabled(false); + } + else + { + // try to find the current open device in the combobox list + int index = device->findText(joydev->device(), Qt::MatchContains); + + if ( index == -1 ) // the current open device is one the user entered (not in the list) + device->setEditText(joydev->device()); + else + device->setEditText(device->itemText(index)); + } +} + +//-------------------------------------------------------------- + +void JoyWidget::deviceChanged(const QString &dev) +{ + // find "/dev" in given string + int start, stop; + QString devName; + + if ( (start = dev.indexOf("/dev")) == -1 ) + { + KMessageBox::sorry(this, + i18n("The given device name is invalid (does not contain /dev).\n" + "Please select a device from the list or\n" + "enter a device file, like /dev/js0."), i18n("Unknown Device")); + + restoreCurrDev(); + return; + } + + if ( (stop = dev.indexOf(")", start)) != -1 ) // seems to be text selected from our list + devName = dev.mid(start, stop - start); + else + devName = dev.mid(start); + + if ( joydev && (devName == joydev->device()) ) return; // user selected the current device; ignore it + + JoyDevice *joy = new JoyDevice(devName); + JoyDevice::ErrorCode ret = joy->open(); + + if ( ret != JoyDevice::SUCCESS ) + { + KMessageBox::error(this, joy->errText(ret), i18n("Device Error")); + + delete joy; + restoreCurrDev(); + return; + } + + showDeviceProps(joy); +} + +//-------------------------------------------------------------- + +void JoyWidget::showDeviceProps(JoyDevice *joy) +{ + joydev = joy; + + buttonTbl->setRowCount(joydev->numButtons()); + + axesTbl->setRowCount(joydev->numAxes()); + if ( joydev->numAxes() >= 2 ) + { + axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)"))); + axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)"))); + } + + calibrate->setEnabled(true); + idle->start(0); + + // make both tables use the same space for header; this looks nicer + // TODO: Don't know how to do this in Qt4; the following does no longer work + // Probably by setting a sizeHint for every single header item ? + /* + buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(), + axesTbl->verticalHeader()->width())); + axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width()); + */ +} + +//-------------------------------------------------------------- + +void JoyWidget::checkDevice() +{ + if ( !joydev ) return; // no open device yet + + JoyDevice::EventType type; + int number, value; + + if ( !joydev->getEvent(type, number, value) ) + return; + + if ( type == JoyDevice::BUTTON ) + { + if ( ! buttonTbl->item(number, 0) ) + buttonTbl->setItem(number, 0, new QTableWidgetItem()); + + if ( value == 0 ) // button release + buttonTbl->item(number, 0)->setText("-"); + else + buttonTbl->item(number, 0)->setText(PRESSED); + } + + if ( type == JoyDevice::AXIS ) + { + if ( number == 0 ) // x-axis + xyPos->changeX(value); + + if ( number == 1 ) // y-axis + xyPos->changeY(value); + + if ( ! axesTbl->item(number, 0) ) + axesTbl->setItem(number, 0, new QTableWidgetItem()); + + axesTbl->item(number, 0)->setText(QString("%1").arg(int(value))); + } +} + +//-------------------------------------------------------------- + +void JoyWidget::calibrateDevice() +{ + if ( !joydev ) return; // just to be save + + JoyDevice::ErrorCode ret = joydev->initCalibration(); + + if ( ret != JoyDevice::SUCCESS ) + { + KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); + return; + } + + if ( KMessageBox::messageBox(this, KMessageBox::Information, + i18n("Calibration is about to check the precision.

    " + "Please move all axes to their center position and then " + "do not touch the joystick anymore.

    " + "Click OK to start the calibration.
    "), + i18n("Calibration"), + KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok ) + return; + + idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog + + CalDialog dlg(this, joydev); + dlg.calibrate(); + + // user canceled somewhere during calibration, therefore the device is in a bad state + if ( dlg.result() == QDialog::Rejected ) + joydev->restoreCorr(); + + idle->start(0); // continue with event getting +} + +//-------------------------------------------------------------- + +void JoyWidget::resetCalibration() +{ + if ( !joydev ) return; // just to be save + + JoyDevice::ErrorCode ret = joydev->restoreCorr(); + + if ( ret != JoyDevice::SUCCESS ) + { + KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); + } + else + { + KMessageBox::information(this, + i18n("Restored all calibration values for joystick device %1.", joydev->device()), + i18n("Calibration Success")); + } +} + +//-------------------------------------------------------------- + +#include "joywidget.moc" diff --git a/kcms/hardware/joystick/joywidget.h b/kcms/hardware/joystick/joywidget.h new file mode 100644 index 0000000000..74e99d170a --- /dev/null +++ b/kcms/hardware/joystick/joywidget.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2003,2005,2006 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef _JOYWIDGET_H_ +#define _JOYWIDGET_H_ + +#include + +class JoyDevice; + +class PosWidget; +class QLabel; +class QTableWidget; +class QTimer; +class KComboBox; +class KMessageWidget; +class QPushButton; +class QCheckBox; +class QFrame; + +// the widget which displays all buttons, values, etc. +class JoyWidget : public QWidget +{ + Q_OBJECT + + public: + JoyWidget(QWidget *parent = 0); + + ~JoyWidget(); + + // initialize list of possible devices and open the first available + void init(); + + public Q_SLOTS: + // reset calibration values to their value when this KCM was started + void resetCalibration(); + + private Q_SLOTS: + void checkDevice(); + void deviceChanged(const QString &dev); + void traceChanged(bool); + void calibrateDevice(); + + private: + void showDeviceProps(JoyDevice *joy); // fill widgets with given device parameters + void restoreCurrDev(); // restores the content of the combobox to reflect the current open device + + private: + KMessageWidget *messageBox; // in case of no device, show here a message rather than in a dialog + KComboBox *device; + PosWidget *xyPos; + QTableWidget *buttonTbl; + QTableWidget *axesTbl; + QCheckBox *trace; + QPushButton *calibrate; + + QTimer *idle; + + JoyDevice *joydev; +}; + +#endif diff --git a/kcms/hardware/joystick/poswidget.cpp b/kcms/hardware/joystick/poswidget.cpp new file mode 100644 index 0000000000..50b3b0fbfb --- /dev/null +++ b/kcms/hardware/joystick/poswidget.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2003,2005,2006 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "poswidget.h" + +#include + +#define XY_WIDTH 220 +#define MARK_WIDTH 10 +#define MAX_POINTS 500 + +//----------------------------------------------------------------- + +PosWidget::PosWidget(QWidget *parent) + : QWidget(parent), x(0), y(0), trace(false) +{ + setMinimumSize(XY_WIDTH, XY_WIDTH); + setMaximumSize(XY_WIDTH, XY_WIDTH); + + QPalette palette; + palette.setColor(backgroundRole(), Qt::white); + setPalette(palette); +} + +//----------------------------------------------------------------- + +void PosWidget::paintEvent(QPaintEvent *) +{ + QPainter paint(this); + + // draw a frame + paint.drawRect(0, 0, width()-1, height()-1); + paint.setPen(Qt::gray); + + // draw a center grid + paint.drawLine(XY_WIDTH/2, 1, + XY_WIDTH/2, XY_WIDTH - 2); + + paint.drawLine(1, XY_WIDTH/2, + XY_WIDTH - 2, XY_WIDTH/2); + + // draw the trace of previous points + if ( trace ) + { + paint.setPen(Qt::black); + + for (int i = 0; i < tracePoints.count()-2; i++) + paint.drawLine(tracePoints[i], tracePoints[i+1]); + + if ( tracePoints.count() > 0 ) + paint.drawLine(tracePoints[tracePoints.count()-1], QPoint(x, y)); + } + + // draw the current position marker + paint.setPen(Qt::blue); + + paint.drawLine(x - MARK_WIDTH/2, y - MARK_WIDTH/2, + x + MARK_WIDTH/2, y + MARK_WIDTH/2); + + paint.drawLine(x - MARK_WIDTH/2, y + MARK_WIDTH/2, + x + MARK_WIDTH/2, y - MARK_WIDTH/2); +} + +//----------------------------------------------------------------- + +void PosWidget::changeX(int newX) +{ + // transform coordinates from joystick to widget coordinates + newX = int((newX/65535.0)*XY_WIDTH + XY_WIDTH/2); + + if ( x == newX ) return; // avoid unnecessary redraw + + if ( trace ) + { + tracePoints.append(QPoint(x, y)); + if ( tracePoints.count() == MAX_POINTS ) + tracePoints.removeFirst(); + } + + x = newX; + update(); +} + +//----------------------------------------------------------------- + +void PosWidget::changeY(int newY) +{ + // transform coordinates from joystick to widget coordinates + newY = int((newY/65535.0)*XY_WIDTH + XY_WIDTH/2); + + if ( y == newY ) return; // avoid unnecessary redraw + + if ( trace ) + { + tracePoints.append(QPoint(x, y)); + if ( tracePoints.count() == MAX_POINTS ) + tracePoints.removeFirst(); + } + + y = newY; + update(); +} + +//----------------------------------------------------------------- + +void PosWidget::showTrace(bool t) +{ + trace = t; + tracePoints.clear(); + + update(); +} + +//----------------------------------------------------------------- + +#include "poswidget.moc" diff --git a/kcms/hardware/joystick/poswidget.h b/kcms/hardware/joystick/poswidget.h new file mode 100644 index 0000000000..671cfedfdf --- /dev/null +++ b/kcms/hardware/joystick/poswidget.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2003,2005,2006 by Martin Koller * + * kollix@aon.at * + * This file is part of the KDE Control Center Module for Joysticks * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef _POSWIDGET_H_ +#define _POSWIDGET_H_ + +#include +#include +class QPaintEvent; + +/** + Widget to display the joystick-selected (x,y) position +*/ +class PosWidget : public QWidget +{ + Q_OBJECT + + public: + PosWidget(QWidget *parent = 0); + + void changeX(int x); + void changeY(int y); + + // define if a trace of the moving joystick shall be displayed + // changing it will erase all previous marks from the widget + void showTrace(bool t); + + protected: + virtual void paintEvent(QPaintEvent *); + + private: + int x, y; + bool trace; + QList tracePoints; +}; + +#endif diff --git a/kcms/input/AUTHORS b/kcms/input/AUTHORS new file mode 100644 index 0000000000..2ab4353d7c --- /dev/null +++ b/kcms/input/AUTHORS @@ -0,0 +1,7 @@ +Mouse & Keyboard Configuration Modules: + + Pat Dowler (dowler@pt1B1106.FSH.UVic.CA) + +Conversion to kcontrol applet: + + Matthias Hoelzer (hoelzer@physik.uni-wuerzburg.de) diff --git a/kcms/input/CMakeLists.txt b/kcms/input/CMakeLists.txt new file mode 100644 index 0000000000..354f7acf32 --- /dev/null +++ b/kcms/input/CMakeLists.txt @@ -0,0 +1,115 @@ + +add_subdirectory( pics ) + +configure_file (config-kcontrol-input.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kcontrol-input.h ) + +include_directories( ${LIBUSB_INCLUDE_DIR} ) + +if(X11_Xcursor_FOUND) + set( libnoinst_SRCS + xcursor/themepage.cpp + xcursor/thememodel.cpp + xcursor/cursortheme.cpp + xcursor/xcursortheme.cpp + xcursor/previewwidget.cpp + xcursor/itemdelegate.cpp + xcursor/sortproxymodel.cpp + ../krdb/krdb.cpp ) + qt5_wrap_ui( libnoinst_SRCS xcursor/themepage.ui ) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/xcursor/ ) +else() + set( libnoinst_SRCS core/themepage.cpp ) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/core/ ) +endif() + +set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) +qt5_add_dbus_interface(libnoinst_SRCS ${klauncher_xml} klauncher_iface) + + +########### next target ############### + +set(kapplymousetheme_SRCS kapplymousetheme.cpp ) + + +add_executable(kapplymousetheme ${kapplymousetheme_SRCS}) + +target_link_libraries(kapplymousetheme ${X11_Xrender_LIB} ${X11_X11_LIB}) +if (X11_Xcursor_FOUND) + target_link_libraries(kapplymousetheme ${X11_Xcursor_LIB}) +endif () + +install(TARGETS kapplymousetheme ${INSTALL_TARGETS_DEFAULT_ARGS}) + + +########### next target ############### + +set(kcm_input_PART_SRCS mouse.cpp main.cpp logitechmouse.cpp) + + +qt5_wrap_ui(kcm_input_PART_SRCS kmousedlg.ui logitechmouse_base.ui ) +qt5_add_dbus_interface(kcm_input_PART_SRCS ${klauncher_xml} klauncher_iface) + +add_library(kcm_input MODULE ${kcm_input_PART_SRCS}) +set_target_properties(kcm_input PROPERTIES COMPILE_FLAGS -DHAVE_X11=1) + + +target_link_libraries(kcm_input + Qt5::DBus + Qt5::X11Extras + KF5::KCMUtils + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::KDE4Support + ${X11_LIBRARIES} + ${LIBUSB_LIBRARIES} + ) +if (X11_Xcursor_FOUND) + target_link_libraries(kcm_input ${X11_Xcursor_LIB}) +endif () +if (X11_Xfixes_FOUND) + target_link_libraries(kcm_input ${X11_Xfixes_LIB}) +endif () + +install(TARGETS kcm_input DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### next target ############### + +set(kcm_cursortheme_PART_SRCS kcmcursortheme.cpp ${libnoinst_SRCS}) + +add_library(kcm_cursortheme MODULE ${kcm_cursortheme_PART_SRCS}) +set_target_properties(kcm_cursortheme PROPERTIES COMPILE_FLAGS -DHAVE_X11=1) + + +target_link_libraries(kcm_cursortheme + Qt5::DBus + Qt5::X11Extras + KF5::Archive + KF5::KCMUtils + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::KDE4Support + KF5::NewStuff + ${X11_LIBRARIES} + ${XCB_XCB_LIBRARY} +) + +if (X11_Xcursor_FOUND) + target_link_libraries(kcm_cursortheme ${X11_Xcursor_LIB}) +endif () +if (X11_Xfixes_FOUND) + target_link_libraries(kcm_cursortheme ${X11_Xfixes_LIB}) +endif () + +install(TARGETS kcm_cursortheme DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES mouse.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES cursortheme.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES cursor_large_black.pcf.gz cursor_large_white.pcf.gz cursor_small_white.pcf.gz DESTINATION ${DATA_INSTALL_DIR}/kcminput ) +install( FILES xcursor/xcursor.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) + diff --git a/kcms/input/ChangeLog b/kcms/input/ChangeLog new file mode 100644 index 0000000000..2d9b8d545e --- /dev/null +++ b/kcms/input/ChangeLog @@ -0,0 +1,11 @@ +2002-07-01 Fabian Wolf + * added option to select a white cursor + +2000-03-14 David Faure + + * mouse.cpp: Added global settings for SC/DC/AutoSelect/ChangeCursor + * mousedefaults.h: New file, to store default values + +1998-11-30 Alex Zepeda + + * Makefile.am: Move all the icons into pics/ && pics/mini/ diff --git a/kcms/input/Messages.sh b/kcms/input/Messages.sh new file mode 100644 index 0000000000..b039def2e6 --- /dev/null +++ b/kcms/input/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find -name \*.ui` >> rc.cpp || exit 11 +$XGETTEXT *.cpp */*.cpp -o $podir/kcminput.pot +rm -f rc.cpp diff --git a/kcms/input/config-kcontrol-input.h.cmake b/kcms/input/config-kcontrol-input.h.cmake new file mode 100644 index 0000000000..ce817d8e5f --- /dev/null +++ b/kcms/input/config-kcontrol-input.h.cmake @@ -0,0 +1,3 @@ +/* Defined if you have libusb */ +#cmakedefine HAVE_LIBUSB 1 + diff --git a/kcms/input/consoleUserPerms b/kcms/input/consoleUserPerms new file mode 100755 index 0000000000..015df642f1 --- /dev/null +++ b/kcms/input/consoleUserPerms @@ -0,0 +1,42 @@ +#!/bin/bash +# +# /etc/hotplug/usb/consoleUserPerms +# +# Sets up newly plugged in USB device so that the user who owns +# the console according to pam_console can access it from user space +# +# Note that for this script to work, you'll need all of the following: +# a) a line in the file /etc/hotplug/usb.usermap or another usermap file +# in /etc/hotplug/usb/ that corresponds to the device you are using. +# b) a setup using pam_console creates the respective lock files +# containing the name of the respective user. You can check for that +# by executing "echo `cat /var/{run,lock}/console.lock`" and +# verifying the appropriate user is mentioned somewhere there. +# c) a Linux kernel supporting hotplug and usbdevfs +# d) the hotplug package (http://linux-hotplug.sourceforge.net/) +# +# In the usermap file, the first field "usb module" should be named +# "consoleUserPerms" to invoke this script. +# + +if [ "${ACTION}" = "add" ] && [ -f "${DEVICE}" ] +then + # New code, using lock files instead of copying /dev/console permissions + # This also works with non-kdm logins (e.g. on a virtual terminal) + # Idea and code from Nalin Dahyabhai + if [ -f /var/run/console.lock ] + then + CONSOLEOWNER=`cat /var/run/console.lock` + elif [ -f /var/lock/console.lock ] + then + CONSOLEOWNER=`cat /var/lock/console.lock` + else + CONSOLEOWNER= + fi + if [ -n "$CONSOLEOWNER" ] + then + chmod 0000 "${DEVICE}" + chown "$CONSOLEOWNER" "${DEVICE}" + chmod 0600 "${DEVICE}" + fi +fi diff --git a/kcms/input/core/bitmaps.h b/kcms/input/core/bitmaps.h new file mode 100644 index 0000000000..1a8e272e68 --- /dev/null +++ b/kcms/input/core/bitmaps.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2003 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __BITMAPS_H +#define __BITMAPS_H + + +static const char* const arrow_large_black_xpm[] = { +"24 24 3 1", +"# c None", +"a c #000000", +". c #ffffff", +"#####..#################", +"#####.a.################", +"#####.aa.###############", +"#####.aaa.##############", +"#####.aaaa.#############", +"#####.aaaaa.############", +"#####.aaaaaa.###########", +"#####.aaaaaaa.##########", +"#####.aaaaaaaa.#########", +"#####.aaaaaaaaa.########", +"#####.aaaaaaaaaa.#######", +"#####.aaaaaaaaaaa.######", +"#####.aaaaaaaaaaaa.#####", +"#####.aaaaaaaa....######", +"#####.aaaaaaa.##########", +"#####.aaa.aaaa.#########", +"#####.aa.#.aaa.#########", +"#####.a.##.aaaa.########", +"#####..####.aaa.########", +"###########.aaaa.#######", +"############.aaa.#######", +"############.aaa.#######", +"#############...########", +"########################"}; + + +static const char* const arrow_small_black_xpm[]={ +"24 24 3 1", +"# c None", +"a c #000000", +". c #ffffff", +"########################", +"########################", +"########################", +"########################", +"#######..###############", +"#######.a.##############", +"#######.aa.#############", +"#######.aaa.############", +"#######.aaaa.###########", +"#######.aaaaa.##########", +"#######.aaaaaa.#########", +"#######.aaaaaaa.########", +"#######.aaaaaaaa.#######", +"#######.aaaaa....#######", +"#######.aa.aa.##########", +"#######.a.#.aa.#########", +"#######..##.aa.#########", +"############.aa.########", +"############.aa.########", +"#############..#########", +"########################", +"########################", +"########################", +"########################"}; + + +static const char* const arrow_large_white_xpm[] = { +"24 24 3 1", +"# c None", +"a c #ffffff", +". c #000000", +"#####..#################", +"#####.a.################", +"#####.aa.###############", +"#####.aaa.##############", +"#####.aaaa.#############", +"#####.aaaaa.############", +"#####.aaaaaa.###########", +"#####.aaaaaaa.##########", +"#####.aaaaaaaa.#########", +"#####.aaaaaaaaa.########", +"#####.aaaaaaaaaa.#######", +"#####.aaaaaaaaaaa.######", +"#####.aaaaaaaaaaaa.#####", +"#####.aaaaaaaa....######", +"#####.aaaaaaa.##########", +"#####.aaa.aaaa.#########", +"#####.aa.#.aaa.#########", +"#####.a.##.aaaa.########", +"#####..####.aaa.########", +"###########.aaaa.#######", +"############.aaa.#######", +"############.aaa.#######", +"#############...########", +"########################"}; + + +static const char* const arrow_small_white_xpm[]={ +"24 24 3 1", +"# c None", +"a c #ffffff", +". c #000000", +"########################", +"########################", +"########################", +"########################", +"#######..###############", +"#######.a.##############", +"#######.aa.#############", +"#######.aaa.############", +"#######.aaaa.###########", +"#######.aaaaa.##########", +"#######.aaaaaa.#########", +"#######.aaaaaaa.########", +"#######.aaaaaaaa.#######", +"#######.aaaaa....#######", +"#######.aa.aa.##########", +"#######.a.#.aa.#########", +"#######..##.aa.#########", +"############.aa.########", +"############.aa.########", +"#############..#########", +"########################", +"########################", +"########################", +"########################"}; + +#endif diff --git a/kcms/input/core/themepage.cpp b/kcms/input/core/themepage.cpp new file mode 100644 index 0000000000..5e8ffc42d3 --- /dev/null +++ b/kcms/input/core/themepage.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2003 Fredrik Höglund + * + * Based on the large cursor code written by Rik Hemsley, + * Copyright (c) 2000 Rik Hemsley + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "themepage.h" +#include +#include "themepage.moc" + +#include "bitmaps.h" + + + +ThemePage::ThemePage( QWidget* parent, const char* name ) + : QWidget( parent ) +{ + setObjectName(name); + QBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( KDialog::marginHint() ); + layout->setSpacing( KDialog::spacingHint() ); + + layout->addWidget(new QLabel( i18n("Select the cursor theme you want to use:"), this )); + + // Create the theme list view + listview = new QTreeWidget( this ); + QStringList lstHeader; + lstHeader<setHeaderLabels( lstHeader ); + listview->setSelectionMode( QAbstractItemView::SingleSelection ); + listview->setRootIsDecorated( false ); + connect( listview, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)) + , this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)) ); + layout->addWidget( listview ); + insertThemes(); +} + + +ThemePage::~ThemePage() +{ +} + + +void ThemePage::selectionChanged( QTreeWidgetItem*current,QTreeWidgetItem*previous ) +{ + Q_UNUSED( previous ); + selectedTheme = current->data( 0, Qt::UserRole + 1 ).toString(); + emit changed( selectedTheme != currentTheme ); +} + + +void ThemePage::save() +{ + if ( currentTheme == selectedTheme ) + return; + + bool whiteCursor = selectedTheme.right( 5 ) == "White"; + bool largeCursor = selectedTheme.left( 5 ) == "Large"; + + KConfig config( "kcminputrc" ); + KConfigGroup c( &config, "Mouse" ); + c.writeEntry( "LargeCursor", largeCursor ); + c.writeEntry( "WhiteCursor", whiteCursor ); + + currentTheme = selectedTheme; + + fixCursorFile(); + + KMessageBox::information( this, i18n("You have to restart KDE for these " + "changes to take effect."), i18n("Cursor Settings Changed"), + "CursorSettingsChanged" ); +} + + +void ThemePage::load() +{ + bool largeCursor, whiteCursor; + + KConfig config( "kcminputrc" ); + KConfigGroup c( &config, "Mouse" ); + largeCursor = c.readEntry( "LargeCursor", false); + whiteCursor = c.readEntry( "WhiteCursor", false); + + if ( largeCursor ) + currentTheme = whiteCursor ? "LargeWhite" : "LargeBlack"; + else + currentTheme = whiteCursor ? "SmallWhite" : "SmallBlack"; + + selectedTheme = currentTheme; + for ( int i = 0;i topLevelItemCount();++i ) + { + QTreeWidgetItem *item = listview->topLevelItem( i ); + if ( item && item->data(0, Qt::UserRole + 1 ) == currentTheme ) + { + listview->setCurrentItem( item ); + } + } +} + + +void ThemePage::defaults() +{ + currentTheme = selectedTheme = "SmallBlack"; + for ( int i = 0;i topLevelItemCount();++i ) + { + QTreeWidgetItem *item = listview->topLevelItem( i ); + if ( item && item->data(0, Qt::UserRole + 1 ) == currentTheme ) + { + listview->setCurrentItem( item ); + } + } +} + + +void ThemePage::insertThemes() +{ + QList lstChildren; + QTreeWidgetItem *item = new QTreeWidgetItem(listview ); + + item->setData( 0, Qt::DisplayRole,i18n("Small black") ); + item->setData( 1, Qt::DisplayRole, i18n("Small black cursors") ); + item->setData( 0, Qt::DecorationRole, QPixmap( arrow_small_black_xpm ) ); + item->setData( 0, Qt::UserRole + 1,"SmallBlack" ); + lstChildren<setData( 0, Qt::DisplayRole, i18n("Large black") ); + item->setData( 1, Qt::DisplayRole, i18n("Large black cursors") ); + item->setData( 0, Qt::DecorationRole, QPixmap( arrow_large_black_xpm ) ); + item->setData( 0, Qt::UserRole + 1,"LargeBlack" ); + lstChildren<setData( 0, Qt::DisplayRole, i18n("Small white") ); + item->setData( 1, Qt::DisplayRole, i18n("Small white cursors") ); + item->setData( 0, Qt::DecorationRole, QPixmap(arrow_small_white_xpm ) ); + item->setData( 0, Qt::UserRole + 1,"SmallWhite" ); + lstChildren<setData( 0, Qt::DisplayRole, i18n("Large white") ); + item->setData( 1, Qt::DisplayRole, i18n("Large white cursors") ); + item->setData( 0, Qt::DecorationRole, QPixmap( arrow_large_white_xpm ) ); + item->setData( 0, Qt::UserRole + 1, "LargeWhite" ); + lstChildren<addTopLevelItems( lstChildren ); +} + + +void ThemePage::fixCursorFile() +{ + // Make sure we have the 'font' resource dir registered and can find the + // override dir. + // + // Next, if the user wants large cursors, copy the font + // cursor_large.pcf.gz to (localkdedir)/share/fonts/override/cursor.pcf.gz. + // Else remove the font cursor.pcf.gz from (localkdedir)/share/fonts/override. + // + // Run mkfontdir to update fonts.dir in that dir. + + KGlobal::dirs()->addResourceType( "font", 0, "share/fonts/" ); + KIO::mkdir( QDir::homePath() + "/.fonts/kde-override" ); + QString overrideDir = QDir::homePath() + "/.fonts/kde-override/"; + + KUrl installedFont; + installedFont.setPath( overrideDir + "cursor.pcf.gz" ); + + if ( currentTheme == "SmallBlack" ) + KIO::NetAccess::del( installedFont, this ); + else { + KUrl source; + + if ( currentTheme == "LargeBlack" ) + source.setPath( KStandardDirs::locate("data", "kcminput/cursor_large_black.pcf.gz") ); + else if ( currentTheme == "LargeWhite" ) + source.setPath( KStandardDirs::locate("data", "kcminput/cursor_large_white.pcf.gz") ); + else if ( currentTheme == "SmallWhite" ) + source.setPath( KStandardDirs::locate("data", "kcminput/cursor_small_white.pcf.gz") ); + + KIO::Job* job = KIO::file_copy( source, installedFont, -1, KIO::Overwrite ); + job->exec(); + } + + QString cmd = KGlobal::dirs()->findExe( "mkfontdir" ); + if ( !cmd.isEmpty() ) + { + KProcess p; + p << cmd << overrideDir; + p.execute(); + } +} + +// vim: set noet ts=4 sw=4: diff --git a/kcms/input/core/themepage.h b/kcms/input/core/themepage.h new file mode 100644 index 0000000000..b264422add --- /dev/null +++ b/kcms/input/core/themepage.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __THEMEPAGE_H +#define __THEMEPAGE_H + +class QTreeWidget; +class QTreeWidgetItem; + +class ThemePage : public QWidget +{ + Q_OBJECT + + public: + explicit ThemePage( QWidget* parent = 0, const char* name = 0 ); + ~ThemePage(); + + // Called by the KCM + void save(); + void load(); + void defaults(); + + Q_SIGNALS: + void changed( bool ); + + private Q_SLOTS: + void selectionChanged( QTreeWidgetItem *,QTreeWidgetItem* ); + + private: + void insertThemes(); + void fixCursorFile(); + + QTreeWidget *listview; + QString currentTheme, selectedTheme; +}; + +#endif // __THEMEPAGE_H + +// vim: set noet ts=4 sw=4: diff --git a/kcms/input/cursor_large.bdf b/kcms/input/cursor_large.bdf new file mode 100644 index 0000000000..241189286e --- /dev/null +++ b/kcms/input/cursor_large.bdf @@ -0,0 +1,4447 @@ +STARTFONT 2.1 +FONT cursor +SIZE 47 78 78 +FONTBOUNDINGBOX 45 42 -21 -23 +STARTPROPERTIES 13 +COPYRIGHT "(C) 2000 Rik Hemsley " +POINT_SIZE 470 +FONT "cursor" +WEIGHT 10 +RESOLUTION 107 +RESOLUTION_X 78 +RESOLUTION_Y 78 +X_HEIGHT -1 +QUAD_WIDTH 20 +DEFAULT_CHAR 0 +FONT_ASCENT 23 +FONT_DESCENT 24 +_XMBDFED_INFO "Edited with xmbdfed 4.3." +ENDPROPERTIES +CHARS 154 +STARTCHAR D +ENCODING 0 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 1 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C002 +ENCODING 2 +SWIDTH 1139 0 +DWIDTH 58 0 +BBX 12 21 -10 -22 +BITMAP +0010 +0030 +0070 +00F0 +01F0 +03F0 +07F0 +0FF0 +1FF0 +3FF0 +7FF0 +FFF0 +0FF0 +07F0 +0F70 +0E30 +1E10 +1C00 +3C00 +3800 +3800 +ENDCHAR +STARTCHAR C003 +ENCODING 3 +SWIDTH 1414 0 +DWIDTH 72 0 +BBX 14 23 -11 -23 +BITMAP +000C +001C +003C +007C +00FC +01FC +03FC +07FC +0FFC +1FFC +3FFC +7FFC +FFFC +7FFC +07FC +0FFC +0FBC +1F9C +1F0C +3F00 +3E00 +3E00 +1C00 +ENDCHAR +STARTCHAR C004 +ENCODING 4 +SWIDTH 137 0 +DWIDTH 7 0 +BBX 11 15 -4 -2 +BITMAP +FFE0 +FFE0 +FFE0 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +FFE0 +7FC0 +3F80 +1F00 +0E00 +0400 +ENDCHAR +STARTCHAR C004 +ENCODING 5 +SWIDTH 176 0 +DWIDTH 9 0 +BBX 15 17 -6 -3 +BITMAP +3FF8 +7FFC +7FFC +7FFC +3FF8 +07C0 +07C0 +07C0 +07C0 +FFFE +7FFC +3FF8 +1FF0 +0FE0 +07C0 +0380 +0100 +ENDCHAR +STARTCHAR C004 +ENCODING 6 +SWIDTH 137 0 +DWIDTH 7 0 +BBX 11 15 -4 -16 +BITMAP +0400 +0E00 +1F00 +3F80 +7FC0 +FFE0 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +FFE0 +FFE0 +FFE0 +ENDCHAR +STARTCHAR C004 +ENCODING 7 +SWIDTH 176 0 +DWIDTH 9 0 +BBX 15 17 -6 -17 +BITMAP +0100 +0380 +07C0 +0FE0 +1FF0 +3FF8 +7FFC +FFFE +07C0 +07C0 +07C0 +07C0 +3FF8 +7FFC +7FFC +7FFC +3FF8 +ENDCHAR +STARTCHAR D +ENCODING 8 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 9 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 10 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 11 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C014 +ENCODING 12 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 21 0 -3 +BITMAP +E00000 +E00000 +E00000 +E00100 +E00380 +E087C0 +E0CF80 +E0FF00 +E0FE00 +E0FC00 +E0FC00 +E0FE00 +E0FF00 +E00000 +E00000 +E00000 +E00000 +E00000 +FFFFF8 +FFFFF8 +FFFFF8 +ENDCHAR +STARTCHAR C014 +ENCODING 13 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 23 23 -1 -4 +BITMAP +700000 +F80000 +F80000 +F80080 +F881C0 +F8C3E0 +F8E7F0 +F8FFE0 +F8FFC0 +F8FF80 +F8FF00 +F8FF00 +F8FF80 +F8FFC0 +F8FFE0 +F80000 +F80000 +F80000 +FFFFFC +FFFFFE +FFFFFE +FFFFFE +FFFFFC +ENDCHAR +STARTCHAR C014 +ENCODING 14 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 21 -18 -3 +BITMAP +000038 +000038 +000038 +040038 +0E0038 +1F0838 +0F9838 +07F838 +03F838 +01F838 +01F838 +03F838 +07F838 +000038 +000038 +000038 +000038 +000038 +FFFFF8 +FFFFF8 +FFFFF8 +ENDCHAR +STARTCHAR C014 +ENCODING 15 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 23 23 -19 -4 +BITMAP +00001C +00003E +00003E +02003E +07023E +0F863E +1FCE3E +0FFE3E +07FE3E +03FE3E +01FE3E +01FE3E +03FE3E +07FE3E +0FFE3E +00003E +00003E +00003E +7FFFFE +FFFFFE +FFFFFE +FFFFFE +7FFFFE +ENDCHAR +STARTCHAR C020 +ENCODING 16 +SWIDTH 216 0 +DWIDTH 11 0 +BBX 19 20 -8 -3 +BITMAP +00E000 +00E000 +00E000 +00E000 +00E000 +00E000 +00E000 +0FFE00 +07FC00 +03F800 +01F000 +00E000 +004000 +000000 +000000 +000000 +000000 +FFFFE0 +FFFFE0 +FFFFE0 +ENDCHAR +STARTCHAR C020 +ENCODING 17 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 21 22 -9 -4 +BITMAP +007000 +00F800 +00F800 +00F800 +00F800 +00F800 +00F800 +1FFFC0 +0FFF80 +07FF00 +03FE00 +01FC00 +00F800 +007000 +002000 +000000 +000000 +7FFFF0 +FFFFF8 +FFFFF8 +FFFFF8 +7FFFF0 +ENDCHAR +STARTCHAR D +ENCODING 18 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 19 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 20 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 21 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C026 +ENCODING 22 +SWIDTH 137 0 +DWIDTH 7 0 +BBX 11 20 -4 -21 +BITMAP +0400 +0400 +0E00 +0E00 +1F00 +1F00 +3F80 +3F80 +7FC0 +7FC0 +FFE0 +CE60 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0400 +ENDCHAR +STARTCHAR C026 +ENCODING 23 +SWIDTH 157 0 +DWIDTH 8 0 +BBX 13 22 -5 -22 +BITMAP +0200 +0700 +0700 +0F80 +0F80 +1FC0 +1FC0 +3FE0 +3FE0 +7FF0 +7FF0 +FFF8 +FFF8 +6FB0 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0700 +0200 +ENDCHAR +STARTCHAR D +ENCODING 24 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 25 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 26 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 27 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 28 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 29 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR quotedbl +ENCODING 30 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 24 23 -10 -13 +BITMAP +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +002800 +FFC7FF +FF83FF +FFC7FF +002800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +ENDCHAR +STARTCHAR numbersign +ENCODING 31 +SWIDTH 294 0 +DWIDTH 15 0 +BBX 26 25 -11 -14 +BITMAP +001C0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +7FF7FF80 +FFE3FFC0 +FFC1FFC0 +FFE3FFC0 +7FF7FF80 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +001C0000 +ENDCHAR +STARTCHAR D +ENCODING 32 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 33 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 24 23 -10 -13 +BITMAP +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +002800 +FFC7FF +FF83FF +FFC7FF +002800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +ENDCHAR +STARTCHAR numbersign +ENCODING 35 +SWIDTH 294 0 +DWIDTH 15 0 +BBX 26 25 -11 -14 +BITMAP +001C0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +7FF7FF80 +FFE3FFC0 +FFC1FFC0 +FFE3FFC0 +7FF7FF80 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +001C0000 +ENDCHAR +STARTCHAR D +ENCODING 36 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 37 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 38 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 39 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 40 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 41 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR asterisk +ENCODING 42 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 15 23 -6 -13 +BITMAP +0100 +0380 +07C0 +0FE0 +1FF0 +3FF8 +7BBC +F39E +638C +0380 +0380 +0380 +0380 +0380 +E38C +F39E +7BBC +3FF8 +1FF0 +0FE0 +07C0 +0380 +0100 +ENDCHAR +STARTCHAR plus +ENCODING 43 +SWIDTH 196 0 +DWIDTH 10 0 +BBX 17 25 -7 -14 +BITMAP +008000 +01C000 +03E000 +07F000 +0FF800 +1FFC00 +3FFE00 +7FFF00 +FFFF80 +7BEF00 +33E600 +03E000 +03E000 +03E000 +33E600 +7BEF00 +FFFF80 +7FFF00 +3FFE00 +1FFC00 +0FF800 +07F000 +03E000 +01C000 +008000 +ENDCHAR +STARTCHAR C002 +ENCODING 44 +SWIDTH 1139 0 +DWIDTH 58 0 +BBX 12 21 -10 -22 +BITMAP +0010 +0030 +0070 +00F0 +01F0 +03F0 +07F0 +0FF0 +1FF0 +3FF0 +7FF0 +FFF0 +0FF0 +07F0 +0F70 +0E30 +1E10 +1C00 +3C00 +3800 +3800 +ENDCHAR +STARTCHAR C003 +ENCODING 45 +SWIDTH 1414 0 +DWIDTH 72 0 +BBX 14 23 -11 -23 +BITMAP +000C +001C +003C +007C +00FC +01FC +03FC +07FC +0FFC +1FFC +3FFC +7FFC +FFFC +7FFC +07FC +0FFC +0FBC +1F9C +1F0C +3F00 +3E00 +3E00 +1C00 +ENDCHAR +STARTCHAR C002 +ENCODING 46 +SWIDTH 1139 0 +DWIDTH 58 0 +BBX 12 21 -10 -22 +BITMAP +0010 +0030 +0070 +00F0 +01F0 +03F0 +07F0 +0FF0 +1FF0 +3FF0 +7FF0 +FFF0 +0FF0 +07F0 +0F70 +0E30 +1E10 +1C00 +3C00 +3800 +3800 +ENDCHAR +STARTCHAR C003 +ENCODING 47 +SWIDTH 1414 0 +DWIDTH 72 0 +BBX 14 23 -11 -23 +BITMAP +000C +001C +003C +007C +00FC +01FC +03FC +07FC +0FFC +1FFC +3FFC +7FFC +FFFC +7FFC +07FC +0FFC +0FBC +1F9C +1F0C +3F00 +3E00 +3E00 +1C00 +ENDCHAR +STARTCHAR D +ENCODING 48 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 49 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR 2 +ENCODING 50 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 23 -9 -13 +BITMAP +00F800 +C3FE00 +E7FF80 +FFC3C0 +DF00E0 +CE0060 +C60020 +C30000 +FF8000 +FF8000 +000000 +000000 +000000 +000FF8 +000FF8 +000618 +200318 +300398 +3C07D8 +1E1FF8 +0FFF38 +03FE18 +00F800 +ENDCHAR +STARTCHAR 2 +ENCODING 51 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 23 -9 -13 +BITMAP +00F800 +C3FE00 +E7FF80 +FFC3C0 +FF00E0 +FE0060 +FE0020 +FF0000 +FF8000 +FF8000 +000000 +000000 +000000 +000FF8 +000FF8 +0007F8 +2003F8 +3003F8 +3C07F8 +1E1FF8 +0FFF38 +03FE18 +00F800 +ENDCHAR +STARTCHAR 4 +ENCODING 52 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 27 27 -12 -15 +BITMAP +00040000 +000E0000 +001F0000 +003F8000 +007FC000 +007FC000 +000E0000 +000E0000 +000E0000 +0C0E0600 +1C0E0700 +3C0E0780 +7FFFFFC0 +FFFFFFE0 +7FFFFFC0 +3C0E0780 +1C0E0700 +0C0E0600 +000E0000 +000E0000 +000E0000 +007FC000 +007FC000 +003F8000 +001F0000 +000E0000 +00040000 +ENDCHAR +STARTCHAR 4 +ENCODING 53 +SWIDTH 314 0 +DWIDTH 16 0 +BBX 29 29 -13 -16 +BITMAP +00020000 +00070000 +000F8000 +001FC000 +003FE000 +007FF000 +007FF000 +003FE000 +000F8000 +060F8300 +0F0F8780 +1F0F87C0 +3FFFFFE0 +7FFFFFF0 +FFFFFFF8 +7FFFFFF0 +3FFFFFE0 +1F0F87C0 +0F0F8780 +060F8300 +000F8000 +003FE000 +007FF000 +007FF000 +003FE000 +001FC000 +000F8000 +00070000 +00020000 +ENDCHAR +STARTCHAR D +ENCODING 54 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 55 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 56 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 57 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR less +ENCODING 58 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 23 21 -21 -21 +BITMAP +001FFC +00FFFE +01E006 +0307FC +060FF8 +0C0060 +0C07C0 +0C0FC0 +0C0060 +1E07C0 +330FC0 +6180C0 +C0C780 +C06F00 +603C00 +318C00 +199800 +0C3000 +066000 +03C000 +018000 +ENDCHAR +STARTCHAR equal +ENCODING 59 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 23 21 -21 -21 +BITMAP +001FFC +00FFFE +01FFFE +03FFFC +07FFF8 +0FFFE0 +0FFFC0 +0FFFC0 +0FFFE0 +1FFFC0 +3FFFC0 +7FFFC0 +FFFF80 +FFFF00 +7FFC00 +3FFC00 +1FF800 +0FF000 +07E000 +03C000 +018000 +ENDCHAR +STARTCHAR less +ENCODING 60 +SWIDTH 962 0 +DWIDTH 49 0 +BBX 23 21 1 -21 +BITMAP +7FF000 +FFFE00 +C00F00 +7FC180 +3FE0C0 +0C0060 +07C060 +07E060 +0C0060 +07C0F0 +07E198 +06030C +03C606 +01EC06 +00780C +006318 +003330 +001860 +000CC0 +000780 +000300 +ENDCHAR +STARTCHAR equal +ENCODING 61 +SWIDTH 923 0 +DWIDTH 47 0 +BBX 23 21 1 -21 +BITMAP +7FF000 +FFFE00 +FFFF00 +7FFF80 +3FFFC0 +0FFFE0 +07FFE0 +07FFE0 +0FFFE0 +07FFF0 +07FFF8 +07FFFC +03FFFE +01FFFE +007FFC +007FF8 +003FF0 +001FE0 +000FC0 +000780 +000300 +ENDCHAR +STARTCHAR D +ENCODING 62 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 63 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 64 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 65 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 66 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 67 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C020 +ENCODING 70 +SWIDTH 216 0 +DWIDTH 11 0 +BBX 20 19 0 -11 +BITMAP +E00000 +E00000 +E00000 +E00000 +E00800 +E01800 +E03800 +E07800 +E0FFF0 +E1FFF0 +E0FFF0 +E07800 +E03800 +E01800 +E00800 +E00000 +E00000 +E00000 +E00000 +ENDCHAR +STARTCHAR C020 +ENCODING 71 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 22 21 -1 -12 +BITMAP +700000 +F80000 +F80000 +F80200 +F80600 +F80E00 +F81E00 +F83E00 +F87FF8 +F8FFFC +F9FFFC +F8FFFC +F87FF8 +F83E00 +F81E00 +F80E00 +F80600 +F80200 +F80000 +F80000 +700000 +ENDCHAR +STARTCHAR D +ENCODING 72 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 73 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 74 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 75 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 76 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 77 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 78 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 79 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 80 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 81 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 82 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 83 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 84 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 85 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +D800 +CC00 +CC00 +6600 +6600 +3300 +3300 +1980 +1980 +0CC0 +0CC0 +0660 +0660 +03F0 +0330 +01F0 +00E0 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FC00 +7E00 +7E00 +3F00 +3F00 +1F80 +1F80 +0FC0 +0FC0 +07E0 +07E0 +03F0 +03F0 +01F0 +00E0 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 353 0 +DWIDTH 18 0 +BBX 17 17 1 -2 +BITMAP +003F80 +007F00 +00FC00 +01F000 +03C000 +07FE00 +01FC00 +03F000 +07C000 +0F0000 +1FF000 +07E000 +0F8000 +1E0000 +380000 +600000 +800000 +ENDCHAR +STARTCHAR X +ENCODING 89 +SWIDTH 392 0 +DWIDTH 20 0 +BBX 20 19 0 -3 +BITMAP +001FF0 +003FE0 +007FC0 +00FF80 +01FE00 +03FFC0 +07FF80 +0FFF00 +03FE00 +07F800 +0FFE00 +1FFC00 +3FF800 +0FF000 +0FC000 +3F0000 +7C0000 +F00000 +C00000 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 15 15 -6 -9 +BITMAP +0380 +0380 +0380 +0380 +0380 +0380 +FFFE +FFFE +FFFE +0380 +0380 +0380 +0380 +0380 +0380 +ENDCHAR +STARTCHAR Z +ENCODING 91 +SWIDTH 196 0 +DWIDTH 10 0 +BBX 17 17 -7 -10 +BITMAP +01C000 +03E000 +03E000 +03E000 +03E000 +03E000 +7FFF00 +FFFF80 +FFFF80 +FFFF80 +7FFF00 +03E000 +03E000 +03E000 +03E000 +03E000 +01C000 +ENDCHAR +STARTCHAR D +ENCODING 92 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 93 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C002 +ENCODING 94 +SWIDTH 1139 0 +DWIDTH 58 0 +BBX 12 21 -10 -22 +BITMAP +0010 +0030 +0070 +00F0 +01F0 +03F0 +07F0 +0FF0 +1FF0 +3FF0 +7FF0 +FFF0 +0FF0 +07F0 +0F70 +0E30 +1E10 +1C00 +3C00 +3800 +3800 +ENDCHAR +STARTCHAR C003 +ENCODING 95 +SWIDTH 1414 0 +DWIDTH 72 0 +BBX 14 23 -11 -23 +BITMAP +000C +001C +003C +007C +00FC +01FC +03FC +07FC +0FFC +1FFC +3FFC +7FFC +FFFC +7FFC +07FC +0FFC +0FBC +1F9C +1F0C +3F00 +3E00 +3E00 +1C00 +ENDCHAR +STARTCHAR C020 +ENCODING 96 +SWIDTH 216 0 +DWIDTH 11 0 +BBX 20 19 -17 -11 +BITMAP +000070 +000070 +000070 +000070 +010070 +018070 +01C070 +01E070 +FFF070 +FFF870 +FFF070 +01E070 +01C070 +018070 +010070 +000070 +000070 +000070 +000070 +ENDCHAR +STARTCHAR C020 +ENCODING 97 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 22 21 -18 -12 +BITMAP +000038 +00007C +00007C +01007C +01807C +01C07C +01E07C +01F07C +7FF87C +FFFC7C +FFFE7C +FFFC7C +7FF87C +01F07C +01E07C +01C07C +01807C +01007C +00007C +00007C +000038 +ENDCHAR +STARTCHAR D +ENCODING 98 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 99 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 100 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 101 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 102 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 103 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 104 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 105 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 157 0 +DWIDTH 8 0 +BBX 13 20 -5 -2 +BITMAP +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +FFF8 +7FF0 +3FE0 +1FC0 +0F80 +0700 +0200 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 176 0 +DWIDTH 9 0 +BBX 15 22 -6 -3 +BITMAP +06C0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +7FFC +FFFE +7FFC +3FF8 +1FF0 +0FE0 +07C0 +0380 +0100 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 25 13 -12 -7 +BITMAP +02002000 +06003000 +0E003800 +1E003C00 +3FFFFE00 +7FFFFF00 +FE003F80 +7FFFFF00 +3FFFFE00 +1E003C00 +0E003800 +06003000 +02002000 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 274 0 +DWIDTH 14 0 +BBX 27 15 -13 -8 +BITMAP +01001000 +03803800 +07803C00 +0F803E00 +1FFFFF00 +3FFFFF80 +7FFFFFC0 +FFFFFFE0 +7FFFFFC0 +3FFFFF80 +1FFFFF00 +0F803E00 +07803C00 +03803800 +01001000 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 412 0 +DWIDTH 21 0 +BBX 20 13 1 -8 +BITMAP +020000 +060000 +0E0000 +1E0000 +3FFFF0 +7FFFF0 +FE0000 +7FFFF0 +3FFFF0 +1E0000 +0E0000 +060000 +020000 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 22 15 0 -9 +BITMAP +010000 +038000 +078000 +0F8000 +1FFFF8 +3FFFFC +7FFFFC +FFFFF8 +7FFFFC +3FFFFC +1FFFF8 +0F8000 +078000 +038000 +010000 +ENDCHAR +STARTCHAR n +ENCODING 112 +SWIDTH 412 0 +DWIDTH 21 0 +BBX 20 13 -18 -8 +BITMAP +000400 +000600 +000700 +000780 +FFFFC0 +FFFFE0 +0007F0 +FFFFE0 +FFFFC0 +000780 +000700 +000600 +000400 +ENDCHAR +STARTCHAR o +ENCODING 113 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 22 15 -19 -9 +BITMAP +000200 +000700 +000780 +0007C0 +7FFFE0 +FFFFF0 +FFFFF8 +7FFFFC +FFFFF8 +FFFFF0 +7FFFE0 +0007C0 +000780 +000700 +000200 +ENDCHAR +STARTCHAR j +ENCODING 114 +SWIDTH 157 0 +DWIDTH 8 0 +BBX 13 20 -5 -21 +BITMAP +0200 +0700 +0F80 +1FC0 +3FE0 +7FF0 +FFF8 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +ENDCHAR +STARTCHAR k +ENCODING 115 +SWIDTH 176 0 +DWIDTH 9 0 +BBX 15 22 -6 -22 +BITMAP +0100 +0380 +07C0 +0FE0 +1FF0 +3FF8 +7FFC +FFFE +7FFC +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +06C0 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 137 0 +DWIDTH 7 0 +BBX 13 25 -5 -14 +BITMAP +0200 +0700 +0F80 +1FC0 +3FE0 +7FF0 +FFF8 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +0D80 +FFF8 +7FF0 +3FE0 +1FC0 +0F80 +0700 +0200 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 157 0 +DWIDTH 8 0 +BBX 15 27 -6 -15 +BITMAP +0100 +0380 +07C0 +0FE0 +1FF0 +3FF8 +7FFC +FFFE +7FFC +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +0FE0 +7FFC +FFFE +7FFC +3FF8 +1FF0 +0FE0 +07C0 +0380 +0100 +ENDCHAR +STARTCHAR D +ENCODING 118 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 119 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 120 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 121 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 122 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 123 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 39 0 +DWIDTH 2 0 +BBX 15 26 -13 -22 +BITMAP +000A +0054 +00A0 +0000 +1CAA +1D54 +1C00 +1C20 +3E14 +7F0A +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +FF80 +FF80 +FF80 +C180 +FF80 +FF80 +FF80 +ENDCHAR +STARTCHAR bar +ENCODING 125 +SWIDTH 39 0 +DWIDTH 2 0 +BBX 15 26 -13 -22 +BITMAP +000E +007C +00E0 +0000 +1CFE +1DFE +1C00 +1C60 +3E3C +7F0E +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +ENDCHAR +STARTCHAR D +ENCODING 126 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 127 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 128 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 129 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR quotedbl +ENCODING 130 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 24 23 -10 -13 +BITMAP +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +002800 +FFC7FF +FF83FF +FFC7FF +002800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +003800 +ENDCHAR +STARTCHAR numbersign +ENCODING 131 +SWIDTH 294 0 +DWIDTH 15 0 +BBX 26 25 -11 -14 +BITMAP +001C0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +7FF7FF80 +FFE3FFC0 +FFC1FFC0 +FFE3FFC0 +7FF7FF80 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +003E0000 +001C0000 +ENDCHAR +STARTCHAR D +ENCODING 132 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 133 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C014 +ENCODING 134 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 21 0 -21 +BITMAP +FFFFF8 +FFFFF8 +FFFFF8 +E00000 +E00000 +E00000 +E00000 +E00000 +E0FF00 +E0FE00 +E0FC00 +E0FC00 +E0FE00 +E0FF00 +E0CF80 +E087C0 +E00380 +E00100 +E00000 +E00000 +E00000 +ENDCHAR +STARTCHAR C014 +ENCODING 135 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 23 23 -1 -22 +BITMAP +FFFFFC +FFFFFE +FFFFFE +FFFFFE +FFFFFC +F80000 +F80000 +F80000 +F8FFE0 +F8FFC0 +F8FF80 +F8FF00 +F8FF00 +F8FF80 +F8FFC0 +F8FFE0 +F8E7F0 +F8C3E0 +F881C0 +F80080 +F80000 +F80000 +700000 +ENDCHAR +STARTCHAR C014 +ENCODING 136 +SWIDTH 510 0 +DWIDTH 26 0 +BBX 21 21 -18 -21 +BITMAP +FFFFF8 +FFFFF8 +FFFFF8 +000038 +000038 +000038 +000038 +000038 +07F838 +03F838 +01F838 +01F838 +03F838 +07F838 +0F9838 +1F0838 +0E0038 +040038 +000038 +000038 +000038 +ENDCHAR +STARTCHAR C014 +ENCODING 137 +SWIDTH 432 0 +DWIDTH 22 0 +BBX 23 23 -19 -22 +BITMAP +7FFFFE +FFFFFE +FFFFFE +FFFFFE +7FFFFE +00003E +00003E +00003E +0FFE3E +07FE3E +03FE3E +01FE3E +01FE3E +03FE3E +07FE3E +0FFE3E +1FCE3E +0F863E +07023E +02003E +00003E +00003E +00001C +ENDCHAR +STARTCHAR C020 +ENCODING 138 +SWIDTH 216 0 +DWIDTH 11 0 +BBX 19 20 -8 -20 +BITMAP +FFFFE0 +FFFFE0 +FFFFE0 +000000 +000000 +000000 +000000 +004000 +00E000 +01F000 +03F800 +07FC00 +0FFE00 +00E000 +00E000 +00E000 +00E000 +00E000 +00E000 +00E000 +ENDCHAR +STARTCHAR C020 +ENCODING 139 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 21 22 -9 -21 +BITMAP +7FFFF0 +FFFFF8 +FFFFF8 +FFFFF8 +7FFFF0 +000000 +000000 +002000 +007000 +00F800 +01FC00 +03FE00 +07FF00 +0FFF80 +1FFFC0 +00F800 +00F800 +00F800 +00F800 +00F800 +00F800 +007000 +ENDCHAR +STARTCHAR D +ENCODING 140 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 141 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 142 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 143 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 144 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 145 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 146 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 147 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR D +ENCODING 148 +SWIDTH 255 0 +DWIDTH 13 0 +BBX 12 21 1 -22 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FF00 +FE00 +EF00 +C700 +8780 +0380 +03C0 +01C0 +01C0 +ENDCHAR +STARTCHAR E +ENCODING 149 +SWIDTH 530 0 +DWIDTH 27 0 +BBX 14 23 0 -23 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FFE0 +FFF0 +FFF8 +FFFC +FFF8 +FF80 +FFC0 +F7C0 +E7E0 +C3E0 +03F0 +01F0 +01F0 +00E0 +ENDCHAR +STARTCHAR C226 +ENCODING 150 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 23 37 -10 -20 +BITMAP +007C00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +03FF80 +0FFFE0 +1F01F0 +3C0078 +383838 +70381C +70381C +60380C +E0380E +E0380E +E0380E +E03C0E +E01E0E +600F0C +70079C +70031C +380038 +3C0078 +1F01F0 +0FFFE0 +03FF80 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +007C00 +ENDCHAR +STARTCHAR C226 +ENCODING 151 +SWIDTH 235 0 +DWIDTH 12 0 +BBX 23 37 -10 -20 +BITMAP +007C00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +03FF80 +0FFFE0 +1FFFF0 +3FFFF8 +3FFFF8 +7FFFFC +7FFFFC +7FFFFC +FFFFFE +FFFFFE +FFFFFE +FFFFFE +FFFFFE +7FFFFC +7FFFFC +7FFFFC +3FFFF8 +3FFFF8 +1FFFF0 +0FFFE0 +03FF80 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +00FE00 +007C00 +ENDCHAR +STARTCHAR C230 +ENCODING 152 +SWIDTH 373 0 +DWIDTH 19 0 +BBX 11 21 -4 -12 +BITMAP +FBE0 +FFE0 +1F00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +0E00 +1F00 +FFE0 +FBE0 +ENDCHAR +STARTCHAR C231 +ENCODING 153 +SWIDTH 157 0 +DWIDTH 8 0 +BBX 13 23 -5 -13 +BITMAP +7DF0 +FFF8 +FFF8 +7FF0 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +0F80 +7FF0 +FFF8 +FFF8 +7DF0 +ENDCHAR +ENDFONT diff --git a/kcms/input/cursor_large_black.pcf.gz b/kcms/input/cursor_large_black.pcf.gz new file mode 100644 index 0000000000..c9b458390c Binary files /dev/null and b/kcms/input/cursor_large_black.pcf.gz differ diff --git a/kcms/input/cursor_large_white.pcf.gz b/kcms/input/cursor_large_white.pcf.gz new file mode 100644 index 0000000000..57feedc97e Binary files /dev/null and b/kcms/input/cursor_large_white.pcf.gz differ diff --git a/kcms/input/cursor_small_white.pcf.gz b/kcms/input/cursor_small_white.pcf.gz new file mode 100644 index 0000000000..618905ea70 Binary files /dev/null and b/kcms/input/cursor_small_white.pcf.gz differ diff --git a/kcms/input/cursortheme.desktop b/kcms/input/cursortheme.desktop new file mode 100644 index 0000000000..4a5feed001 --- /dev/null +++ b/kcms/input/cursortheme.desktop @@ -0,0 +1,169 @@ +[Desktop Entry] +Exec=kcmshell5 cursortheme +Icon=edit-select +Type=Service + +X-KDE-ServiceTypes=KCModule +X-KDE-Library=kcm_cursortheme +X-KDE-ParentApp=kcontrol +X-KDE-System-Settings-Parent-Category=desktop-appearance +X-DocPath=kcontrol/cursortheme/index.html + +Name=Cursor Theme +Name[ar]=سِمة المؤشر +Name[ast]=Tema de cursores +Name[bg]=Тема за курсора +Name[bn]=কার্সর থীম +Name[bs]=Tema pokazivača +Name[ca]=Tema de cursor +Name[ca@valencia]=Tema de cursor +Name[cs]=Motiv kurzorů +Name[da]=Markørtema +Name[de]=Zeigerdesigns +Name[el]=Θέμα δρομέα +Name[en_GB]=Cursor Theme +Name[es]=Tema de cursores +Name[et]=Kursoriteema +Name[eu]=Kurtsorearen gaia +Name[fi]=Osoitinteema +Name[fr]=Thème de curseur +Name[ga]=Téama Cúrsóra +Name[gl]=Tema do cursor +Name[he]=ערכת־נושא למצביע העכבר +Name[hi]=संकेतक प्रसंग +Name[hr]=Tema pokazivača +Name[hu]=Kurzortéma +Name[ia]=Thema de cursor +Name[is]=Bendilþema +Name[it]=Tema dei cursori +Name[ja]=カーソルテーマ +Name[kk]=Меңзер нақышы +Name[km]=ស្បែក​ទស្សន៍ទ្រនិច +Name[kn]=ಸ್ಥಳಸೂಚಕ (ಕರ್ಸರ್) ಪರಿಸರವಿನ್ಯಾಸ +Name[ko]=커서 테마 +Name[lt]=Žymeklių tema +Name[lv]=Kursora tēma +Name[mr]=कर्सर शैली +Name[nb]=Pekertema +Name[nds]=Wieserutsehn +Name[nl]=Cursorthema +Name[pa]=ਕਰਸਰ ਥੀਮ +Name[pl]=Zestawy kursorów +Name[pt]=Tema de Cursores +Name[pt_BR]=Tema de cursores +Name[ro]=Tematica de cursor +Name[ru]=Тема курсоров +Name[sk]=Téma kurzora +Name[sl]=Tema kazalke +Name[sr]=Тема показивача +Name[sr@ijekavian]=Тема показивача +Name[sr@ijekavianlatin]=Tema pokazivača +Name[sr@latin]=Tema pokazivača +Name[sv]=Muspekartema +Name[th]=ชุดเคอร์เซอร์ +Name[tr]=İmleç Teması +Name[ug]=نۇربەلگە ئۆرنىكى +Name[uk]=Тема вказівника +Name[vi]=Sắc thái con trỏ +Name[wa]=Tinme do cursoe +Name[x-test]=xxCursor Themexx +Name[zh_CN]=光标主题 +Name[zh_TW]=游標主題 + +Comment=Customize the mouse cursor appearance +Comment[ar]=خصص مظهر سِمة مؤشر الفأرة +Comment[ast]=Personalizar l'apariencia del cursor del mur +Comment[bg]=Настройки на изгледа на курсора на мишката +Comment[bn]=মাউস কার্সর-এর চেহারা পছন্দমত বদলান +Comment[bs]=Prilagodite izgled pokazivača miša +Comment[ca]=Personalitza l'aparença del cursor del ratolí +Comment[ca@valencia]=Personalitza l'aparença del cursor del ratolí +Comment[cs]=Upravit vzhled kurzoru myši +Comment[da]=Tilpas musemarkørens udseende +Comment[de]=Das Erscheinungsbild des Mauszeigers anpassen +Comment[el]=Προσαρμογή εμφάνισης του δείκτη ποντικιού +Comment[en_GB]=Customise the mouse cursor appearance +Comment[es]=Personalizar la apariencia del cursor del ratón +Comment[et]=Hiirekursori välimuse kohandamine +Comment[eu]=Pertsonalizatu saguaren kurtsorearen itxura +Comment[fi]=Hiiren osoittimen ulkoasuasetukset +Comment[fr]=Personnaliser l'apparence du curseur de la souris +Comment[gl]=Personaliza o tema do cursor +Comment[he]=התאמה אישית של מראה מצביע העכבר +Comment[hr]=Podesi izgled pokazivača miša +Comment[hu]=Az egérmutató megjelenésének testreszabása +Comment[ia]=Personalisa le apparentia de cursor de mus +Comment[is]=Sérsníða útlit músarbendilsins +Comment[it]=Personalizza l'aspetto del cursore del mouse +Comment[ja]=マウスカーソル表示のカスタマイズ +Comment[kk]=Меңзер нақышын өзінше баптап алу +Comment[km]=ប្ដូរ​រូបរាង​ទស្សន៍ទ្រនិច​កណ្ដុរ​តាម​បំណង +Comment[ko]=마우스 커서 모양 사용자 정의 +Comment[lt]=Derinti pelės žymeklio išvaizdą +Comment[lv]=Pielāgot peles kursora izskatu +Comment[mr]=कर्सर शैलीत ऐच्छिक बदल करा +Comment[nb]=Sett opp utseende for musepekeren +Comment[nds]=Dat Muuswieser-Utsehn topassen +Comment[nl]=Het uiterlijk van de muiscursor aanpassen +Comment[pa]=ਮਾਊਸ ਕਰਸਰ ਦਿੱਖ ਆਪਣੀ ਪਸੰਦ ਦੀ ਬਣਾਉ +Comment[pl]=Dostosowywanie wyglądu kursora myszy +Comment[pt]=Personalizar a aparência do cursor do rato +Comment[pt_BR]=Personaliza a aparência do cursor do mouse +Comment[ro]=Particularizează tematica de cursor +Comment[ru]=Настройка внешнего вида курсора мыши +Comment[sk]=Prispôsobenie vzhľadu kurzora myši +Comment[sl]=Prilagodite videz kazalke miške +Comment[sr]=Прилагодите изглед показивача миша +Comment[sr@ijekavian]=Прилагодите изглед показивача миша +Comment[sr@ijekavianlatin]=Prilagodite izgled pokazivača miša +Comment[sr@latin]=Prilagodite izgled pokazivača miša +Comment[sv]=Anpassa muspekarens utseende +Comment[th]=ปรับแต่งชุดเคอร์เซอร์ของเมาส์เอง +Comment[tr]=Fare işaretçisi görünümünü özelleştir +Comment[ug]=چاشقىنەك نۇربەلگە قىياپىتىنى ئۆزلەشتۈر +Comment[uk]=Налаштування вигляду вказівника миші на екрані +Comment[vi]=Tuỳ chỉnh diện mạo con trỏ chuột +Comment[wa]=Mete a vosse môde li rivnance do cursoe del sori +Comment[x-test]=xxCustomize the mouse cursor appearancexx +Comment[zh_CN]=定制鼠标指针外观 +Comment[zh_TW]=自訂滑鼠游標外觀 + +X-KDE-Keywords=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance +X-KDE-Keywords[bs]=miš,kursor,tema,pojavljivanje kurosra,boja kursora,tema kursora,tema miša,pojavljivanje miša,površina miša,pokazatelj boja,pojava boja +X-KDE-Keywords[ca]=Ratolí,Cursor,Tema,Aparença de cursor,Color de cursor,Tema de cursor,Tema de ratolí,Aparença de ratolí,Pells de ratolí,Colors d'apuntador,Aparença d'apuntador +X-KDE-Keywords[ca@valencia]=Ratolí,Cursor,Tema,Aparença de cursor,Color de cursor,Tema de cursor,Tema de ratolí,Aparença de ratolí,Pells de ratolí,Colors d'apuntador,Aparença d'apuntador +X-KDE-Keywords[da]=Mus,markør,cursor,tema,markørens udseende,markørfarve,markørtema,musetema,musens udseende,museskin +X-KDE-Keywords[de]=Maus,Zeiger,Mauszeiger,Zeigerfarbe,Zeigerdesign +X-KDE-Keywords[el]=ποντίκι,δρομέας,θέμα,εμφάνιση δρομέα,χρώμα δρομέα,θέμα δρομέα,θέμα ποντικιού,εμφάνιση ποντικιού,θέματα ποντικιού,χρώματα δείκτη,εμφάνιση δείκτη +X-KDE-Keywords[es]=Ratón,Cursor,Tema,Apariencia del cursor,Color del cursor,Tema del cursor,Tema del ratón,Apariencia del ratón,Pieles del ratón,Colores del puntero,Apariencia del puntero +X-KDE-Keywords[et]=Hiir,Kursor,Teema,Kursori välimus,Kursori värv,Kursori teema,Hiireteema,Hiire välimus,Hiire nahad,Osutusseadme värvid,Osutusseadme välimus +X-KDE-Keywords[eu]=sagu,kurtsore,gai,kurtsorearen itsura,kurtsorearen kolorea,saguaren gaia,saguaren itxura,saguaren azalak,erakuslearen koloreak,erakuslearen itxura +X-KDE-Keywords[fi]=hiiri,osoitin,teema,osoittimen ulkoasu,osoittimen väri,osoitinteema,hiiren teema,hiiriteema,hiiren ulkoasu,hiiriteemat,osoitinvärit +X-KDE-Keywords[fr]=Souris, Curseur, Thème, Apparence du curseur, Couleur du curseur, Thème de curseurs, Thème de la souris, Apparence de la souris, Revêtement de la souris, Couleur du pointeur, Apparence du pointeur +X-KDE-Keywords[gl]=rato, cursor, tema, aparencia do cursor, cor do cursor, tema do cursor, tema do rato, aparencia do rato, cor do rato, punteiro, cor do punteiro, aparencia do punteiro, tema do punteiro +X-KDE-Keywords[hu]=Egér,Kurzor,Téma,Kurzormegjelenés,Kurzorszín,Kurzortéma,Egértéma,Egérmegjelenés,Egérfelületek,Mutató színek,Mutató megjelenés +X-KDE-Keywords[ia]=Mus,Cursor,Thema,Apparentia,Cursor,Color,Thema de Cursor,Thema de Mus, Apparentia de Mus,Pelles de Mus,Colores de punctator,Apparentia de punctator +X-KDE-Keywords[it]=Mouse,Cursore,Aspetto cursore,Colore cursore,Tema cursore,Tema mouse,Aspetto mouse,Skin mouse,Colore puntatore,Aspetto puntatore +X-KDE-Keywords[kk]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance +X-KDE-Keywords[km]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance +X-KDE-Keywords[ko]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,마우스,커서,커서 테마,포인터 +X-KDE-Keywords[mr]=माऊस, कर्सर, थीम, कर्सर, अपिरिअन्स, कर्सर, कलर, कर्सर थीम, माऊस थीम, माऊस अपिरिअन्स, माऊस स्कीन्स, पॉईटर अपिरिअन्स +X-KDE-Keywords[nb]=Mus.peker,tema,pekerutseende,pekerfarge,pekertem,musetema,musutseende,museskins,pekerfarger,pekeerutseende +X-KDE-Keywords[nds]=Muus,Wieser,Muster, Wieserutsehn,Klöör,Utsehn +X-KDE-Keywords[nl]=Muis,Cursor,Thema,Uiterlijk van cursor,kleur van cursor,Thema van cursor,Thema van muis,uiterlijk van muis,Muisoppervlak,Kleuren van aanwijzer,Uiterlijk van aanwijzer +X-KDE-Keywords[pl]=Mysz,Kursor,Motyw,Wygląd kursora,Kolor kursora,Motyw kursora,Motyw myszy,Wygląd myszy,Skórki myszy,Kolory wskaźnika,Wygląd wskaźnika +X-KDE-Keywords[pt]=Rato,Cursor,Tema,Aparência do Cursor,Cor do Cursor,Tema do Cursor,Tema do Rato,Aparência do Rato,Visuais do Rato,Cores do Cursor +X-KDE-Keywords[pt_BR]=Mouse,Cursor,Tema,Aparência do cursor,Cor do cursor,Tema do cursor,Tema do mouse,Aparência do mouse,Visuais do mouse,Cores do ponteiro,Aparência do ponteiro +X-KDE-Keywords[ru]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,мышь,курсор,тема,внешний вид курсора мыши,цвет указателя,внешний вид указателя +X-KDE-Keywords[sk]=Myš, kurzor,téma,vzhľad kurzora,farba kurzora,téma kurzora,téma myši,vzhľad myši,skiny myši,farby ukazovateľa,vzhľad ukazovateľa +X-KDE-Keywords[sl]=miška,kazalec,kurzor,kazalka,tema,videz kazalca,videz kazalke,barva kazalca,barva kazalke,tema kazalcev,tema kazalk,tema miške,videz miške,preobleke miške,teme miške +X-KDE-Keywords[sr]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,миш,показивач,курсор,тема,изглед показивача,боја показивача,тема показивача,тема миша,изглед миша,пресвлаке миша,боје показивача +X-KDE-Keywords[sr@ijekavian]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,миш,показивач,курсор,тема,изглед показивача,боја показивача,тема показивача,тема миша,изглед миша,пресвлаке миша,боје показивача +X-KDE-Keywords[sr@ijekavianlatin]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,miš,pokazivač,kursor,tema,izgled pokazivača,boja pokazivača,tema pokazivača,tema miša,izgled miša,presvlake miša,boje pokazivača +X-KDE-Keywords[sr@latin]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,miš,pokazivač,kursor,tema,izgled pokazivača,boja pokazivača,tema pokazivača,tema miša,izgled miša,presvlake miša,boje pokazivača +X-KDE-Keywords[sv]=Mus,Pekare,Tema,Utseende,Färg,Pekartema,Mustema,Musutseende,Musskal,Pekarfärger,Pekarutseende +X-KDE-Keywords[tr]=Fare,İşaretçi,Tema,İşaretçi Görünümü,İşaretçi Rengi,İşaretçi Teması,Fare Teması,Fare Görünümü,Fare Kabuğu,İşaretçi Renkleri,İşaretçi Görünümü +X-KDE-Keywords[uk]=миша,вказівник,тема,вигляд вказівника,колір вказівника,тема вказівника,тема миші,вигляд миші,Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance +X-KDE-Keywords[x-test]=xxMouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearancexx +X-KDE-Keywords[zh_CN]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance,鼠标,指针,主题,指针外观,指针颜色,指针主题,鼠标主题,鼠标外观,鼠标皮肤,指针外观 +X-KDE-Keywords[zh_TW]=Mouse,Cursor,Theme,Cursor Appearance,Cursor Color,Cursor Theme,Mouse Theme,Mouse Appearance,Mouse Skins,Pointer Colors,Pointer Appearance diff --git a/kcms/input/kapplymousetheme.cpp b/kcms/input/kapplymousetheme.cpp new file mode 100644 index 0000000000..67b5138058 --- /dev/null +++ b/kcms/input/kapplymousetheme.cpp @@ -0,0 +1,88 @@ +/* + * main.cpp + * + * Copyright (c) 1999 Matthias Hoelzer-Kluepfel + * Copyright (c) 2005 Lubos Lunak + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include + +#ifdef HAVE_XCURSOR +# include +#endif + +static Display* dpy; +Display* display() { return dpy; } +Window appRootWindow() { return DefaultRootWindow( dpy ); } + +static bool isEmpty( const char* str ) + { + if( str == NULL ) + return true; + while( isspace( *str )) + ++str; + return *str == '\0'; + } + +int main( int argc, char* argv[] ) + { + if( argc != 3 ) + return 1; + dpy = XOpenDisplay( NULL ); + if( dpy == NULL ) + return 2; + int ret = 0; +#ifdef HAVE_XCURSOR + const char* theme = argv[ 1 ]; + const char* size = argv[ 2 ]; + + // Note: If you update this code, update kapplymousetheme as well. + + // use a default value for theme only if it's not configured at all, not even in X resources + if( isEmpty( theme ) + && isEmpty( XGetDefault( display(), "Xcursor", "theme" )) + && isEmpty( XcursorGetTheme( display()))) + { + theme = "default"; + ret = 10; // means to switch to default + } + + // Apply the KDE cursor theme to ourselves + if( !isEmpty( theme )) + XcursorSetTheme(display(), theme ); + + if (!isEmpty( size )) + XcursorSetDefaultSize(display(), atoi( size )); + + // Load the default cursor from the theme and apply it to the root window. + Cursor handle = XcursorLibraryLoadCursor(display(), "left_ptr"); + XDefineCursor(display(), appRootWindow(), handle); + XFreeCursor(display(), handle); // Don't leak the cursor + +#else + ( void ) argv; +#endif + XCloseDisplay( dpy ); + return ret; + } diff --git a/kcms/input/kcmcursortheme.cpp b/kcms/input/kcmcursortheme.cpp new file mode 100644 index 0000000000..68f44bb287 --- /dev/null +++ b/kcms/input/kcmcursortheme.cpp @@ -0,0 +1,70 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcmcursortheme.h" + +#include +#include +#include + +K_PLUGIN_FACTORY(CursorThemeConfigFactory, + registerPlugin(); +) +K_EXPORT_PLUGIN(CursorThemeConfigFactory("kcm_cursortheme", "kcminput")) + + +CursorThemeConfig::CursorThemeConfig(QWidget *parent, const QVariantList &args) + : KCModule(parent, args) +{ + QLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + themepage = new ThemePage(this); + connect(themepage, SIGNAL(changed(bool)), SLOT(changed())); + layout->addWidget(themepage); + + KAboutData* aboutData = new KAboutData(QStringLiteral("kcm_cursortheme"), QString(), i18n("Cursor Theme"), + QString(), QString(), KAboutData::License_GPL, i18n("(c) 2003-2007 Fredrik Höglund")); + aboutData->addAuthor(i18n("Fredrik Höglund")); + setAboutData(aboutData); +} + +CursorThemeConfig::~CursorThemeConfig() +{ + /* */ +} + +void CursorThemeConfig::load() +{ + themepage->load(); + emit changed(false); +} + +void CursorThemeConfig::save() +{ + themepage->save(); + emit changed(false); +} + +void CursorThemeConfig::defaults() +{ + themepage->defaults(); + changed(); +} + +#include "kcmcursortheme.moc" diff --git a/kcms/input/kcmcursortheme.h b/kcms/input/kcmcursortheme.h new file mode 100644 index 0000000000..d9e32b2359 --- /dev/null +++ b/kcms/input/kcmcursortheme.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KCMCURSORTHEME_H +#define KCMCURSORTHEME_H + +#include +#include "themepage.h" + +class CursorThemeConfig : public KCModule +{ + Q_OBJECT + +public: + CursorThemeConfig(QWidget *parent, const QVariantList &); + ~CursorThemeConfig(); + +public: + void load(); + void save(); + void defaults(); + +private: + ThemePage *themepage; +}; + +#endif diff --git a/kcms/input/kmousedlg.ui b/kcms/input/kmousedlg.ui new file mode 100644 index 0000000000..b48a6065fa --- /dev/null +++ b/kcms/input/kmousedlg.ui @@ -0,0 +1,295 @@ + + + KMouseDlg + + + + 0 + 0 + 416 + 329 + + + + + + + + + + + + 0 + 0 + + + + Button Order + + + true + + + + + + Righ&t handed + + + true + + + + + + + Le&ft handed + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 1 + 1 + + + + + + + + + + + Change the direction of scrolling for the mouse wheel or the 4th and 5th mouse buttons. + + + Re&verse scroll direction + + + + + + + + + + 0 + 0 + + + + + 96 + 80 + + + + true + + + + + + + + + Icons + + + + + + Dou&ble-click to open files and folders (select icons on first click) + + + false + + + + + + + &Single-click to open files and folders + + + true + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + Cha&nge pointer shape over icons + + + true + + + + + + + A&utomatically select icons + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + false + + + + 0 + 0 + + + + Delay + + + 0 + + + 2000 + + + 125 + + + ms + + + true + + + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 1 + + + + + + + + + KIntNumInput + QWidget +
    knuminput.h
    +
    +
    + + rightHanded + leftHanded + cbScrollPolarity + doubleClick + singleClick + cb_pointershape + cbAutoSelect + + + kdialog.h + + + + + singleClick + toggled(bool) + cb_pointershape + setEnabled(bool) + + + 40 + 209 + + + 69 + 240 + + + + + singleClick + toggled(bool) + cbAutoSelect + setEnabled(bool) + + + 40 + 209 + + + 69 + 264 + + + + +
    diff --git a/kcms/input/logitechmouse.cpp b/kcms/input/logitechmouse.cpp new file mode 100644 index 0000000000..2b856c3c6c --- /dev/null +++ b/kcms/input/logitechmouse.cpp @@ -0,0 +1,421 @@ +/* + * logitechmouse.cpp + * + * Copyright (C) 2004 Brad Hards + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_LIBUSB +#include + +#include "logitechmouse.h" + +LogitechMouse::LogitechMouse( struct usb_device *usbDev, int mouseCapabilityFlags, QWidget* parent, const char* name ) + : LogitechMouseBase( parent ) + , m_resolution( 0 ) +{ + if ( !name ) + setObjectName( "LogitechMouse" ); + + cordlessNameLabel->setText( i18n("Mouse type: %1", objectName() ) ); + + m_mouseCapabilityFlags = mouseCapabilityFlags; + + m_usbDeviceHandle = usb_open( usbDev ); + + if ( !m_usbDeviceHandle ) { + kWarning() << "Error opening usbfs file: " << usb_strerror() ; + return; + } + + if ( mouseCapabilityFlags & USE_CH2 ) { + m_useSecondChannel = 0x0100; + } else { + m_useSecondChannel = 0x0000; + } + + permissionProblemText->hide(); + + if ( mouseCapabilityFlags & HAS_RES ) { + updateResolution(); + resolutionSelector->setEnabled( true ); + + connect( button400cpi, SIGNAL(clicked()), parent, SLOT(changed()) ); + connect( button800cpi, SIGNAL(clicked()), parent, SLOT(changed()) ); + + if ( 4 == resolution() ) { + button800cpi->setChecked( true ); + } else if ( 3 == resolution() ) { + button400cpi->setChecked( true ); + } else { + // it must have failed, try to help out + resolutionSelector->setEnabled(false); + permissionProblemText->show(); + } + } + + if ( mouseCapabilityFlags & HAS_CSR ) { + + initCordlessStatusReporting(); + + // Do a name + cordlessNameLabel->setText( i18n("Mouse type: %1", cordlessName() ) ); + cordlessNameLabel->setEnabled( true ); + + // Display the battery power level - the level gets updated in updateGUI() + batteryBox->setEnabled( true ); + + // Channel + channelSelector->setEnabled( true ); + // if the channel is changed, we need to turn off the timer, otherwise it + // just resets the button to reflect the current status. The timer is + // started again when we applyChanges() + connect( channel1, SIGNAL(clicked()), this, SLOT(stopTimerForNow()) ); + connect( channel1, SIGNAL(clicked()), parent, SLOT(changed()) ); + if ( isDualChannelCapable() ) { + channel2->setEnabled( true ); + connect( channel2, SIGNAL(clicked()), this, SLOT(stopTimerForNow()) ); + connect( channel2, SIGNAL(clicked()), parent, SLOT(changed()) ); + } + + updateGUI(); + } + +} + +LogitechMouse::~LogitechMouse() +{ + if ( m_usbDeviceHandle ) + usb_close( m_usbDeviceHandle ); +} + +void LogitechMouse::initCordlessStatusReporting() +{ + updateCordlessStatus(); + doUpdate = new QTimer( this ); // will be automatically deleted + connect( doUpdate, SIGNAL(timeout()), this, SLOT(updateGUI()) ); + doUpdate->start( 20000 ); +} + +void LogitechMouse::updateCordlessStatus() +{ + QByteArray status(8, '\0'); + + int result = -1; + + if ( m_usbDeviceHandle ) + result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR | USB_ENDPOINT_IN,0x09, + (0x0003 | m_useSecondChannel), + (0x0000 | m_useSecondChannel), + status.data(), + 0x0008, + 1000); + + if (0 > result) { + // We probably have a permission problem + m_channel = 0; + channelSelector->setEnabled( false ); + batteryBox->setEnabled( false ); + cordlessNameLabel->hide(); + permissionProblemText->show(); + } else { + // kDebug() << "P6 (connect status): " << (status[0] & 0xFF); + if ( status[0] & 0x20 ) { // mouse is talking + m_connectStatus = ( status[0] & 0x80 ); + m_mousePowerup = ( status[0] & 0x40 ); + m_receiverUnlock = ( status[0] & 0x10 ); + m_waitLock = ( status[0] & 0x08 ); + } + + // kDebug() << "P0 (receiver type): " << (status[1] & 0xFF); + /* + 0x38 = pid C501 + 0x39 = pid C502 + 0x3B = pid C504 + 0x3C = pid C508 + 0x3D = pid C506 + 0x3E = pid C505 + */ + + m_cordlessNameIndex = (status[2] & 0xFF); + + m_batteryLevel = (status[3] & 0x07 ); + if ( status[3] & 0x08 ) { + m_channel = 2; + } else { + m_channel = 1; + } + + m_cordlessSecurity = ( ( status[4] ) & ( status[5] << 8 ) ); + + m_caseShape = ( status[6] & 0x7F ); + + // kDebug() << "PB1 (device Capabilities): " << (status[7] & 0xFF); + m_numberOfButtons = 2 + ( status[7] & 0x07 ); // 9 means something more than 8 + m_twoChannelCapable = ( status[7] & 0x40 ); + m_verticalRoller = ( status[7] & 0x08 ); + m_horizontalRoller = ( status[7] & 0x10 ); + m_has800cpi = ( status[7] & 0x20 ); + } + +} + +void LogitechMouse::updateGUI() +{ + updateCordlessStatus(); + + batteryBar->setValue( batteryLevel() ); + + if ( isDualChannelCapable() ) { + if ( 2 == channel() ) { + channel2->setChecked( true ); + } else if ( 1 == channel() ) { + channel1->setChecked( true ); + } // else it might have failed - we don't do anything + } +} + +void LogitechMouse::stopTimerForNow() +{ + doUpdate->stop(); +} + +void LogitechMouse::applyChanges() +{ + if ( m_mouseCapabilityFlags & HAS_RES ) { + if ( ( resolution() == 4 ) && ( button400cpi->isChecked() ) ) { + // then we are in 800cpi mode, but want 400cpi + setLogitechTo400(); + } else if ( ( resolution() == 3 ) && (button800cpi->isChecked() ) ) { + // then we are in 400 cpi mode, but want 800 cpi + setLogitechTo800(); + } + } + + if ( isDualChannelCapable() ) { + if ( ( channel() == 2 ) && ( channel1->isChecked() ) ) { + // we are on channel 2, but want channel 1 + setChannel1(); + KMessageBox::information(this, i18n("RF channel 1 has been set. Please press Connect button on mouse to re-establish link"), i18n("Press Connect Button") ); + } else if ( ( channel() == 1 ) && ( channel2->isChecked() ) ) { + // we are on channel 1, but want channel 2 + setChannel2(); + KMessageBox::information(this, i18n("RF channel 2 has been set. Please press Connect button on mouse to re-establish link"), i18n("Press Connect Button") ); + } + + initCordlessStatusReporting(); + } +} + +void LogitechMouse::save(KConfig * /*config*/) +{ + kDebug() << "Logitech mouse settings not saved - not implemented yet"; +} + +quint8 LogitechMouse::resolution() +{ + // kDebug() << "resolution: " << m_resolution; + if ( 0 == m_resolution ) { + updateResolution(); + } + return m_resolution; +} + +void LogitechMouse::updateResolution() +{ + char resolution; + + int result = -1; + + if ( m_usbDeviceHandle ) + result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR | USB_ENDPOINT_IN, + 0x01, + 0x000E, + 0x0000, + &resolution, + 0x0001, + 100); + + // kDebug() << "resolution is: " << resolution; + if (0 > result) { + kWarning() << "Error getting resolution from device : " << usb_strerror() ; + m_resolution = 0; + } else { + m_resolution = resolution; + } +} + +void LogitechMouse::setLogitechTo800() +{ + int result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR, + 0x02, + 0x000E, + 4, + NULL, + 0x0000, + 100); + if (0 > result) { + kWarning() << "Error setting resolution on device: " << usb_strerror() ; + } +} + +void LogitechMouse::setLogitechTo400() +{ + int result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR, + 0x02, + 0x000E, + 3, + NULL, + 0x0000, + 100); + if (0 > result) { + kWarning() << "Error setting resolution on device: " << usb_strerror() ; + } +} + +quint8 LogitechMouse::batteryLevel() const +{ + return m_batteryLevel; +} + + +quint8 LogitechMouse::channel() const +{ + return m_channel; +} + +bool LogitechMouse::isDualChannelCapable() const +{ + return m_twoChannelCapable; +} + +void LogitechMouse::setChannel1() +{ + int result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR, + 0x02, + (0x0008 | m_useSecondChannel), + (0x0000 | m_useSecondChannel), + NULL, + 0x0000, + 1000); + + if (0 > result) { + kWarning() << "Error setting mouse to channel 1 : " << usb_strerror() ; + } + +} + +void LogitechMouse::setChannel2() +{ + int result = usb_control_msg( m_usbDeviceHandle, + USB_TYPE_VENDOR, + 0x02, + (0x0008 | m_useSecondChannel), + (0x0001 | m_useSecondChannel), + NULL, + 0x0000, + 1000); + + if (0 > result) { + kWarning() << "Error setting mouse to channel 2 : " << usb_strerror() ; + } + +} + +QString LogitechMouse::cordlessName() const +{ + switch ( m_cordlessNameIndex ) { + case 0x00: + return i18nc( "no cordless mouse", "none" ); + break; + case 0x04: + return i18n( "Cordless Mouse" ); + break; + case 0x05: + return i18n( "Cordless Wheel Mouse" ); + break; + case 0x06: + return i18n( "Cordless MouseMan Wheel" ); + break; + case 0x07: + return i18n( "Cordless Wheel Mouse" ); + break; + case 0x08: + return i18n( "Cordless Wheel Mouse" ); + break; + case 0x09: + return i18n( "Cordless TrackMan Wheel" ); + break; + case 0x0A: + return i18n( "TrackMan Live" ); + break; + case 0x0C: + return i18n( "Cordless TrackMan FX" ); + break; + case 0x0D: + return i18n( "Cordless MouseMan Optical" ); + break; + case 0x0E: + return i18n( "Cordless Optical Mouse" ); + break; + case 0x0F: + return i18n( "Cordless Mouse" ); + break; + case 0x12: + return i18n( "Cordless MouseMan Optical (2ch)" ); + break; + case 0x13: + return i18n( "Cordless Optical Mouse (2ch)" ); + break; + case 0x14: + return i18n( "Cordless Mouse (2ch)" ); + break; + case 0x82: + return i18n( "Cordless Optical TrackMan" ); + break; + case 0x8A: + return i18n( "MX700 Cordless Optical Mouse" ); + break; + case 0x8B: + return i18n( "MX700 Cordless Optical Mouse (2ch)" ); + break; + default: + return i18n( "Unknown mouse"); + } +} + +#include "logitechmouse.moc" + +#endif + diff --git a/kcms/input/logitechmouse.h b/kcms/input/logitechmouse.h new file mode 100644 index 0000000000..cc4f5f6210 --- /dev/null +++ b/kcms/input/logitechmouse.h @@ -0,0 +1,107 @@ +/* + * logitechmouse.h + * + * Copyright (C) 2004 Brad Hards + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __LOGITECHMOUSE_H__ +#define __LOGITECHMOUSE_H__ + +#include + +#include + +#include + +#include "ui_logitechmouse_base.h" + +#include + +#define VENDOR_LOGITECH 0x046D +#define HAS_RES 0x01 /* mouse supports variable resolution */ +#define HAS_SS 0x02 /* mouse supports smart scroll control */ +#define HAS_CSR 0x04 /* mouse supports cordless status reporting and control */ +#define HAS_SSR 0x08 /* mouse supports smart scroll reporting */ +#define USE_CH2 0x10 /* mouse needs to use the second channel */ + +class LogitechMouseBase : public QWidget, public Ui::LogitechMouseBase +{ +public: + LogitechMouseBase( QWidget *parent ) : QWidget( parent ) { + setupUi( this ); + } +}; + + + +class LogitechMouse : public LogitechMouseBase +{ + Q_OBJECT + +public: + LogitechMouse( struct usb_device *usbDev, int mouseCapabilityFlags, QWidget* parent = 0, const char* name=0); + ~LogitechMouse(); + void applyChanges(); + void save(KConfig *config); + +protected Q_SLOTS: + void setChannel1(); + void setChannel2(); + void updateGUI(); + void stopTimerForNow(); + +private: + void initCordlessStatusReporting(); + void updateCordlessStatus(); + + void setLogitechTo400(); + void setLogitechTo800(); + + QString cordlessName() const ; + quint8 resolution(); + void updateResolution(); + quint8 batteryLevel() const; + quint8 channel() const; + bool isDualChannelCapable() const; + + QTimer *doUpdate; + + struct usb_dev_handle *m_usbDeviceHandle; + bool m_connectStatus; // true if the CONNECT button on the mouse is pressed + bool m_mousePowerup; // true if we are doing "just out of the box" auto-locking + bool m_receiverUnlock; // true if mouse has been disconnected by a long press + // of the receiver's CONNECT button + bool m_waitLock; // true if receiver searching for new mouse because the + // CONNECT button on the receiver was pressed + quint8 m_batteryLevel; + quint8 m_channel; + quint8 m_cordlessNameIndex; // this gets convered into a QString in cordlessName() + quint16 m_cordlessSecurity; + quint16 m_useSecondChannel; + quint8 m_caseShape; + quint8 m_numberOfButtons; + quint8 m_resolution; + bool m_twoChannelCapable; // true if the mouse supports dual channels + bool m_verticalRoller; // true if the mouse has a vertical roller (wheel) + bool m_horizontalRoller; // true if the mouse has a horizontal roller (wheel) + bool m_has800cpi; // true if the mouse does 800cpi resolution + int m_mouseCapabilityFlags; +}; + +#endif + diff --git a/kcms/input/logitechmouse.usermap b/kcms/input/logitechmouse.usermap new file mode 100644 index 0000000000..65da520c53 --- /dev/null +++ b/kcms/input/logitechmouse.usermap @@ -0,0 +1,35 @@ +# script match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info +# Wheel Mouse Optical +consoleUserPerms 0x0003 0x046d 0xc00e 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MouseMan Traveler +consoleUserPerms 0x0003 0x046d 0xc00f 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MouseMan Dual Optical +consoleUserPerms 0x0003 0x046d 0xc012 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX310 Optical Mouse +consoleUserPerms 0x0003 0x046d 0xc01b 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX510 Optical Mouse +consoleUserPerms 0x0003 0x046d 0xc01d 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX300 Optical Mouse +consoleUserPerms 0x0003 0x046d 0xc024 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX500 Optical Mouse +consoleUserPerms 0x0003 0x046d 0xc025 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# iFeel Mouse +consoleUserPerms 0x0003 0x046d 0xc031 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Mouse Receiver +consoleUserPerms 0x0003 0x046d 0xc501 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Dual Receiver +consoleUserPerms 0x0003 0x046d 0xc502 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Cordless Freedom Optical +consoleUserPerms 0x0003 0x046d 0xc504 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Cordless Elite Duo +consoleUserPerms 0x0003 0x046d 0xc505 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX700 Optical Mouse +consoleUserPerms 0x0003 0x046d 0xc506 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Cordless Optical Trackman +consoleUserPerms 0x0003 0x046d 0xc508 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Cordless MX Duo Receiver +consoleUserPerms 0x0003 0x046d 0xc50b 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# MX100 Laser Mouse +consoleUserPerms 0x0003 0x046d 0xc50e 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Receiver for Cordless Presenter +consoleUserPerms 0x0003 0x046d 0xc702 0x0000 0xffff 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 diff --git a/kcms/input/logitechmouse_base.ui b/kcms/input/logitechmouse_base.ui new file mode 100644 index 0000000000..13ac59e511 --- /dev/null +++ b/kcms/input/logitechmouse_base.ui @@ -0,0 +1,134 @@ + + LogitechMouseBase + + + + + + Cordless Name + + + false + + + + + + + false + + + Sensor Resolution + + + + + + 400 counts per inch + + + 1 + + + + + + + 800 counts per inch + + + 1 + + + + + + + + + + false + + + Battery Level + + + + + + 7 + + + + + + + + + + false + + + RF Channel + + + + + + Channel 1 + + + true + + + 3 + + + + + + + false + + + Channel 2 + + + 3 + + + + + + + + + + You have a Logitech Mouse connected, and libusb was found at compile time, but it was not possible to access this mouse. This is probably caused by a permissions problem - you should consult the manual on how to fix this. + + + Qt::AlignVCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/kcms/input/main.cpp b/kcms/input/main.cpp new file mode 100644 index 0000000000..e68044c1ec --- /dev/null +++ b/kcms/input/main.cpp @@ -0,0 +1,94 @@ +/* + * main.cpp + * + * Copyright (c) 1999 Matthias Hoelzer-Kluepfel + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include "mouse.h" +#include + +#include + +#include +#ifdef HAVE_XCURSOR +# include +#endif + +extern "C" +{ + Q_DECL_EXPORT void kcminit_mouse() + { + KConfig *config = new KConfig("kcminputrc", KConfig::NoGlobals ); + MouseSettings settings; + settings.load(config); + settings.apply(true); // force + +#ifdef HAVE_XCURSOR + KConfigGroup group = config->group("Mouse"); + QString theme = group.readEntry("cursorTheme", QString()); + QString size = group.readEntry("cursorSize", QString()); + + // Note: If you update this code, update kapplymousetheme as well. + + // use a default value for theme only if it's not configured at all, not even in X resources + if( theme.isEmpty() + && QByteArray( XGetDefault( QX11Info::display(), "Xcursor", "theme" )).isEmpty() + && QByteArray( XcursorGetTheme( QX11Info::display())).isEmpty()) + { + theme = "default"; + } + + // Apply the KDE cursor theme to ourselves + if( !theme.isEmpty()) + XcursorSetTheme(QX11Info::display(), QFile::encodeName(theme)); + + if (!size.isEmpty()) + XcursorSetDefaultSize(QX11Info::display(), size.toUInt()); + + // Load the default cursor from the theme and apply it to the root window. + Cursor handle = XcursorLibraryLoadCursor(QX11Info::display(), "left_ptr"); + XDefineCursor(QX11Info::display(), QX11Info::appRootWindow(), handle); + XFreeCursor(QX11Info::display(), handle); // Don't leak the cursor + + // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment + // variables when launching applications. + OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), + QStringLiteral("/KLauncher"), + QDBusConnection::sessionBus()); + if(!theme.isEmpty()) + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); + if( !size.isEmpty()) + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); + +#endif + + delete config; + } +} + + diff --git a/kcms/input/mouse.cpp b/kcms/input/mouse.cpp new file mode 100644 index 0000000000..a3847ba66e --- /dev/null +++ b/kcms/input/mouse.cpp @@ -0,0 +1,821 @@ +/* + * mouse.cpp + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Layout management, enhancements: + * Copyright (c) 1999 Dirk A. Mueller + * + * SC/DC/AutoSelect/ChangeCursor: + * Copyright (c) 2000 David Faure + * + * Double click interval, drag time & dist + * Copyright (c) 2000 Bernd Gehrmann + * + * Large cursor support + * Visual activation TODO: speed + * Copyright (c) 2000 Rik Hemsley + * + * White cursor support + * TODO: give user the option to choose a certain cursor font + * -> Theming + * + * General/Advanced tabs + * Copyright (c) 2000 Brad Hughes + * + * redesign for KDE 2.2 + * Copyright (c) 2001 Ralf Nolden + * + * Logitech mouse support + * Copyright (C) 2004 Brad Hards + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#undef Below +#undef Above +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mouse.h" + +#include +#include +#include +#include +#include + +#undef Below + + +K_PLUGIN_FACTORY(MouseConfigFactory, + registerPlugin(); // mouse + ) +K_EXPORT_PLUGIN(MouseConfigFactory("kcminput")) + +MouseConfig::MouseConfig(QWidget *parent, const QVariantList &args) + : KCModule(parent, args) +{ + + + setQuickHelp( i18n("

    Mouse

    This module allows you to choose various" + " options for the way in which your pointing device works. Your" + " pointing device may be a mouse, trackball, or some other hardware" + " that performs a similar function.")); + + QString wtstr; + + QBoxLayout *top = new QVBoxLayout(this); + top->setMargin(0); + + tabwidget = new QTabWidget(this); + top->addWidget(tabwidget); + + generalTab = new KMouseDlg(this); + QButtonGroup *group = new QButtonGroup( generalTab ); + group->setExclusive( true ); + group->addButton( generalTab->singleClick ); + group->addButton( generalTab->doubleClick ); + + tabwidget->addTab(generalTab, i18n("&General")); + + group = new QButtonGroup( generalTab ); + group->setExclusive( true ); + group->addButton( generalTab->rightHanded,RIGHT_HANDED ); + group->addButton( generalTab->leftHanded,LEFT_HANDED ); + + connect(group, SIGNAL(buttonClicked(int)), this, SLOT(changed())); + connect(group, SIGNAL(buttonClicked(int)), this, SLOT(slotHandedChanged(int))); + + wtstr = i18n("If you are left-handed, you may prefer to swap the" + " functions of the left and right buttons on your pointing device" + " by choosing the 'left-handed' option. If your pointing device" + " has more than two buttons, only those that function as the" + " left and right buttons are affected. For example, if you have" + " a three-button mouse, the middle button is unaffected."); + generalTab->handedBox->setWhatsThis( wtstr ); + + connect(generalTab->doubleClick, SIGNAL(clicked()), SLOT(changed())); + + wtstr = i18n("The default behavior in KDE is to select and activate" + " icons with a single click of the left button on your pointing" + " device. This behavior is consistent with what you would expect" + " when you click links in most web browsers. If you would prefer" + " to select with a single click, and activate with a double click," + " check this option."); + generalTab->doubleClick->setWhatsThis( wtstr ); + + wtstr = i18n("Activates and opens a file or folder with a single click."); + generalTab->singleClick->setWhatsThis( wtstr ); + + + connect(generalTab->cbAutoSelect, SIGNAL(clicked()), this, SLOT(changed())); + + wtstr = i18n("If you check this option, pausing the mouse pointer" + " over an icon on the screen will automatically select that icon." + " This may be useful when single clicks activate icons, and you" + " want only to select the icon without activating it."); + generalTab->cbAutoSelect->setWhatsThis( wtstr ); + + wtstr = i18n("If you have checked the option to automatically select" + " icons, this slider allows you to select how long the mouse pointer" + " must be paused over the icon before it is selected."); + generalTab->slAutoSelect->setWhatsThis( wtstr ); + + connect(generalTab->slAutoSelect, SIGNAL(valueChanged(int)), this, SLOT(changed())); + + connect(generalTab->cb_pointershape, SIGNAL(clicked()), this, SLOT(changed())); + + connect(generalTab->singleClick, SIGNAL(clicked()), this, SLOT(changed())); + connect(generalTab->singleClick, SIGNAL(clicked()), this, SLOT(slotClick())); + connect(generalTab->singleClick, SIGNAL(clicked()), this, SLOT(slotSmartSliderEnabling())); + + connect( generalTab->doubleClick, SIGNAL(clicked()), this, SLOT(slotClick()) ); + connect( generalTab->cbAutoSelect, SIGNAL(clicked()), this, SLOT(slotClick()) ); + connect(generalTab->cbAutoSelect, SIGNAL(clicked()), this, SLOT(slotSmartSliderEnabling())); + + + // Only allow setting reversing scroll polarity if we have scroll buttons + unsigned char map[20]; + if ( XGetPointerMapping(QX11Info::display(), map, 20) >= 5 ) + { + generalTab->cbScrollPolarity->setEnabled( true ); + generalTab->cbScrollPolarity->show(); + } + else + { + generalTab->cbScrollPolarity->setEnabled( false ); + generalTab->cbScrollPolarity->hide(); + } + connect(generalTab->cbScrollPolarity, SIGNAL(clicked()), this, SLOT(changed())); + connect(generalTab->cbScrollPolarity, SIGNAL(clicked()), this, SLOT(slotScrollPolarityChanged())); + + // Advanced tab + advancedTab = new QWidget(0); + advancedTab->setObjectName("Advanced Tab"); + tabwidget->addTab(advancedTab, i18n("Advanced")); + + QFormLayout *lay = new QFormLayout(advancedTab); + + accel = new KDoubleNumInput(0.1, 20, 2, advancedTab, 0.1, 1); + accel->setSuffix(i18n(" x")); + lay->addRow(i18n("Pointer acceleration:"), accel); + connect(accel, SIGNAL(valueChanged(double)), this, SLOT(changed())); + + wtstr = i18n("

    This option allows you to change the relationship" + " between the distance that the mouse pointer moves on the" + " screen and the relative movement of the physical device" + " itself (which may be a mouse, trackball, or some other" + " pointing device.)

    " + " A high value for the acceleration will lead to large" + " movements of the mouse pointer on the screen even when" + " you only make a small movement with the physical device." + " Selecting very high values may result in the mouse pointer" + " flying across the screen, making it hard to control.

    "); + accel->setWhatsThis( wtstr ); + + thresh = new KIntNumInput(20, advancedTab); + thresh->setRange(0,20,1); + thresh->setSteps(1,1); + lay->addRow(i18n("Pointer threshold:"), thresh); + connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(slotThreshChanged(int))); + slotThreshChanged(thresh->value()); + + wtstr = i18n("

    The threshold is the smallest distance that the" + " mouse pointer must move on the screen before acceleration" + " has any effect. If the movement is smaller than the threshold," + " the mouse pointer moves as if the acceleration was set to 1X;

    " + " thus, when you make small movements with the physical device," + " there is no acceleration at all, giving you a greater degree" + " of control over the mouse pointer. With larger movements of" + " the physical device, you can move the mouse pointer" + " rapidly to different areas on the screen.

    "); + thresh->setWhatsThis( wtstr ); + + // It would be nice if the user had a test field. + // Selecting such values in milliseconds is not intuitive + doubleClickInterval = new KIntNumInput(2000, advancedTab); + doubleClickInterval->setRange(0, 2000, 100); + doubleClickInterval->setSuffix(i18n(" msec")); + doubleClickInterval->setSteps(100, 100); + lay->addRow(i18n("Double click interval:"), doubleClickInterval); + connect(doubleClickInterval, SIGNAL(valueChanged(int)), this, SLOT(changed())); + + wtstr = i18n("The double click interval is the maximal time" + " (in milliseconds) between two mouse clicks which" + " turns them into a double click. If the second" + " click happens later than this time interval after" + " the first click, they are recognized as two" + " separate clicks."); + doubleClickInterval->setWhatsThis( wtstr ); + + dragStartTime = new KIntNumInput(2000, advancedTab); + dragStartTime->setRange(0, 2000, 100); + dragStartTime->setSuffix(i18n(" msec")); + dragStartTime->setSteps(100, 100); + lay->addRow(i18n("Drag start time:"), dragStartTime); + connect(dragStartTime, SIGNAL(valueChanged(int)), this, SLOT(changed())); + + wtstr = i18n("If you click with the mouse (e.g. in a multi-line" + " editor) and begin to move the mouse within the" + " drag start time, a drag operation will be initiated."); + dragStartTime->setWhatsThis( wtstr ); + + dragStartDist = new KIntNumInput(20, advancedTab); + dragStartDist->setRange(1, 20, 1); + dragStartDist->setSteps(1,1); + lay->addRow(i18n("Drag start distance:"), dragStartDist); + connect(dragStartDist, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(dragStartDist, SIGNAL(valueChanged(int)), this, SLOT(slotDragStartDistChanged(int))); + slotDragStartDistChanged(dragStartDist->value()); + + wtstr = i18n("If you click with the mouse and begin to move the" + " mouse at least the drag start distance, a drag" + " operation will be initiated."); + dragStartDist->setWhatsThis( wtstr ); + + wheelScrollLines = new KIntNumInput(3, advancedTab); + wheelScrollLines->setRange(1, 12, 1); + wheelScrollLines->setSteps(1,1); + lay->addRow(i18n("Mouse wheel scrolls by:"), wheelScrollLines); + connect(wheelScrollLines, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(wheelScrollLines, SIGNAL(valueChanged(int)), SLOT(slotWheelScrollLinesChanged(int))); + slotWheelScrollLinesChanged(wheelScrollLines->value()); + + wtstr = i18n("If you use the wheel of a mouse, this value determines the number of lines to scroll for each wheel movement. Note that if this number exceeds the number of visible lines, it will be ignored and the wheel movement will be handled as a page up/down movement."); + wheelScrollLines->setWhatsThis(wtstr); + +{ + QWidget *mouse = new QWidget(this); + mouse->setObjectName("Mouse Navigation"); + tabwidget->addTab(mouse, i18n("Mouse Navigation")); + + QFormLayout *form = new QFormLayout(mouse); + + mouseKeys = new QCheckBox(i18n("&Move pointer with keyboard (using the num pad)"), mouse); + form->addRow(mouseKeys); + + mk_delay = new KIntNumInput(mouse); + mk_delay->setRange(1, 1000, 50); + mk_delay->setSuffix(i18n(" msec")); + form->addRow(i18n("&Acceleration delay:"), mk_delay); + + mk_interval = new KIntNumInput(0, mouse); + mk_interval->setRange(1, 1000, 10); + mk_interval->setSuffix(i18n(" msec")); + form->addRow(i18n("R&epeat interval:"), mk_interval); + + mk_time_to_max = new KIntNumInput(0, mouse); + mk_time_to_max->setRange(100, 10000, 200); + mk_time_to_max->setSuffix(i18n(" msec")); + form->addRow(i18n("Acceleration &time:"), mk_time_to_max); + + mk_max_speed = new KIntNumInput(0, mouse); + mk_max_speed->setRange(1, 2000, 20); + mk_max_speed->setSuffix(i18n(" pixel/sec")); + form->addRow(i18n("Ma&ximum speed:"), mk_max_speed); + + mk_curve = new KIntNumInput(0, mouse); + mk_curve->setRange(-1000, 1000, 100); + form->addRow(i18n("Acceleration &profile:"), mk_curve); + + connect(mouseKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); + connect(mouseKeys, SIGNAL(clicked()), this, SLOT(changed())); + connect(mk_delay, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(mk_interval, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(mk_time_to_max, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(mk_max_speed, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(mk_curve, SIGNAL(valueChanged(int)), this, SLOT(changed())); +} + + settings = new MouseSettings; + + // This part is for handling features on Logitech USB mice. + // It only works if libusb is available. +#ifdef HAVE_LIBUSB + + static const + struct device_table { + int idVendor; + int idProduct; + const char* Model; + const char* Name; + int flags; + } device_table[] = { + { VENDOR_LOGITECH, 0xC00E, "M-BJ58", "Wheel Mouse Optical", HAS_RES }, + { VENDOR_LOGITECH, 0xC00F, "M-BJ79", "MouseMan Traveler", HAS_RES }, + { VENDOR_LOGITECH, 0xC012, "M-BL63B", "MouseMan Dual Optical", HAS_RES }, + { VENDOR_LOGITECH, 0xC01B, "M-BP86", "MX310 Optical Mouse", HAS_RES }, + { VENDOR_LOGITECH, 0xC01D, "M-BS81A", "MX510 Optical Mouse", HAS_RES | HAS_SS | HAS_SSR }, + { VENDOR_LOGITECH, 0xC024, "M-BP82", "MX300 Optical Mouse", HAS_RES }, + { VENDOR_LOGITECH, 0xC025, "M-BP81A", "MX500 Optical Mouse", HAS_RES | HAS_SS | HAS_SSR }, + { VENDOR_LOGITECH, 0xC031, "M-UT58A", "iFeel Mouse (silver)", HAS_RES }, + { VENDOR_LOGITECH, 0xC501, "C-BA4-MSE", "Mouse Receiver", HAS_CSR }, + { VENDOR_LOGITECH, 0xC502, "C-UA3-DUAL", "Dual Receiver", HAS_CSR | USE_CH2}, + { VENDOR_LOGITECH, 0xC504, "C-BD9-DUAL", "Cordless Freedom Optical", HAS_CSR | USE_CH2 }, + { VENDOR_LOGITECH, 0xC505, "C-BG17-DUAL", "Cordless Elite Duo", HAS_SS | HAS_SSR | HAS_CSR | USE_CH2}, + { VENDOR_LOGITECH, 0xC506, "C-BF16-MSE", "MX700 Optical Mouse", HAS_SS | HAS_CSR }, + { VENDOR_LOGITECH, 0xC508, "C-BA4-MSE", "Cordless Optical TrackMan", HAS_SS | HAS_CSR }, + { VENDOR_LOGITECH, 0xC50B, "967300-0403", "Cordless MX Duo Receiver", HAS_SS|HAS_CSR }, + { VENDOR_LOGITECH, 0xC50E, "M-RAG97", "MX1000 Laser Mouse", HAS_SS | HAS_CSR }, + { VENDOR_LOGITECH, 0xC702, "C-UF15", "Receiver for Cordless Presenter", HAS_CSR }, + { 0, 0, 0, 0, 0 } + }; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + struct usb_bus *bus; + struct usb_device *dev; + + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + for (int n = 0; device_table[n].idVendor; n++) + if ( (device_table[n].idVendor == dev->descriptor.idVendor) && + (device_table[n].idProduct == dev->descriptor.idProduct) ) { + // OK, we have a device that appears to be one of the ones we support + LogitechMouse *mouse = new LogitechMouse( dev, device_table[n].flags, this, device_table[n].Name ); + settings->logitechMouseList.append(mouse); + tabwidget->addTab( (QWidget*)mouse, device_table[n].Name ); + } + } + } + +#endif + + KAboutData* about = new KAboutData(QStringLiteral("kcmmouse"), QString(), i18n("Mouse"), QString(), QString(), + KAboutData::License_GPL, i18n("(c) 1997 - 2005 Mouse developers")); + about->addAuthor(i18n("Patrick Dowler")); + about->addAuthor(i18n("Dirk A. Mueller")); + about->addAuthor(i18n("David Faure")); + about->addAuthor(i18n("Bernd Gehrmann")); + about->addAuthor(i18n("Rik Hemsley")); + about->addAuthor(i18n("Brad Hughes")); + about->addAuthor(i18n("Ralf Nolden")); + about->addAuthor(i18n("Brad Hards")); + setAboutData( about ); +} + +void MouseConfig::checkAccess() +{ + mk_delay->setEnabled(mouseKeys->isChecked()); + mk_interval->setEnabled(mouseKeys->isChecked()); + mk_time_to_max->setEnabled(mouseKeys->isChecked()); + mk_max_speed->setEnabled(mouseKeys->isChecked()); + mk_curve->setEnabled(mouseKeys->isChecked()); +} + + +MouseConfig::~MouseConfig() +{ + delete settings; +} + +double MouseConfig::getAccel() +{ + return accel->value(); +} + +void MouseConfig::setAccel(double val) +{ + accel->setValue(val); +} + +int MouseConfig::getThreshold() +{ + return thresh->value(); +} + +void MouseConfig::setThreshold(int val) +{ + thresh->setValue(val); +} + + +int MouseConfig::getHandedness() +{ + if (generalTab->rightHanded->isChecked()) + return RIGHT_HANDED; + else + return LEFT_HANDED; +} + +void MouseConfig::setHandedness(int val) +{ + generalTab->rightHanded->setChecked(false); + generalTab->leftHanded->setChecked(false); + if (val == RIGHT_HANDED){ + generalTab->rightHanded->setChecked(true); + generalTab->mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); + } + else{ + generalTab->leftHanded->setChecked(true); + generalTab->mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); + } + settings->m_handedNeedsApply = true; +} + +void MouseConfig::load() +{ + KConfig config( "kcminputrc" ); + settings->load(&config); + + generalTab->rightHanded->setEnabled(settings->handedEnabled); + generalTab->leftHanded->setEnabled(settings->handedEnabled); + if ( generalTab->cbScrollPolarity->isEnabled() ) + generalTab->cbScrollPolarity->setEnabled(settings->handedEnabled); + generalTab->cbScrollPolarity->setChecked( settings->reverseScrollPolarity ); + + setAccel(settings->accelRate); + setThreshold(settings->thresholdMove); + setHandedness(settings->handed); + + doubleClickInterval->setValue(settings->doubleClickInterval); + dragStartTime->setValue(settings->dragStartTime); + dragStartDist->setValue(settings->dragStartDist); + wheelScrollLines->setValue(settings->wheelScrollLines); + + generalTab->singleClick->setChecked( settings->singleClick ); + generalTab->doubleClick->setChecked(!settings->singleClick); + generalTab->cb_pointershape->setChecked(settings->changeCursor); + generalTab->cbAutoSelect->setChecked( settings->autoSelectDelay >= 0 ); + if ( settings->autoSelectDelay < 0 ) + generalTab->slAutoSelect->setValue( 0 ); + else + generalTab->slAutoSelect->setValue( settings->autoSelectDelay ); + slotClick(); + + + KConfig ac("kaccessrc"); + + KConfigGroup group = ac.group("Mouse"); + mouseKeys->setChecked(group.readEntry("MouseKeys", false)); + mk_delay->setValue(group.readEntry("MKDelay", 160)); + + int interval = group.readEntry("MKInterval", 5); + mk_interval->setValue(interval); + + // Default time to reach maximum speed: 5000 msec + int time_to_max = group.readEntry("MKTimeToMax", (5000+interval/2)/interval); + time_to_max = group.readEntry("MK-TimeToMax", time_to_max*interval); + mk_time_to_max->setValue(time_to_max); + + // Default maximum speed: 1000 pixels/sec + // (The old default maximum speed from KDE <= 3.4 + // (100000 pixels/sec) was way too fast) + long max_speed = group.readEntry("MKMaxSpeed", interval); + max_speed = max_speed * 1000 / interval; + if (max_speed > 2000) + max_speed = 2000; + max_speed = group.readEntry("MK-MaxSpeed", int(max_speed)); + mk_max_speed->setValue(max_speed); + + mk_curve->setValue(group.readEntry("MKCurve", 0)); + + checkAccess(); + + emit changed(false); +} + +void MouseConfig::save() +{ + settings->accelRate = getAccel(); + settings->thresholdMove = getThreshold(); + settings->handed = getHandedness(); + + settings->doubleClickInterval = doubleClickInterval->value(); + settings->dragStartTime = dragStartTime->value(); + settings->dragStartDist = dragStartDist->value(); + settings->wheelScrollLines = wheelScrollLines->value(); + settings->singleClick = !generalTab->doubleClick->isChecked(); + settings->autoSelectDelay = generalTab->cbAutoSelect->isChecked()? generalTab->slAutoSelect->value():-1; +// settings->changeCursor = generalTab->singleClick->isChecked(); + settings->changeCursor = generalTab->cb_pointershape->isChecked(); + settings->reverseScrollPolarity = generalTab->cbScrollPolarity->isChecked(); + + settings->apply(); + KConfig config( "kcminputrc" ); + settings->save(&config); + + KConfig ac("kaccessrc"); + + KConfigGroup group = ac.group("Mouse"); + + int interval = mk_interval->value(); + group.writeEntry("MouseKeys", mouseKeys->isChecked()); + group.writeEntry("MKDelay", mk_delay->value()); + group.writeEntry("MKInterval", interval); + group.writeEntry("MK-TimeToMax", mk_time_to_max->value()); + group.writeEntry("MKTimeToMax", + (mk_time_to_max->value() + interval/2)/interval); + group.writeEntry("MK-MaxSpeed", mk_max_speed->value()); + group.writeEntry("MKMaxSpeed", + (mk_max_speed->value()*interval + 500)/1000); + group.writeEntry("MKCurve", mk_curve->value()); + group.sync(); + group.writeEntry("MKCurve", mk_curve->value()); + + // restart kaccess + KToolInvocation::startServiceByDesktopName("kaccess"); + + emit changed(false); +} + +void MouseConfig::defaults() +{ + setThreshold(2); + setAccel(2); + setHandedness(RIGHT_HANDED); + generalTab->cbScrollPolarity->setChecked( false ); + doubleClickInterval->setValue(400); + dragStartTime->setValue(500); + dragStartDist->setValue(4); + wheelScrollLines->setValue(3); + generalTab->doubleClick->setChecked( !KDE_DEFAULT_SINGLECLICK ); + generalTab->cbAutoSelect->setChecked( KDE_DEFAULT_AUTOSELECTDELAY != -1 ); + generalTab->slAutoSelect->setValue( KDE_DEFAULT_AUTOSELECTDELAY == -1 ? 50 : KDE_DEFAULT_AUTOSELECTDELAY ); + generalTab->singleClick->setChecked( KDE_DEFAULT_SINGLECLICK ); + generalTab->cb_pointershape->setChecked(KDE_DEFAULT_CHANGECURSOR); + slotClick(); + + mouseKeys->setChecked(false); + mk_delay->setValue(160); + mk_interval->setValue(5); + mk_time_to_max->setValue(5000); + mk_max_speed->setValue(1000); + mk_curve->setValue(0); + + checkAccess(); + + changed(); +} + +void MouseConfig::slotClick() +{ + // Autoselect has a meaning only in single-click mode + generalTab->cbAutoSelect->setEnabled(!generalTab->doubleClick->isChecked() || generalTab->singleClick->isChecked()); + // Delay has a meaning only for autoselect + bool bDelay = generalTab->cbAutoSelect->isChecked() && ! generalTab->doubleClick->isChecked(); + generalTab->slAutoSelect->setEnabled( bDelay ); +} + +/** No descriptions */ +void MouseConfig::slotHandedChanged(int val){ + if(val==RIGHT_HANDED) + generalTab->mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); + else + generalTab->mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); + settings->m_handedNeedsApply = true; +} + +void MouseSettings::load(KConfig *config) +{ + int accel_num, accel_den, threshold; + double accel; + XGetPointerControl( QX11Info::display(), + &accel_num, &accel_den, &threshold ); + accel = float(accel_num) / float(accel_den); + + // get settings from X server + int h = RIGHT_HANDED; + unsigned char map[20]; + num_buttons = XGetPointerMapping(QX11Info::display(), map, 20); + + handedEnabled = true; + + // ## keep this in sync with KGlobalSettings::mouseSettings + if( num_buttons == 1 ) + { + /* disable button remapping */ + handedEnabled = false; + } + else if( num_buttons == 2 ) + { + if ( (int)map[0] == 1 && (int)map[1] == 2 ) + h = RIGHT_HANDED; + else if ( (int)map[0] == 2 && (int)map[1] == 1 ) + h = LEFT_HANDED; + else + /* custom button setup: disable button remapping */ + handedEnabled = false; + } + else + { + middle_button = (int)map[1]; + if ( (int)map[0] == 1 && (int)map[2] == 3 ) + h = RIGHT_HANDED; + else if ( (int)map[0] == 3 && (int)map[2] == 1 ) + h = LEFT_HANDED; + else + { + /* custom button setup: disable button remapping */ + handedEnabled = false; + } + } + + KConfigGroup group = config->group("Mouse"); + double a = group.readEntry("Acceleration",-1.0); + if (a == -1) + accelRate = accel; + else + accelRate = a; + + int t = group.readEntry("Threshold",-1); + if (t == -1) + thresholdMove = threshold; + else + thresholdMove = t; + + QString key = group.readEntry("MouseButtonMapping"); + if (key == "RightHanded") + handed = RIGHT_HANDED; + else if (key == "LeftHanded") + handed = LEFT_HANDED; +#ifdef __GNUC__ +#warning was key == NULL how was this working? is key.isNull() what the coder meant? +#endif + else if (key.isNull()) + handed = h; + reverseScrollPolarity = group.readEntry( "ReverseScrollPolarity", false); + m_handedNeedsApply = false; + + // SC/DC/AutoSelect/ChangeCursor + group = config->group("KDE"); + doubleClickInterval = group.readEntry("DoubleClickInterval", 400); + dragStartTime = group.readEntry("StartDragTime", 500); + dragStartDist = group.readEntry("StartDragDist", 4); + wheelScrollLines = group.readEntry("WheelScrollLines", 3); + + singleClick = group.readEntry("SingleClick", KDE_DEFAULT_SINGLECLICK); + autoSelectDelay = group.readEntry("AutoSelectDelay", KDE_DEFAULT_AUTOSELECTDELAY); + changeCursor = group.readEntry("ChangeCursor", KDE_DEFAULT_CHANGECURSOR); +} + +void MouseConfig::slotThreshChanged(int value) +{ + thresh->setSuffix(i18np(" pixel", " pixels", value)); +} + +void MouseConfig::slotDragStartDistChanged(int value) +{ + dragStartDist->setSuffix(i18np(" pixel", " pixels", value)); +} + +void MouseConfig::slotWheelScrollLinesChanged(int value) +{ + wheelScrollLines->setSuffix(i18np(" line", " lines", value)); +} + +void MouseSettings::apply(bool force) +{ + XChangePointerControl( QX11Info::display(), + true, true, int(qRound(accelRate*10)), 10, thresholdMove); + + // 256 might seems extreme, but X has already been known to return 32, + // and we don't want to truncate things. Xlib limits the table to 256 bytes, + // so it's a good uper bound.. + unsigned char map[256]; + num_buttons = XGetPointerMapping(QX11Info::display(), map, 256); + + int remap=(num_buttons>=1); + if (handedEnabled && (m_handedNeedsApply || force)) { + if( num_buttons == 1 ) + { + map[0] = (unsigned char) 1; + } + else if( num_buttons == 2 ) + { + if (handed == RIGHT_HANDED) + { + map[0] = (unsigned char) 1; + map[1] = (unsigned char) 3; + } + else + { + map[0] = (unsigned char) 3; + map[1] = (unsigned char) 1; + } + } + else // 3 buttons and more + { + if (handed == RIGHT_HANDED) + { + map[0] = (unsigned char) 1; + map[1] = (unsigned char) middle_button; + map[2] = (unsigned char) 3; + } + else + { + map[0] = (unsigned char) 3; + map[1] = (unsigned char) middle_button; + map[2] = (unsigned char) 1; + } + if( num_buttons >= 5 ) + { + // Apps seem to expect logical buttons 4,5 are the vertical wheel. + // With mice with more than 3 buttons (not including wheel) the physical + // buttons mapped to logical 4,5 may not be physical 4,5 , so keep + // this mapping, only possibly reversing them. + int pos; + for( pos = 0; pos < num_buttons; ++pos ) + if( map[pos] == 4 || map[pos] == 5 ) + break; + if( pos < num_buttons - 1 ) + { + map[pos] = reverseScrollPolarity ? (unsigned char) 5 : (unsigned char) 4; + map[pos+1] = reverseScrollPolarity ? (unsigned char) 4 : (unsigned char) 5; + } + } + } + int retval; + if (remap) + while ((retval=XSetPointerMapping(QX11Info::display(), map, + num_buttons)) == MappingBusy) + /* keep trying until the pointer is free */ + { }; + m_handedNeedsApply = false; + } + + // This iterates through the various Logitech mice, if we have support. + #ifdef HAVE_LIBUSB + LogitechMouse *logitechMouse; + Q_FOREACH( logitechMouse, logitechMouseList ) { + logitechMouse->applyChanges(); + } + #endif +} + +void MouseSettings::save(KConfig *config) +{ + KConfigGroup group = config->group("Mouse"); + group.writeEntry("Acceleration",accelRate); + group.writeEntry("Threshold",thresholdMove); + if (handed == RIGHT_HANDED) + group.writeEntry("MouseButtonMapping",QString("RightHanded")); + else + group.writeEntry("MouseButtonMapping",QString("LeftHanded")); + group.writeEntry( "ReverseScrollPolarity", reverseScrollPolarity ); + + group = config->group("KDE"); + group.writeEntry("DoubleClickInterval", doubleClickInterval, KConfig::Persistent|KConfig::Global); + group.writeEntry("StartDragTime", dragStartTime, KConfig::Persistent|KConfig::Global); + group.writeEntry("StartDragDist", dragStartDist, KConfig::Persistent|KConfig::Global); + group.writeEntry("WheelScrollLines", wheelScrollLines, KConfig::Persistent|KConfig::Global); + group.writeEntry("SingleClick", singleClick, KConfig::Persistent|KConfig::Global); + group.writeEntry("AutoSelectDelay", autoSelectDelay, KConfig::Persistent|KConfig::Global); + group.writeEntry("ChangeCursor", changeCursor,KConfig::Persistent|KConfig::Global); + // This iterates through the various Logitech mice, if we have support. +#ifdef HAVE_LIBUSB + LogitechMouse *logitechMouse; + Q_FOREACH( logitechMouse, logitechMouseList ) { + logitechMouse->save(config); + } +#endif + config->sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); +} + +void MouseConfig::slotScrollPolarityChanged() +{ + settings->m_handedNeedsApply = true; +} + +void MouseConfig::slotSmartSliderEnabling() +{ + bool enabled = generalTab->singleClick->isChecked() ? generalTab->cbAutoSelect->isChecked() : false; + generalTab->slAutoSelect->setEnabled(enabled); +} + +#include "mouse.moc" diff --git a/kcms/input/mouse.desktop b/kcms/input/mouse.desktop new file mode 100644 index 0000000000..a3ca207411 --- /dev/null +++ b/kcms/input/mouse.desktop @@ -0,0 +1,240 @@ +[Desktop Entry] +Exec=kcmshell5 mouse +Icon=preferences-desktop-mouse +Type=Service +X-KDE-ServiceTypes=KCModule,KCModuleInit +X-DocPath=kcontrol/mouse/index.html + +X-KDE-Library=kcm_input +X-KDE-Init-Symbol=mouse +X-KDE-ParentApp=kcontrol +X-KDE-OnlyShowOnQtPlatforms=xcb; + +X-KDE-System-Settings-Parent-Category=input-devices +X-KDE-Weight=60 + +Name=Mouse +Name[af]=Muis +Name[ar]=الفأرة +Name[ast]=Mur +Name[be]=Мыш +Name[be@latin]=Myš +Name[bg]=Мишка +Name[bn]=মাউস +Name[bn_IN]=মাউস +Name[br]=Logodenn +Name[bs]=Miš +Name[ca]=Ratolí +Name[ca@valencia]=Ratolí +Name[cs]=Myš +Name[csb]=Mësz +Name[cy]=Llygoden +Name[da]=Mus +Name[de]=Maus +Name[el]=Ποντίκι +Name[en_GB]=Mouse +Name[eo]=Muso +Name[es]=Ratón +Name[et]=Hiir +Name[eu]=Sagua +Name[fa]=موشی +Name[fi]=Hiiri +Name[fr]=Souris +Name[fy]=Mûs +Name[ga]=Luch +Name[gl]=Rato +Name[gu]=માઉસ +Name[he]=עכבר +Name[hi]=माउस +Name[hne]=मुसुवा +Name[hr]=Miš +Name[hsb]=Myš +Name[hu]=Egér +Name[ia]=Mus +Name[id]=Tetikus +Name[is]=Mús +Name[it]=Mouse +Name[ja]=マウス +Name[ka]=თაგვი +Name[kk]=Тышқан +Name[km]=កណ្ដុរ +Name[kn]=ಮೂಷಕ (ಮೌಸ್) +Name[ko]=마우스 +Name[ku]=Mişk +Name[lt]=Pelė +Name[lv]=Pele +Name[mai]=माउस +Name[mk]=Глушец +Name[ml]=മൌസ് +Name[mr]=माऊस +Name[ms]=Tetikus +Name[nb]=Mus +Name[nds]=Muus +Name[ne]=माउस +Name[nl]=Muis +Name[nn]=Mus +Name[oc]=Mirga +Name[or]=ମାଉସ +Name[pa]=ਮਾਊਸ +Name[pl]=Mysz +Name[pt]=Rato +Name[pt_BR]=Mouse +Name[ro]=Maus +Name[ru]=Мышь +Name[se]=Sáhpán +Name[si]=මවුසය +Name[sk]=Myš +Name[sl]=Miška +Name[sr]=Миш +Name[sr@ijekavian]=Миш +Name[sr@ijekavianlatin]=Miš +Name[sr@latin]=Miš +Name[sv]=Mus +Name[ta]=சுட்டி +Name[te]=మౌస్ +Name[tg]=Муш +Name[th]=เมาส์ +Name[tr]=Fare +Name[ug]=چاشقىنەك +Name[uk]=Мишка +Name[uz]=Sichqoncha +Name[uz@cyrillic]=Сичқонча +Name[vi]=Chuột +Name[wa]=Sori +Name[xh]=Mouse +Name[x-test]=xxMousexx +Name[zh_CN]=鼠标 +Name[zh_TW]=滑鼠 + +Comment=Mouse settings +Comment[af]=Muis instellings +Comment[ar]=إعدادات الفأرة +Comment[ast]=Preferencies sobre'l mur +Comment[be]=Настаўленні мышы +Comment[be@latin]=Nałady myšy +Comment[bg]=Настройки на мишка +Comment[bn]=মাউস সেটিংস +Comment[bn_IN]=মাউস সংক্রান্ত বৈশিষ্ট্য +Comment[br]=Kefluniañ al logodenn +Comment[bs]=Postavke miša +Comment[ca]=Arranjament del ratolí +Comment[ca@valencia]=Arranjament del ratolí +Comment[cs]=Nastavení myši +Comment[csb]=Nastôw mëszë +Comment[cy]=Gosodiadau Llygoden +Comment[da]=Indstilling af mus +Comment[de]=Einstellung der Maus +Comment[el]=Ρυθμίσεις Ποντικιού +Comment[en_GB]=Mouse settings +Comment[eo]=Agordo de la muso +Comment[es]=Preferencias sobre el ratón +Comment[et]=Hiire seadistused +Comment[eu]=Saguaren ezarpenak +Comment[fa]=تنظیمات موشی +Comment[fi]=Hiiren asetukset +Comment[fr]=Configuration de la souris +Comment[fy]=Mûsynstellings +Comment[ga]=Socruithe luiche +Comment[gl]=Configuración do rato +Comment[gu]=માઉસ ગોઠવણીઓ +Comment[he]=הגדרות העכבר +Comment[hi]=माउस विन्यास +Comment[hne]=मुसुवा सेटिंग +Comment[hr]=Postavke miša +Comment[hsb]=Nastajenja za myš +Comment[hu]=Az egér beállításai +Comment[ia]=Preferentias de mus +Comment[id]=Pengaturan tetikus +Comment[is]=Stillingar músar +Comment[it]=Impostazione del mouse +Comment[ja]=マウスの設定 +Comment[ka]=თაგვის კონფიგურაცია +Comment[kk]=Тышқан параметрлері +Comment[km]=ការ​កំណត់​កណ្ដុរ +Comment[kn]=ಮೂಷಕದ (ಮೌಸ್) ಸಂಯೋಜನೆಗಳು +Comment[ko]=마우스 설정 +Comment[ku]=Mîhengên Mişkî +Comment[lt]=Pelės parametrai +Comment[lv]=Peles parametri +Comment[mai]=माउस बिन्यास +Comment[mk]=Поставувања на глушецот +Comment[ml]=മൌസിന്റെ സജ്ജീകരണങ്ങള്‍ +Comment[mr]=माऊस संयोजना +Comment[ms]=Seting tetikus +Comment[nb]=Musinnstillinger +Comment[nds]=Muus instellen +Comment[ne]=माउस सेटिङ +Comment[nl]=Muisinstellingen +Comment[nn]=Musinnstillingar +Comment[or]=ମାଉସ ବିନ୍ୟାସ +Comment[pa]=ਮਾਊਸ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia myszy +Comment[pt]=Configuração do rato +Comment[pt_BR]=Configurações do mouse +Comment[ro]=Configurări maus +Comment[ru]=Настройка мыши +Comment[se]=Sáhpánheivehusat +Comment[si]=මවුසයේ සැකසුම් +Comment[sk]=Nastavenie myši +Comment[sl]=Nastavitve miške +Comment[sr]=Поставке миша +Comment[sr@ijekavian]=Поставке миша +Comment[sr@ijekavianlatin]=Postavke miša +Comment[sr@latin]=Postavke miša +Comment[sv]=Anpassa mus +Comment[ta]=சுட்டி அமைப்புகள் +Comment[te]=మౌస్ అమరికలు +Comment[tg]=Танзимоти муш +Comment[th]=ตั้งค่าต่างๆ ของเมาส์ +Comment[tr]=Fare ayarları +Comment[ug]=چاشقىنەك تەڭشەك +Comment[uk]=Налаштування миші +Comment[uz]=Sichqonchaning moslamalari +Comment[uz@cyrillic]=Сичқончанинг мосламалари +Comment[vi]=Thiết lập con chuột +Comment[wa]=Apontiaedjes del sori +Comment[xh]=Izicwangciso ze mouse +Comment[x-test]=xxMouse settingsxx +Comment[zh_CN]=鼠标设置 +Comment[zh_TW]=滑鼠設定 + +X-KDE-Keywords=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation +X-KDE-Keywords[bs]=Miš, Brzina kretanja miša, Prag osjetljivosti miša, Tasteri miša, Oblik kursora, Ulazni Uređaji, Mapiranje tastera, Klik, Ikone, feedback (povratna veza), Pokazivači, Vuči (drag), Dupli klik, Jedan Klik, Mapiranje, Dešnjak, Ljevak, Uređaj koji je Pokazivač (miš, Trackball, Joystick), Točkić za pomicanje (gore ili dolje), Emulatija Miša, Navigacija Miša, Miš Povucite i ispustite(drag and drop), Skrolanje Mišem, Osjetljivost Miša, Pomjeranje miša pomoću brojevne tastature, Emulacija miša pomoću brojevne tastature +X-KDE-Keywords[ca]=Ratolí,Acceleració de ratolí,Llindar de ratolí,Botons de ratolí,Selecció,Ombra de cursor,Dispositius d'entrada,Mapatge de boton,Clic,icones,reacció,Apuntadors,Arrossegar,Clic doble,Clic normal,mapatge,dretà,esquerrà,Dispositiu apuntador,Roda de ratolí,Emulació de ratolí,Navegació amb ratolí,Arrossegar i deixar anar de ratolí,Desplaçament amb ratolí,Sensitivitat de ratolí,Moure el ratolí amb teclat numèric,Emulació de ratolí amb teclat numèric +X-KDE-Keywords[ca@valencia]=Ratolí,Acceleració de ratolí,Llindar de ratolí,Botons de ratolí,Selecció,Ombra de cursor,Dispositius d'entrada,Mapatge de boton,Clic,icones,reacció,Apuntadors,Arrossegar,Clic doble,Clic normal,mapatge,dretà,esquerrà,Dispositiu apuntador,Roda de ratolí,Emulació de ratolí,Navegació amb ratolí,Arrossegar i deixar anar de ratolí,Desplaçament amb ratolí,Sensitivitat de ratolí,Moure el ratolí amb teclat numèric,Emulació de ratolí amb teclat numèric +X-KDE-Keywords[da]=Mus,Museacceleration,musetærskel,museknapper,markering,markørform,Input-enheder,knapkobling,klik,ikoner,feedback,Pointers,træk,dobbeltklik,enkeltklik,kobling,højrehåndet,venstrehåndet,pegeenhed,musehjul,museemulering,musenavigation,træk og slip,muserulning,scrolling,musefølsomhed,bevæg mus med numerisk tastatur,markører,cursor,rulning +X-KDE-Keywords[de]=Maus,Mausbeschleunigung,Mausschwellwert,Maustasten,Auswahl,Cursor,Cursor-Form,Eingabegeräte,Knöpfe,Buttons,Zuordnungen,Klicks,Zeigegeräte,Doppelklick,Rechtshänder,Linkshänder,Ziehen,Mausrad,Maus-Emulation,Maus-Navigation,Ziehen und Ablegen mit der Maus,Blättern mit der Maus,Maus-Empfindlichkeit,Mauszeiger mit der Zahlentastatur verschieben,Maus-Emulation mit der Zahlentastatur +X-KDE-Keywords[el]=ποντίκι,επιτάχυνση ποντικιού,κατώφλι ποντικιού,κουμπιά ποντικιού,επιλογή,σχήμα δρομέα,συσκευές εισόδου,χαρτογράφηση κουμπιών,κλικ,εικονίδια,ανάδραση,δείκτες,έλξη,διπλό κλικ,μονό κλικ,χαρτογράφηση,δεξιόχειρας,αριστερόχειρας,συσκευή δείκτη,τροχός ποντικιού,εξομοίωση ποντικιού,πλοήγηση ποντικιού,έλξη και απόθεση ποντικιού,κύλιση ποντικιού,ευαισθησία ποντικιού,κίνηση ποντικιού με το αριθμητικό πληκτρολόγιο,εξομοίωση ποντικιού αριθμητικού πληκτρολογίου +X-KDE-Keywords[es]=Ratón,Aceleración de ratón,Umbral del ratón,Botones del ratón,Selección,Forma del cursor,Dispositivos de entrada,Mapeo de botones,Clic,iconos,reacción,Punteros,Arrastrar,Doble clic,clic sencillo,mapeo,diestro,zurdo,Dispositivo apuntador,Rueda del ratón,Emulación del ratón,Navegación con el ratón,Arrastrar y soltar con el ratón,Desplazamiento con el ratón,Sensibilidad del ratón,Mover el ratón con el teclado numérico,Emulación del ratón con el teclado numérico +X-KDE-Keywords[et]=hiir,hiire kiirendamine,hiire lävi,hiirenupud,valik,kursori kuju,sisendseadmed,nuppude seostamine,klõps,klõpsamine,ikoonid,tagasiside,lohistamine,topeltklõps,ühekordne klõps,seostamine,paremakäelised,vasakukäelised,osutusseade,hiireratas,hiire emuleerimine,hiirega liikumine,hiirega lohistamine,hiirega kerimine,hiire tundlikkus,hiire liigutamine numbriklahvistikuga,hiire numbriklahvistiku emuleerimine +X-KDE-Keywords[eu]=sagu,saguaren azelerazio,saguaren atalase,saguaren botoiak,hautapena,kurtsorearen forma,sarrerako gailuak,botoiak mapatzea,klik,ikonoak,oharrak,erakusle,arrastatu,klik bikoitza,klik bakarra,mapatze,eskuina,ezkerra,erakuslearen gailu,saguaren gurpil,saguaren emulazio,sagu bidezko nabigazio,saguaren bidez arrastatu eta jaregin,saguaren bidez korritu,saguaren sentikortasuna,sagua zenbakizko teklatuarekin mugitzea,sagua emulatzea zenbakizko teklatuarekin +X-KDE-Keywords[fi]=Hiiri,Osoittimen kiihdytys,Osoittimen raja-arvo,Hiiripainikkeet,Valinta,Osoittimen muoto,Syöttölaitteet,Syötelaitteet,Painikkeiden kuvaus,Napsauta,Napsautus,kuvakkeet,tuntuma,Osoittimet,Raahaus,Kaksoisnapsautus,kuvaus,oikeakätinen,oikeakätisyys,vasenkätinen,vasenkätisyys,osoitinlaite,hiiren rulla,hiiren emulointi,hiirinavigointi,hiiren vedä ja pudota,hiiren vieritys,hiiren herkkyys,hiiren liikuttaminen numeronäppäimistöltä +X-KDE-Keywords[fr]=Souris, Accélération de la souris, Seuil de la souris, Boutons de la souris, Sélection, Forme du curseur, Périphériques d'entrée, affectation des boutons, Clic, icônes, réaction, Pointeurs, Glisser, Double clic, Simple clic, affectation, droitier, gaucher, Périphérique de pointage, Roulette de la souris, Émulation de la souris, Navigation de la souris, Glisser-déposer, Défilement de la souris, Sensibilité de la souris, Déplacement de la souris avec le pavé numérique, Émulation de la souris du pavé numérique +X-KDE-Keywords[gl]=rato, aceleración do rato, limiar do rato, botóns do rato, selección, escolla, forma do rato, dispositivos de entrada, mapas de botóns, premer, clic, iconas, punteiro, arrastrar, duplo clic, dobre clic, dereito, zurdo, dispositivo do punteiro, roda do rato, emulación do rato, navegación co rato, arrastrar e soltar co rato, sensibilidade do rato, mover o rato co teclado numérico, teclado numérico +X-KDE-Keywords[hu]=Egér,Egérgyorsítás,Egérküszöb,Egérgombok,Kijelölés,Kurzorforma,Beviteli eszközök,Gombhozzárendelés,Kattintás,ikonok,visszajelzés,Mutatók,Fogás,Dupla kattintás,Egyszeres kattintás,leképezés,jobb kezes,bal kezes,Mutató eszköz,Egérgörgő,Egéremuláció,Egérnavigáció,Egér fogd és vidd,Egérgörgetés,Egér érzékenység,Egér mozgatása numerikus billentyűkkel,Egér numerikus billentyűzet emuláció +X-KDE-Keywords[ia]=Mus,Acceleration de mus,Limine de mus,Buttones de mus,Selection,Forma de cursor, Dispositivos de ingresso,Mappatura de button,Click,icones,retro-information,Punctatores,Trahe,DupleClick,Singule click,mappar,con mano dextere, con mano sinistre,Dispositivo punctator,Rota de Mus,Emulation de mus,Navigation de mus,Traher e Poner con Mus, Rolar con Mus,Sensibilitate de mus, Move Mus con NumPad,Emulation Mus con NumPad +X-KDE-Keywords[it]=mouse,accelerazione del mouse,soglia del mouse,pulsanti del mouse,selezione,forma del cursore,dispositivi di ingresso,mappatura dei pulsanti,clic,icone,segnale di avvio,puntatori,trascinamento,doppio clic,singolo clic,mappatura,destrorso,mancino,dispositivo di puntamento,rotellina del mouse,emulazione del mouse,navigazione con il mouse, trascinamento con il mouse, scorrimento con il mouse, sensibilità del mouse,muovi il mouse con il tastierino numerico,emulazione mouse con il tastierino numerico +X-KDE-Keywords[kk]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation +X-KDE-Keywords[km]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation +X-KDE-Keywords[ko]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,마우스,마우스 가속,시간,커서 모양,입력 장치,클릭,드래그,왼손잡이,오른손잡이,마우스 휠,마우스 탐색, 마우스 스크롤,감도,스크롤,휠 +X-KDE-Keywords[mr]=माऊस, माऊस एक्सलरेशन, माऊस थ्रेशहोल्ड, माऊस बटन्स, सिलेक्शन, कर्सर शेप, इनपुट डिव्हाईस, बटन मेपिंग, क्लिक, आयकॉनस, फीडबेक, पॉईटर, ड्रग डबल, क्लिक, सिंगल क्लिक, मेपिंग, राईट हेन्डेड ,पॉईटर डिव्हाईस,माऊस, व्हील, माऊस इम्यूलेशन, माऊस,नेव्हीगेशन, माऊस ड्रेग व ड्रोप,न्यूम पॅड, माऊस न्यूम पॅड इम्यूलेशन +X-KDE-Keywords[nb]=Mus,Pekerakselerasjon,Pekerterskel,knapper,valg,pekerform,Inndataenheter,knappeavbildning,Klikk, ikoner,oppstartsmelding,Pekere,Dra,Dobbeltklikk,Enkeltklikk,Knapperekkefølge,høyrehendt,venstrehendt,Pekerenhet,Musehjul,Musemulering,Musenavigasjon,Dra og slipp med Mus, Muserulling,Flytt mus med talltastatur,Museemulering med talltastatur +X-KDE-Keywords[nds]=Muus,Muusbeslünigen,Wieser-Süll,Muusgauheit,Muusknööp,Köör,Blinkerform,Ingaavreedschappen,Knoop-Toornen,Klick,Lüttbiller,Rückmellen,Wiesers,Dregen,Dubbelklick,Towiesen,Rechtepoot,Linkepoot,Wiesreedschap,Muusrad,Muusemuleren,Muusstüern,Dregen un Droppen,Trecken,Afsetten,Fallenlaten,Rullen,Föhlsamkeit,Tallenblock +X-KDE-Keywords[nl]=muis,muisversnelling,muisdrempel,muisknoppen,selectie,cursorvorm,invoerapparaten,knoppenmapping,klik,pictogrammen,terugkoppeling,aanwijzer,slepen,dubbelklik,klik,mapping,rechtshandig,linkshandig,aanwijsapparaat,muiswiel,muisemulatie,muisnavigatie,slepen en laten vallen met muis,schuiven met de muis,muisgevoeligheid,muis bewegen met num-pad,emulatie van num-pad met muis +X-KDE-Keywords[pl]=Mysz,Przyspieszenie myszy,Próg myszy,Przyciski myszy,Zaznaczenie, Kształt kursora,Urządzenia wejścia,Mapowanie przycisków,Kliknięcie,ikony,odczucie,Wskaźniki,Przeciągnięcie,Podwójne kliknięcie,mapowanie,praworęczny,leworęczny, Urządzenie wskazujące,Rolka myszy,Emulacja myszy,Nawigacja myszą,Przeciąganie i upuszczanie myszą,Przewijanie myszą,Czułość myszy,Przemieszczaj myszą przy użyciu klawiatury numerycznej,Emulacja myszy klawiaturą numeryczną +X-KDE-Keywords[pt]=Rato,aceleração do rato,limiar do rato,botões do rato,selecção,forma do cursor,dispositivos de entrada,associação de botões,click,ícones,reacção,cursores,arrastar,duplo-click,associação,destro,esquerdino,ponteiro,roda do rato,emulação do rato,navegação do rato,arrastamento com o rato,deslocamento do rato,sensibilidade do rato,mover o rato com cursores,emulação do rato com teclado numérico +X-KDE-Keywords[pt_BR]=Mouse,aceleração do mouse,limiar do mouse,botões do mouse,seleção,forma do cursor,dispositivos de entrada,associação de botões,clique,ícones,feedback,ponteiros,arrastar,clique duplo,clique simples,associação,destro,canhoto, ponteiro,roda do mouse,emulação do mouse,navegação do mouse,arrastar e soltar com o mouse,deslocamento do mouse,sensibilidade do mouse,mover o mouse com o teclado numérico,emulação do mouse com o teclado numérico +X-KDE-Keywords[ru]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,мышь,ускорение мыши,границы мыши,кнопки мыши,выбор,форма курсора,устройство ввода,назначение клавиш,клик,значки,обратная связь,указатели,перемещение,двойной клик,двойной щелчок,одиночный клик,одиночный щелчок,правая рука,левая рука,праворукая,леворукая,колесо мыши,эмуляция мыши,управление мышью, навигация мыши,прокрутка,чувствительность,управление мышью через цифровую клавиатуру +X-KDE-Keywords[sk]=Myš,Zrýchlenie myši,Prah myši,Tlačidlá myši,Výber,Tvar kurzora,Vstupné zariadenia,Mapovanie tlačidiel,Klik,ikony,odozva,Ukazovatele,Drag,Dvojklik,Jednoduchý klik,mapovanie,pre pravákov,pre ľavákov,ukazovacie zariadenie,koliesko myši,emulácia myši,navigácia myši,drag and drop myši,rolovanie myši,citlivosť myši,pohyb myši numerickou klávesnicou,emulácia myši numerickou klávesnicou +X-KDE-Keywords[sl]=miška,pospešek miške,prag miške,gumbi miške,miškini gumbi,izbor,oblika kazalca,oblika kazalke,vhodne naprave,preslikava gumbov,klik,ikone,povratne informacije,odziv,kazalci,kazalke,vleka,dvojni klik,dvoklik,enojni klik,preslikava,desničar,levičar,kazalna naprava,kolešček miške,miškin kolešček,posnemanje miške,krmarjenje z miško,vleka in spuščanje z miško,pomikanje z miško,občutljivost miške,miškina občutljivost,premikanje miške s številčnico,posnemanje s številčnico +X-KDE-Keywords[sr]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,миш,убрзање миша,праг миша,дугмад миша,избор,облик показивача,облик курсора,улазни уређаји,мапирање дугмади,клик,иконе,одзив,превлачење,двоклик,десноруки,леворуки,показивачки уређај,точкић миша,осетљивост миша,кретање мишем,емулација миша,емулација миша преко нумеричке тастатуре +X-KDE-Keywords[sr@ijekavian]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,миш,убрзање миша,праг миша,дугмад миша,избор,облик показивача,облик курсора,улазни уређаји,мапирање дугмади,клик,иконе,одзив,превлачење,двоклик,десноруки,леворуки,показивачки уређај,точкић миша,осетљивост миша,кретање мишем,емулација миша,емулација миша преко нумеричке тастатуре +X-KDE-Keywords[sr@ijekavianlatin]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,miš,ubrzanje miša,prag miša,dugmad miša,izbor,oblik pokazivača,oblik kursora,ulazni uređaji,mapiranje dugmadi,klik,ikone,odziv,prevlačenje,dvoklik,desnoruki,levoruki,pokazivački uređaj,točkić miša,osetljivost miša,kretanje mišem,emulacija miša,emulacija miša preko numeričke tastature +X-KDE-Keywords[sr@latin]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,miš,ubrzanje miša,prag miša,dugmad miša,izbor,oblik pokazivača,oblik kursora,ulazni uređaji,mapiranje dugmadi,klik,ikone,odziv,prevlačenje,dvoklik,desnoruki,levoruki,pokazivački uređaj,točkić miša,osetljivost miša,kretanje mišem,emulacija miša,emulacija miša preko numeričke tastature +X-KDE-Keywords[sv]=Mus,Musacceleration,Muströskel,Musknappar,Val,Pekarform,Indataenheter,Knappavbildning,Klick,ikoner,återmatning,Pekare,Dra,Dubbelklick,avbildning,högerhänt,vänsterhänt,pekarenhet,mushjul,musemulering,musnavigering,mus drag och släpp,musrullning,muskänslighet,flytta musen med numeriskt tangentbord,emulering av mus med numeriskt tangentbord +X-KDE-Keywords[tr]=Fare,Fare Hızlandırma,Fare Eşiği,Fare Düğmeleri,Seçim,İşaretçi Şekli,Girdi Aygıtları,Düğme Haritalama,Tıklama,simgeler,geri bildirim,İşaretçiler,Sürükle,Çift Tıklama,Tek Tıklama,haritalama,sağlak,solak,İşaretçi Aygıtı,Fare Tekeri,Fare Benzetimi,Fare Gezinmesi,Fare Sürükle Bırak,Fare Kaydırması,Fare Hassasiyeti,Fareyi Sayısal Tuşlarla Hareket Ettir,Fare Sayısal Tuş Benzetimi +X-KDE-Keywords[uk]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,DoubleClick,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,миша,прискорення,поріг,порогове значення,кнопка,вибір,вказівник,форма,введення,кнопки,клацання,піктограми,значки,іконки,супровід,вказівники,перетягування,подвійне клацання,відповідність,праворукий,шульга,лівша,пристрій вказівника,коліщатко миші,емуляція миші,імітація миші,навігація за допомогою миші,перетягування зі скиданням мишею,гортання мишею,чутливість миші,миша з цифровою панеллю,емуляція цифрової панелі миші +X-KDE-Keywords[x-test]=xxMouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulationxx +X-KDE-Keywords[zh_CN]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation,鼠标,鼠标加速,鼠标阀值,鼠标按键,选择,光标形状,输入设备,按键映射,点击,图标,反馈,指针,拖拽,双击,映射,右手习惯,左手习惯,光标设备,指针设备,鼠标滚轮,鼠标模拟,鼠标导航,鼠标拖拽,鼠标滚动,鼠标灵敏度,小键盘移动鼠标,小键盘鼠标模拟 +X-KDE-Keywords[zh_TW]=Mouse,Mouse acceleration,Mouse threshold,Mouse buttons,Selection,Cursor Shape,Input Devices,Button Mapping,Click,icons,feedback,Pointers,Drag,Double Click,Single Click,mapping,right handed,left handed,Pointer Device,Mouse Wheel,Mouse Emulation,Mouse Navigation,Mouse Drag and Drop,Mouse Scrolling,Mouse Sensitivity,Move Mouse with Num Pad,Mouse Num Pad Emulation +Categories=Qt;KDE;X-KDE-settings-hardware; diff --git a/kcms/input/mouse.h b/kcms/input/mouse.h new file mode 100644 index 0000000000..58d92012e2 --- /dev/null +++ b/kcms/input/mouse.h @@ -0,0 +1,160 @@ +/* + * mouse.h + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Layout management, enhancements: + * Copyright (c) 1999 Dirk A. Mueller + * + * SC/DC/AutoSelect/ChangeCursor: + * Copyright (c) 2000 David Faure + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __MOUSECONFIG_H__ +#define __MOUSECONFIG_H__ + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#ifdef HAVE_LIBUSB +#include "logitechmouse.h" +#endif + +#include +#include "ui_kmousedlg.h" + +#define RIGHT_HANDED 0 +#define LEFT_HANDED 1 + +class QCheckBox; +class QSlider; +class QTabWidget; + +class KMouseDlg : public QWidget, public Ui::KMouseDlg +{ +public: + KMouseDlg( QWidget *parent ) : QWidget( parent ) { + setupUi( this ); + } +}; + + +class MouseSettings +{ +public: + void save(KConfig *); + void load(KConfig *); + void apply(bool force=false); +public: + int num_buttons; + int middle_button; + bool handedEnabled; + bool m_handedNeedsApply; + int handed; + double accelRate; + int thresholdMove; + int doubleClickInterval; + int dragStartTime; + int dragStartDist; + bool singleClick; + int autoSelectDelay; + bool changeCursor; + int wheelScrollLines; + bool reverseScrollPolarity; + + #ifdef HAVE_LIBUSB + // TODO: In Qt4, replace with a better container. + QList logitechMouseList; + #endif +}; + +class MouseConfig : public KCModule +{ + Q_OBJECT +public: + MouseConfig(QWidget *parent, const QVariantList &args); + ~MouseConfig(); + + void save(); + void load(); + void defaults(); + +private Q_SLOTS: + + void slotClick(); + /** No descriptions */ + void slotHandedChanged(int val); + void slotScrollPolarityChanged(); + void checkAccess(); + void slotThreshChanged(int value); + void slotDragStartDistChanged(int value); + void slotWheelScrollLinesChanged(int value); + void slotSmartSliderEnabling(); + +private: + + double getAccel(); + int getThreshold(); + int getHandedness(); + + void setAccel(double); + void setThreshold(int); + void setHandedness(int); + + KDoubleNumInput *accel; + KIntNumInput *thresh; + KIntNumInput *doubleClickInterval; + KIntNumInput *dragStartTime; + KIntNumInput *dragStartDist; + KIntNumInput *wheelScrollLines; + +// QRadioButton *leftHanded, *rightHanded; +// QCheckBox *doubleClick; +// QCheckBox *cbAutoSelect; + QLabel *lDelay; +// QSlider *slAutoSelect; +// QCheckBox *cbVisualActivate; +// QCheckBox *cbCursor; +// QCheckBox *cbLargeCursor; + + QTabWidget *tabwidget; + QWidget *advancedTab; + KMouseDlg* generalTab; + MouseSettings *settings; + + QCheckBox *mouseKeys; + KIntNumInput *mk_delay, *mk_interval, *mk_time_to_max, *mk_max_speed, + *mk_curve; + + +}; + +#endif + diff --git a/kcms/input/pics/CMakeLists.txt b/kcms/input/pics/CMakeLists.txt new file mode 100644 index 0000000000..4ef49ea13b --- /dev/null +++ b/kcms/input/pics/CMakeLists.txt @@ -0,0 +1 @@ +install( FILES mouse_rh.png mouse_lh.png DESTINATION ${DATA_INSTALL_DIR}/kcminput/pics ) diff --git a/kcms/input/pics/mouse_lh.png b/kcms/input/pics/mouse_lh.png new file mode 100644 index 0000000000..796d599425 Binary files /dev/null and b/kcms/input/pics/mouse_lh.png differ diff --git a/kcms/input/pics/mouse_lh.svgz b/kcms/input/pics/mouse_lh.svgz new file mode 100644 index 0000000000..fa44476a29 Binary files /dev/null and b/kcms/input/pics/mouse_lh.svgz differ diff --git a/kcms/input/pics/mouse_rh.png b/kcms/input/pics/mouse_rh.png new file mode 100644 index 0000000000..1d70d8979a Binary files /dev/null and b/kcms/input/pics/mouse_rh.png differ diff --git a/kcms/input/pics/mouse_rh.svgz b/kcms/input/pics/mouse_rh.svgz new file mode 100644 index 0000000000..fc2b069a8d Binary files /dev/null and b/kcms/input/pics/mouse_rh.svgz differ diff --git a/kcms/input/xcursor/cursortheme.cpp b/kcms/input/xcursor/cursortheme.cpp new file mode 100644 index 0000000000..1ea66886a9 --- /dev/null +++ b/kcms/input/xcursor/cursortheme.cpp @@ -0,0 +1,155 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "themepage.h" +#include "cursortheme.h" + +#include + +#ifdef HAVE_XFIXES +# include +# include +#endif + + +CursorTheme::CursorTheme(const QString &title, const QString &description) +{ + setTitle(title); + setDescription(description); + setSample("left_ptr"); + setIsHidden(false); + setIsWritable(false); +} + + +QPixmap CursorTheme::icon() const +{ + if (m_icon.isNull()) + m_icon = createIcon(); + + return m_icon; +} + + +QImage CursorTheme::autoCropImage(const QImage &image) const +{ + // Compute an autocrop rectangle for the image + QRect r(image.rect().bottomRight(), image.rect().topLeft()); + const quint32 *pixels = reinterpret_cast(image.bits()); + + for (int y = 0; y < image.height(); y++) + { + for (int x = 0; x < image.width(); x++) + { + if (*(pixels++)) + { + if (x < r.left()) r.setLeft(x); + if (x > r.right()) r.setRight(x); + if (y < r.top()) r.setTop(y); + if (y > r.bottom()) r.setBottom(y); + } + } + } + + // Normalize the rectangle + return image.copy(r.normalized()); +} + + +QPixmap CursorTheme::loadPixmap(const QString &name, int size) const +{ + QImage image = loadImage(name, size); + if (image.isNull()) + return QPixmap(); + + return QPixmap::fromImage(image); +} + + +static int nominalCursorSize(int iconSize) +{ + for (int i = 512; i > 8; i /= 2) + { + if (i < iconSize) + return i; + + if ((i * .75) < iconSize) + return int(i * .75); + } + + return 8; +} + + +QPixmap CursorTheme::createIcon() const +{ + int iconSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); + int cursorSize = nominalCursorSize(iconSize); + QSize size = QSize(iconSize, iconSize); + + QPixmap pixmap = createIcon(cursorSize); + + if (!pixmap.isNull()) + { + // Scale the pixmap if it's larger than the preferred icon size + if (pixmap.width() > size.width() || pixmap.height() > size.height()) + pixmap = pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + return pixmap; +} + + +QPixmap CursorTheme::createIcon(int size) const +{ + QPixmap pixmap; + QImage image = loadImage(sample(), size); + + if (image.isNull() && sample() != "left_ptr") + image = loadImage("left_ptr", size); + + if (!image.isNull()) + { + pixmap = QPixmap::fromImage(image); + } + + return pixmap; +} + + +void CursorTheme::setCursorName(qulonglong cursor, const QString &name) const +{ +#ifdef HAVE_XFIXES + static bool haveXfixes = ThemePage::haveXfixes(); + + if (haveXfixes) + { + XFixesSetCursorName(QX11Info::display(), cursor, + QFile::encodeName(name)); + } +#endif +} + diff --git a/kcms/input/xcursor/cursortheme.h b/kcms/input/xcursor/cursortheme.h new file mode 100644 index 0000000000..d280005f4c --- /dev/null +++ b/kcms/input/xcursor/cursortheme.h @@ -0,0 +1,143 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef CURSORTHEME_H +#define CURSORTHEME_H + +#include +#include + +/** + * This is the abstract base class for all cursor themes stored in a + * CursorThemeModel and previewed in a PreviewWidget. + * + * All cursor themes have a title, a description, an icon, and an internal + * name, all of which, except for the internal name, CursorThemeModel + * supplies to item views. + * + * A cursor theme may also have a path to the directory where the theme + * is located in the filesystem. If isWritable() returns true, This directory + * may be deleted in order to remove the theme at the users request. + * + * Subclasses must reimplement loadImage() and loadCursor(), which are + * called by PreviewWidget to load cursors and cursor images. Subclasses may + * optionally reimplement loadPixmap(), which in the default implementation + * calls loadImage(), and converts the returned image to a pixmap. + * Subclasses may also reimplement the protected function createIcon(), + * which creates the icon pixmap that's supplied to item views. The default + * implementation calls loadImage() to load the sample cursor, and creates + * the icon from that. + */ +class CursorTheme +{ + public: + enum ItemDataRole { + // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) + // to define additional roles. + DisplayDetailRole = 0x24A3DAF8 + }; + + CursorTheme() {} + CursorTheme(const QString &title, const QString &description = QString()); + virtual ~CursorTheme() {} + + const QString title() const { return m_title; } + const QString description() const { return m_description; } + const QString sample() const { return m_sample; } + const QString name() const { return m_name; } + const QString path() const { return m_path; } + /** @returns A list of the available sizes in this cursor theme, + @warning This list may be empty if the engine doesn't support + the recognition of the size. */ + const QList availableSizes() const + { return m_availableSizes; } + bool isWritable() const { return m_writable; } + bool isHidden() const { return m_hidden; } + QPixmap icon() const; + + /// Hash value for the internal name + uint hash() const { return m_hash; } + + /// Loads the cursor image @p name, with the nominal size @p size. + /// The image should be autocropped to the smallest possible size. + /// If the theme doesn't have the cursor @p name, it should return a null image. + virtual QImage loadImage(const QString &name, int size = 0) const = 0; + + /// Convenience function. Default implementation calls + /// QPixmap::fromImage(loadImage()); + virtual QPixmap loadPixmap(const QString &name, int size = 0) const; + + /// Loads the cursor @p name, with the nominal size @p size. + /// If the theme doesn't have the cursor @p name, it should return + /// the default cursor from the active theme instead. + virtual qulonglong loadCursor(const QString &name, int size = 0) const = 0; + + /** Creates the icon returned by @ref icon(). Don't use this function + directly but use @ref icon() instead, because @ref icon() caches + the icon. + @returns A pixmap with a cursor (usually left_ptr) that can + be used as icon for this theme. The size is adopted to + standard icon sizes.*/ + virtual QPixmap createIcon() const; + /** @returns A pixmap with a cursor (usually left_ptr) that can + be used as icon for this theme. */ + virtual QPixmap createIcon(int size) const; + + protected: + void setTitle( const QString &title ) { m_title = title; } + void setDescription( const QString &desc ) { m_description = desc; } + void setSample( const QString &sample ) { m_sample = sample; } + inline void setName( const QString &name ); + void setPath( const QString &path ) { m_path = path; } + void setAvailableSizes( const QList &availableSizes ) + { m_availableSizes = availableSizes; } + void setIcon( const QPixmap &icon ) { m_icon = icon; } + void setIsWritable( bool val ) { m_writable = val; } + void setIsHidden( bool val ) { m_hidden = val; } + + /// Convenience function for cropping an image. + QImage autoCropImage( const QImage &image ) const; + + // Convenience function that uses Xfixes to tag a cursor with a name + void setCursorName(qulonglong cursor, const QString &name) const; + + QString m_title; + QString m_description; + QString m_path; + QList m_availableSizes; + QString m_sample; + mutable QPixmap m_icon; + bool m_writable:1; + bool m_hidden:1; + + private: + QString m_name; + uint m_hash; + + friend class CursorThemeModel; +}; + +void CursorTheme::setName(const QString &name) +{ + m_name = name; + m_hash = qHash(name); +} + +#endif // CURSORTHEME_H + diff --git a/kcms/input/xcursor/itemdelegate.cpp b/kcms/input/xcursor/itemdelegate.cpp new file mode 100644 index 0000000000..e7370056e6 --- /dev/null +++ b/kcms/input/xcursor/itemdelegate.cpp @@ -0,0 +1,174 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "itemdelegate.h" +#include "cursortheme.h" + +#include +#include +#include + +namespace +{ + const int decorationMargin = 8; +} + + +ItemDelegate::ItemDelegate(QObject *parent) + : QAbstractItemDelegate(parent) +{ +} + + +ItemDelegate::~ItemDelegate() +{ +} + + +QString ItemDelegate::firstLine(const QModelIndex &index) const +{ + if (index.isValid()) + return index.model()->data(index, Qt::DisplayRole).toString(); + + return QString(); +} + + +QString ItemDelegate::secondLine(const QModelIndex &index) const +{ + if (index.isValid()) + return index.model()->data(index, CursorTheme::DisplayDetailRole).toString(); + + return QString(); +} + + +QPixmap ItemDelegate::decoration(const QModelIndex &index) const +{ + if (index.isValid()) + return qvariant_cast(index.model()->data(index, Qt::DecorationRole)); + + return QPixmap(); +} + + +QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (!index.isValid()) + return QSize(); + + QFont normalfont = option.font; + QFont boldfont = normalfont; + boldfont.setBold(true); + + // Extract the items we want to measure + QString firstRow = firstLine(index); + QString secondRow = secondLine(index); + + // Compute the height + QFontMetrics fm1(boldfont); + QFontMetrics fm2(normalfont); + int height = fm1.lineSpacing() + fm2.lineSpacing(); + height = qMax(height, option.decorationSize.height()); + + // Compute the text width + int width = fm1.width(firstRow); + width = qMax(width, fm2.width(secondRow)); + + // Add decoration width + margin + width += option.decorationSize.width() + decorationMargin; + + return QSize(width, height + 16); +} + + +QPalette::ColorRole ItemDelegate::foregroundRole(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + + if (option.state & QStyle::State_Selected) + return QPalette::HighlightedText; + + return QPalette::Text; +} + + +void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (!index.isValid()) + return; + + painter->save(); + + QFont normalfont = option.font; + QFont boldfont = normalfont; + boldfont.setBold(true); + + QString firstRow = firstLine(index); + QString secondRow = secondLine(index); + QPixmap pixmap = decoration(index); + + QColor textcol = option.palette.color(foregroundRole(option, index)); + + // Draw the background + QStyleOptionViewItemV4 opt = option; + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); + + // Draw the icon + int x = option.rect.left() + (option.decorationSize.width() - pixmap.width() + decorationMargin) / 2; + int y = option.rect.top() + (option.rect.height() - pixmap.height()) / 2; + + QRect pixmapRect = QStyle::visualRect(option.direction, option.rect, + QRect(x, y, pixmap.width(), pixmap.height())); + + painter->drawPixmap(pixmapRect.x(), pixmapRect.y(), pixmap); + + // Draw the text + QFontMetrics fm1(boldfont); + QFontMetrics fm2(normalfont); + + int textAreaHeight = fm1.lineSpacing(); + if (!secondRow.isEmpty()) { + textAreaHeight += fm2.lineSpacing(); + } + + x = option.rect.left() + option.decorationSize.width() + decorationMargin; + int y1 = option.rect.top() + (option.rect.height() - textAreaHeight) / 2; + int y2 = y1 + fm1.lineSpacing(); + + QRect firstRowRect = QStyle::visualRect(option.direction, option.rect, + QRect(x, y1, fm1.width(firstRow), fm1.lineSpacing())); + + QRect secondRowRect = QStyle::visualRect(option.direction, option.rect, + QRect(x, y2, fm2.width(secondRow), fm2.lineSpacing())); + + painter->setPen(textcol); + + // First line + painter->setFont(boldfont); + painter->drawText(firstRowRect, Qt::AlignCenter, firstRow); + + // Second line + painter->setFont(normalfont); + painter->drawText(secondRowRect, Qt::AlignCenter, secondRow); + + painter->restore(); +} + diff --git a/kcms/input/xcursor/itemdelegate.h b/kcms/input/xcursor/itemdelegate.h new file mode 100644 index 0000000000..9acb0e97bb --- /dev/null +++ b/kcms/input/xcursor/itemdelegate.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ITEMDELEGATE_H +#define ITEMDELEGATE_H + +#include + +class QPainter; + +class ItemDelegate : public QAbstractItemDelegate +{ + public: + ItemDelegate(QObject *parent = 0); + ~ItemDelegate(); + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private: + QString firstLine(const QModelIndex &index) const; + QString secondLine(const QModelIndex &index) const; + QPixmap decoration(const QModelIndex &index) const; + QPalette::ColorRole foregroundRole(const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif diff --git a/kcms/input/xcursor/previewwidget.cpp b/kcms/input/xcursor/previewwidget.cpp new file mode 100644 index 0000000000..79d1305f93 --- /dev/null +++ b/kcms/input/xcursor/previewwidget.cpp @@ -0,0 +1,239 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "previewwidget.h" + +#include +#include +#include +#include + +#include "cursortheme.h" + + + +namespace { + + // Preview cursors + const char * const cursor_names[] = + { + "left_ptr", + "left_ptr_watch", + "wait", + "pointing_hand", + "whats_this", + "ibeam", + "size_all", + "size_fdiag", + "cross", + "split_h", + "size_ver", + "size_hor", + "size_bdiag", + "split_v", + }; + + const int numCursors = 9; // The number of cursors from the above list to be previewed + const int cursorSpacing = 20; // Spacing between preview cursors + const int widgetMinWidth = 10; // The minimum width of the preview widget + const int widgetMinHeight = 48; // The minimum height of the preview widget +} + + +class PreviewCursor +{ + public: + PreviewCursor( const CursorTheme *theme, const QString &name, int size ); + ~PreviewCursor(); + + const QPixmap &pixmap() const { return m_pixmap; } + int width() const { return m_pixmap.width(); } + int height() const { return m_pixmap.height(); } + inline QRect rect() const; + void setPosition( const QPoint &p ) { m_pos = p; } + void setPosition( int x, int y ) { m_pos = QPoint(x, y); } + QPoint position() const { return m_pos; } + operator const uint32_t () const { return m_cursor; } + operator const QPixmap& () const { return pixmap(); } + + private: + QPixmap m_pixmap; + uint32_t m_cursor; + QPoint m_pos; +}; + + +PreviewCursor::PreviewCursor(const CursorTheme *theme, const QString &name, int size) +{ + // Create the preview pixmap + QImage image = theme->loadImage(name, size); + + if (image.isNull()) + return; + + m_pixmap = QPixmap::fromImage(image); + + // Load the cursor + m_cursor = theme->loadCursor(name, size); + // ### perhaps we should tag the cursor so it doesn't get + // replaced when a new theme is applied +} + +PreviewCursor::~PreviewCursor() +{ + if (QX11Info::isPlatformX11() && m_cursor != XCB_CURSOR_NONE) { + xcb_free_cursor(QX11Info::connection(), m_cursor); + } +} + +QRect PreviewCursor::rect() const +{ + return QRect(m_pos, m_pixmap.size()) + .adjusted(-(cursorSpacing / 2), -(cursorSpacing / 2), + cursorSpacing / 2, cursorSpacing / 2); +} + + + +// ------------------------------------------------------------------------------ + + + +PreviewWidget::PreviewWidget(QWidget *parent) : QWidget(parent) +{ + setMouseTracking(true); + current = NULL; +} + + +PreviewWidget::~PreviewWidget() +{ + qDeleteAll(list); + list.clear(); +} + + +QSize PreviewWidget::sizeHint() const +{ + int totalWidth = 0; + int maxHeight = 0; + + foreach (const PreviewCursor *c, list) + { + totalWidth += c->width(); + maxHeight = qMax(c->height(), maxHeight); + } + + totalWidth += (list.count() - 1) * cursorSpacing; + maxHeight = qMax(maxHeight, widgetMinHeight); + + return QSize(qMax(totalWidth, widgetMinWidth), qMax(height(), maxHeight)); +} + + +void PreviewWidget::layoutItems() +{ + if (!list.isEmpty()) + { + QSize size = sizeHint(); + int cursorWidth = size.width() / list.count(); + int nextX = (width() - size.width()) / 2; + + foreach (PreviewCursor *c, list) + { + c->setPosition(nextX + (cursorWidth - c->width()) / 2, + (height() - c->height()) / 2); + nextX += cursorWidth; + } + } + + needLayout = false; +} + + +void PreviewWidget::setTheme(const CursorTheme *theme, const int size) +{ + qDeleteAll(list); + list.clear(); + + if (theme) + { + for (int i = 0; i < numCursors; i++) + list << new PreviewCursor(theme, cursor_names[i], size); + + needLayout = true; + updateGeometry(); + } + + current = NULL; + update(); +} + + +void PreviewWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + + if (needLayout) + layoutItems(); + + foreach(const PreviewCursor *c, list) + { + if (c->pixmap().isNull()) + continue; + + p.drawPixmap(c->position(), *c); + } +} + + +void PreviewWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (needLayout) + layoutItems(); + + foreach (const PreviewCursor *c, list) + { + if (c->rect().contains(e->pos())) + { + if (c != current) + { + const uint32_t cursor = *c; + if (QX11Info::isPlatformX11() && (cursor != XCB_CURSOR_NONE)) { + xcb_change_window_attributes(QX11Info::connection(), winId(), XCB_CW_CURSOR, &cursor); + } + current = c; + } + return; + } + } + + setCursor(Qt::ArrowCursor); + current = NULL; +} + + +void PreviewWidget::resizeEvent(QResizeEvent *) +{ + if (!list.isEmpty()) + needLayout = true; +} + diff --git a/kcms/input/xcursor/previewwidget.h b/kcms/input/xcursor/previewwidget.h new file mode 100644 index 0000000000..4a11e2d2a8 --- /dev/null +++ b/kcms/input/xcursor/previewwidget.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PREVIEWWIDGET_H +#define PREVIEWWIDGET_H + +#include + +class CursorTheme; +class PreviewCursor; + +class PreviewWidget : public QWidget +{ + public: + PreviewWidget(QWidget *parent); + ~PreviewWidget(); + + void setTheme(const CursorTheme *theme, const int size); + void setUseLables(bool); + QSize sizeHint() const; + + protected: + void paintEvent(QPaintEvent *); + void mouseMoveEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *); + + private: + void layoutItems(); + + QList list; + const PreviewCursor *current; + bool needLayout:1; +}; + +#endif // PREVIEWWIDGET_H + diff --git a/kcms/input/xcursor/sortproxymodel.cpp b/kcms/input/xcursor/sortproxymodel.cpp new file mode 100644 index 0000000000..b9d6309278 --- /dev/null +++ b/kcms/input/xcursor/sortproxymodel.cpp @@ -0,0 +1,50 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include "sortproxymodel.h" +#include "cursortheme.h" + + +int SortProxyModel::compare(const QModelIndex &left, const QModelIndex &right, int role) const +{ + const QAbstractItemModel *model = sourceModel(); + + QString first = model->data(left, role).toString(); + QString second = model->data(right, role).toString(); + + if (filterCaseSensitivity() == Qt::CaseSensitive) + { + first = first.toLower(); + second = second.toLower(); + } + + return QString::localeAwareCompare(first, second); +} + + +bool SortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + const int result = compare(left, right, Qt::DisplayRole); + + if (result != 0) + return (result < 0); + else + return compare(left, right, CursorTheme::DisplayDetailRole) < 0; +} diff --git a/kcms/input/xcursor/sortproxymodel.h b/kcms/input/xcursor/sortproxymodel.h new file mode 100644 index 0000000000..95c9646c7c --- /dev/null +++ b/kcms/input/xcursor/sortproxymodel.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SORTPROXYMODEL_H +#define SORTPROXYMODEL_H + +#include +#include + +/** + * SortProxyModel is a sorting proxy model intended to be used in combination + * with the ItemDelegate class. + * + * First it compares the Qt::DisplayRoles, and if they match it compares + * the CursorTheme::DisplayDetailRoles. + * + * The model assumes both display roles are QStrings. + */ +class SortProxyModel : public QSortFilterProxyModel +{ + public: + SortProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + ~SortProxyModel() {} + inline const CursorTheme *theme(const QModelIndex &index) const; + inline QModelIndex findIndex(const QString &name) const; + inline QModelIndex defaultIndex() const; + inline void removeTheme(const QModelIndex &index); + + private: + int compare(const QModelIndex &left, const QModelIndex &right, int role) const; + + protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; +}; + + +const CursorTheme *SortProxyModel::theme(const QModelIndex &index) const +{ + CursorThemeModel *model = static_cast(sourceModel()); + return model->theme(mapToSource(index)); +} + +QModelIndex SortProxyModel::findIndex(const QString &name) const +{ + CursorThemeModel *model = static_cast(sourceModel()); + return mapFromSource(model->findIndex(name)); +} + +QModelIndex SortProxyModel::defaultIndex() const +{ + CursorThemeModel *model = static_cast(sourceModel()); + return mapFromSource(model->defaultIndex()); +} + +void SortProxyModel::removeTheme(const QModelIndex &index) +{ + CursorThemeModel *model = static_cast(sourceModel()); + model->removeTheme(mapToSource(index)); +} + +#endif // SORTPROXYMODEL_H diff --git a/kcms/input/xcursor/thememodel.cpp b/kcms/input/xcursor/thememodel.cpp new file mode 100644 index 0000000000..0dc04cff79 --- /dev/null +++ b/kcms/input/xcursor/thememodel.cpp @@ -0,0 +1,419 @@ +/* + * Copyright © 2005-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include + +#include "thememodel.h" +#include "thememodel.moc" +#include "xcursortheme.h" + +#include +#include + +// Check for older version +#if !defined(XCURSOR_LIB_MAJOR) && defined(XCURSOR_MAJOR) +# define XCURSOR_LIB_MAJOR XCURSOR_MAJOR +# define XCURSOR_LIB_MINOR XCURSOR_MINOR +#endif + + + +CursorThemeModel::CursorThemeModel(QObject *parent) + : QAbstractTableModel(parent) +{ + insertThemes(); +} + +CursorThemeModel::~CursorThemeModel() +{ + qDeleteAll(list); + list.clear(); +} + +void CursorThemeModel::refreshList() +{ + beginResetModel(); + qDeleteAll(list); + list.clear(); + endResetModel(); + insertThemes(); +} + +QVariant CursorThemeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + // Only provide text for the headers + if (role != Qt::DisplayRole) + return QVariant(); + + // Horizontal header labels + if (orientation == Qt::Horizontal) + { + switch (section) + { + case NameColumn: + return i18n("Name"); + + case DescColumn: + return i18n("Description"); + + default: return QVariant(); + } + } + + // Numbered vertical header lables + return QString(section); +} + + +QVariant CursorThemeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= list.count()) + return QVariant(); + + const CursorTheme *theme = list.at(index.row()); + + // Text label + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case NameColumn: + return theme->title(); + + case DescColumn: + return theme->description(); + + default: return QVariant(); + } + } + + // Description for the first name column + if (role == CursorTheme::DisplayDetailRole && index.column() == NameColumn) + return theme->description(); + + // Icon for the name column + if (role == Qt::DecorationRole && index.column() == NameColumn) + return theme->icon(); + + return QVariant(); +} + + +void CursorThemeModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); + + // Sorting of the model isn't implemented, as the KCM currently uses + // a sorting proxy model. +} + + +const CursorTheme *CursorThemeModel::theme(const QModelIndex &index) +{ + if (!index.isValid()) + return NULL; + + if (index.row() < 0 || index.row() >= list.count()) + return NULL; + + return list.at(index.row()); +} + + +QModelIndex CursorThemeModel::findIndex(const QString &name) +{ + uint hash = qHash(name); + + for (int i = 0; i < list.count(); i++) + { + const CursorTheme *theme = list.at(i); + if (theme->hash() == hash) + return index(i, 0); + } + + return QModelIndex(); +} + + +QModelIndex CursorThemeModel::defaultIndex() +{ + return findIndex(defaultName); +} + + +const QStringList CursorThemeModel::searchPaths() +{ + if (!baseDirs.isEmpty()) + return baseDirs; + +#if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 + // These are the default paths Xcursor will scan for cursor themes + QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); + + // If XCURSOR_PATH is set, use that instead of the default path + char *xcursorPath = std::getenv("XCURSOR_PATH"); + if (xcursorPath) + path = xcursorPath; +#else + // Get the search path from Xcursor + QString path = XcursorLibraryPath(); +#endif + + // Separate the paths + baseDirs = path.split(':', QString::SkipEmptyParts); + + // Remove duplicates + QMutableStringListIterator i(baseDirs); + while (i.hasNext()) + { + const QString path = i.next(); + QMutableStringListIterator j(i); + while (j.hasNext()) + if (j.next() == path) + j.remove(); + } + + // Expand all occurrences of ~/ to the home dir + baseDirs.replaceInStrings(QRegExp("^~\\/"), QDir::home().path() + '/'); + return baseDirs; +} + + +bool CursorThemeModel::hasTheme(const QString &name) const +{ + const uint hash = qHash(name); + + foreach (const CursorTheme *theme, list) + if (theme->hash() == hash) + return true; + + return false; +} + + +bool CursorThemeModel::isCursorTheme(const QString &theme, const int depth) +{ + // Prevent infinite recursion + if (depth > 10) + return false; + + // Search each icon theme directory for 'theme' + foreach (const QString &baseDir, searchPaths()) + { + QDir dir(baseDir); + if (!dir.exists() || !dir.cd(theme)) + continue; + + // If there's a cursors subdir, we'll assume this is a cursor theme + if (dir.exists("cursors")) + return true; + + // If the theme doesn't have an index.theme file, it can't inherit any themes. + if (!dir.exists("index.theme")) + continue; + + // Open the index.theme file, so we can get the list of inherited themes + KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals); + KConfigGroup cg(&config, "Icon Theme"); + + // Recurse through the list of inherited themes, to check if one of them + // is a cursor theme. + QStringList inherits = cg.readEntry("Inherits", QStringList()); + foreach (const QString &inherit, inherits) + { + // Avoid possible DoS + if (inherit == theme) + continue; + + if (isCursorTheme(inherit, depth + 1)) + return true; + } + } + + return false; +} + + +bool CursorThemeModel::handleDefault(const QDir &themeDir) +{ + QFileInfo info(themeDir.path()); + + // If "default" is a symlink + if (info.isSymLink()) + { + QFileInfo target(info.symLinkTarget()); + if (target.exists() && (target.isDir() || target.isSymLink())) + defaultName = target.fileName(); + + return true; + } + + // If there's no cursors subdir, or if it's empty + if (!themeDir.exists("cursors") || QDir(themeDir.path() + "/cursors") + .entryList(QDir::Files | QDir::NoDotAndDotDot ).isEmpty()) + { + if (themeDir.exists("index.theme")) + { + XCursorTheme theme(themeDir); + if (!theme.inherits().isEmpty()) + defaultName = theme.inherits().at(0); + } + return true; + } + + defaultName = QLatin1String("default"); + return false; +} + + +void CursorThemeModel::processThemeDir(const QDir &themeDir) +{ + bool haveCursors = themeDir.exists("cursors"); + + // Special case handling of "default", since it's usually either a + // symlink to another theme, or an empty theme that inherits another + // theme. + if (defaultName.isNull() && themeDir.dirName() == "default") + { + if (handleDefault(themeDir)) + return; + } + + // If the directory doesn't have a cursors subdir and lacks an + // index.theme file it can't be a cursor theme. + if (!themeDir.exists("index.theme") && !haveCursors) + return; + + static bool isX11 = QX11Info::isPlatformX11(); + if (!isX11) { + // TODO: implement Wayland Cursor Theme support + return; + } + // Create a cursor theme object for the theme dir + XCursorTheme *theme = new XCursorTheme(themeDir); + + // Skip this theme if it's hidden. + if (theme->isHidden()) { + delete theme; + return; + } + + // If there's no cursors subdirectory we'll do a recursive scan + // to check if the theme inherits a theme with one. + if (!haveCursors) + { + bool foundCursorTheme = false; + + foreach (const QString &name, theme->inherits()) + if ((foundCursorTheme = isCursorTheme(name))) + break; + + if (!foundCursorTheme) { + delete theme; + return; + } + } + + // Append the theme to the list + beginInsertRows(QModelIndex(), list.size(), list.size()); + list.append(theme); + endInsertRows(); +} + + +void CursorThemeModel::insertThemes() +{ + // Scan each base dir for Xcursor themes and add them to the list. + foreach (const QString &baseDir, searchPaths()) + { + QDir dir(baseDir); + if (!dir.exists()) + continue; + + // Process each subdir in the directory + foreach (const QString &name, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + { + // Don't process the theme if a theme with the same name already exists + // in the list. Xcursor will pick the first one it finds in that case, + // and since we use the same search order, the one Xcursor picks should + // be the one already in the list. + if (hasTheme(name) || !dir.cd(name)) + continue; + + processThemeDir(dir); + dir.cdUp(); // Return to the base dir + } + } + + // The theme Xcursor will end up using if no theme is configured + if (defaultName.isNull() || !hasTheme(defaultName)) + defaultName = QLatin1String("KDE_Classic"); +} + + +bool CursorThemeModel::addTheme(const QDir &dir) +{ + XCursorTheme *theme = new XCursorTheme(dir); + + // Don't add the theme to the list if it's hidden + if (theme->isHidden()) { + delete theme; + return false; + } + + // ### If the theme is hidden, the user will probably find it strange that it + // doesn't appear in the list view. There also won't be a way for the user + // to delete the theme using the KCM. Perhaps a warning about this should + // be issued, and the user be given a chance to undo the installation. + + // If an item with the same name already exists in the list, + // we'll remove it before inserting the new one. + for (int i = 0; i < list.count(); i++) + { + if (list.at(i)->hash() == theme->hash()) { + removeTheme(index(i, 0)); + break; + } + } + + // Append the theme to the list + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + list.append(theme); + endInsertRows(); + + return true; +} + + +void CursorThemeModel::removeTheme(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + beginRemoveRows(QModelIndex(), index.row(), index.row()); + delete list.takeAt(index.row()); + endRemoveRows(); +} + diff --git a/kcms/input/xcursor/thememodel.h b/kcms/input/xcursor/thememodel.h new file mode 100644 index 0000000000..bcf046a9c8 --- /dev/null +++ b/kcms/input/xcursor/thememodel.h @@ -0,0 +1,114 @@ +/* + * Copyright © 2005-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THEMEMODEL_H +#define THEMEMODEL_H + +#include +#include + +class QDir; +class CursorTheme; + +// The two TableView/TreeView columns provided by the model +enum Columns { NameColumn = 0, DescColumn }; + + +/** + * The CursorThemeModel class provides a model for all locally installed + * Xcursor themes, and the KDE/Qt legacy bitmap theme. + * + * This class automatically scans the locations in the file system from + * which Xcursor loads cursors, and creates an internal list of all + * available cursor themes. + * + * The model provides this theme list to item views in the form of a list + * of rows with two columns; the first column has the theme's descriptive + * name and its sample cursor as its icon, and the second column contains + * the theme's description. + * + * Additional Xcursor themes can be added to a model after it's been + * created, by calling addTheme(), which takes QDir as a parameter, + * with the themes location. The intention is for this function to be + * called when a new Xcursor theme has been installed, after the model + * was instantiated. + * + * The KDE legacy theme is a read-only entry, with the descriptive name + * "KDE Classic", and the internal name "#kde_legacy#". + * + * Calling defaultIndex() will return the index of the theme Xcursor + * will use if the user hasn't explicitly configured a cursor theme. + */ +class CursorThemeModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + CursorThemeModel(QObject *parent = 0); + ~CursorThemeModel(); + inline int columnCount(const QModelIndex &parent = QModelIndex()) const; + inline int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role) const; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + + /// Returns the CursorTheme at @p index. + const CursorTheme *theme(const QModelIndex &index); + + /// Returns the index for the CursorTheme with the internal name @p name, + /// or an invalid index if no matching theme can be found. + QModelIndex findIndex(const QString &name); + + /// Returns the index for the default theme. + QModelIndex defaultIndex(); + + /// Adds the theme in @p dir, and returns @a true if successful or @a false otherwise. + bool addTheme(const QDir &dir); + void removeTheme(const QModelIndex &index); + + /// Returns the list of base dirs Xcursor looks for themes in. + const QStringList searchPaths(); + + /// Refresh the list of themes by checking what's on disk. + void refreshList(); + + private: + bool handleDefault(const QDir &dir); + void processThemeDir(const QDir &dir); + void insertThemes(); + bool hasTheme(const QString &theme) const; + bool isCursorTheme(const QString &theme, const int depth = 0); + + private: + QList list; + QStringList baseDirs; + QString defaultName; +}; + +int CursorThemeModel::rowCount(const QModelIndex &) const +{ + return list.count(); +} + +int CursorThemeModel::columnCount(const QModelIndex &) const +{ + return 2; +} + +#endif // THEMEMODEL_H diff --git a/kcms/input/xcursor/themepage.cpp b/kcms/input/xcursor/themepage.cpp new file mode 100644 index 0000000000..6a8fbd6f0e --- /dev/null +++ b/kcms/input/xcursor/themepage.cpp @@ -0,0 +1,589 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include +#include "../../krdb/krdb.h" + +#include +#include +#include +#include + +#include "themepage.h" +#include "themepage.moc" + +#include "thememodel.h" +#include "itemdelegate.h" +#include "sortproxymodel.h" +#include "cursortheme.h" + +#include +#include + +#ifdef HAVE_XFIXES +# include +#endif + + +ThemePage::ThemePage(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + installKnsButton->setIcon(QIcon::fromTheme("get-hot-new-stuff")); + installButton->setIcon(QIcon::fromTheme("document-import")); + removeButton->setIcon(QIcon::fromTheme("edit-delete")); + + model = new CursorThemeModel(this); + + proxy = new SortProxyModel(this); + proxy->setSourceModel(model); + proxy->setFilterCaseSensitivity(Qt::CaseSensitive); + proxy->sort(NameColumn, Qt::AscendingOrder); + + // Get the icon size for QListView + int size = style()->pixelMetric(QStyle::PM_LargeIconSize); + + view->setModel(proxy); + view->setItemDelegate(new ItemDelegate(this)); + view->setIconSize(QSize(size, size)); + view->setSelectionMode(QAbstractItemView::SingleSelection); + + // Make sure we find out about selection changes + connect(view->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + SLOT(selectionChanged())); + + // Make sure we find out about size changes + connect(sizeComboBox, + SIGNAL(currentIndexChanged(int)), + SLOT(sizeChanged())); + + // Make sure we find out about user activity + connect(sizeComboBox, + SIGNAL(activated(int)), + SLOT(preferredSizeChanged())); + + // Disable the install button if we can't install new themes to ~/.icons, + // or Xcursor isn't set up to look for cursor themes there. + if (!model->searchPaths().contains(QDir::homePath() + "/.icons") || !iconsIsWritable()) { + installButton->setEnabled(false); + installKnsButton->setEnabled(false); + } + + connect(installKnsButton, SIGNAL(clicked()), SLOT(getNewClicked())); + connect(installButton, SIGNAL(clicked()), SLOT(installClicked())); + connect(removeButton, SIGNAL(clicked()), SLOT(removeClicked())); +} + + +ThemePage::~ThemePage() +{ +} + + +bool ThemePage::iconsIsWritable() const +{ + const QFileInfo icons = QFileInfo(QDir::homePath() + "/.icons"); + const QFileInfo home = QFileInfo(QDir::homePath()); + + return ((icons.exists() && icons.isDir() && icons.isWritable()) || + (!icons.exists() && home.isWritable())); +} + + +void ThemePage::updateSizeComboBox() +{ + // clear the combo box + sizeComboBox->clear(); + + // refill the combo box and adopt its icon size + QModelIndex selected = selectedIndex(); + int maxIconWidth = 0; + int maxIconHeight = 0; + if (selected.isValid()) + { + const CursorTheme *theme = proxy->theme(selected); + const QList sizes = theme->availableSizes(); + QIcon m_icon; + if (sizes.size() > 1) // only refill the combobox if there is more that 1 size + { + int i; + QList comboBoxList; + QPixmap m_pixmap; + + // insert the items + m_pixmap = theme->createIcon(0); + if (m_pixmap.width() > maxIconWidth) + maxIconWidth = m_pixmap.width(); + if (m_pixmap.height() > maxIconHeight) + maxIconHeight = m_pixmap.height(); + sizeComboBox->addItem( + QIcon(m_pixmap), + i18nc("@item:inlistbox size", "resolution dependent"), + 0); + comboBoxList << 0; + foreach (i, sizes) + { + m_pixmap = theme->createIcon(i); + if (m_pixmap.width() > maxIconWidth) + maxIconWidth = m_pixmap.width(); + if (m_pixmap.height() > maxIconHeight) + maxIconHeight = m_pixmap.height(); + sizeComboBox->addItem(QIcon(m_pixmap), QString::number(i), i); + comboBoxList << i; + }; + + // select an item + int selectItem = comboBoxList.indexOf(preferredSize); + if (selectItem < 0) // preferredSize not available for this theme + { + /* Search the value next to preferredSize. The first entry (0) + is ignored. (If preferredSize would have been 0, then we + would had found it yet. As preferredSize is not 0, we won't + default to "automatic size".)*/ + int j; + int distance; + int smallestDistance; + selectItem = 1; + j = comboBoxList.value(selectItem); + smallestDistance = j < preferredSize ? preferredSize - j : j - preferredSize; + for (int i = 2; i < comboBoxList.size(); ++i) + { + j = comboBoxList.value(i); + distance = j < preferredSize ? preferredSize - j : j - preferredSize; + if (distance < smallestDistance || (distance == smallestDistance && j > preferredSize)) + { + smallestDistance = distance; + selectItem = i; + }; + } + }; + sizeComboBox->setCurrentIndex(selectItem); + }; + }; + sizeComboBox->setIconSize(QSize(maxIconWidth, maxIconHeight)); + + // enable or disable the combobox + KConfig c("kcminputrc"); + KConfigGroup cg(&c, "Mouse"); + if (cg.isEntryImmutable("cursorSize")) { + sizeComboBox->setEnabled(false); + sizeLabel->setEnabled(false); + } else { + sizeComboBox->setEnabled(sizeComboBox->count() > 0); + sizeLabel->setEnabled(sizeComboBox->count() > 0); + }; +} + + +int ThemePage::selectedSize() const +{ + if (sizeComboBox->isEnabled() && sizeComboBox->currentIndex() >= 0) + return sizeComboBox->itemData(sizeComboBox->currentIndex(), Qt::UserRole).toInt(); + return 0; +} + + +void ThemePage::updatePreview() +{ + QModelIndex selected = selectedIndex(); + + if (selected.isValid()) { + const CursorTheme *theme = proxy->theme(selected); + preview->setTheme(theme, selectedSize()); + removeButton->setEnabled(theme->isWritable()); + } else { + preview->setTheme(NULL, 0); + removeButton->setEnabled(false); + }; +} + + +bool ThemePage::haveXfixes() +{ + bool result = false; + +#ifdef HAVE_XFIXES + if (!QX11Info::isPlatformX11()) { + return result; + } + int event_base, error_base; + if (XFixesQueryExtension(QX11Info::display(), &event_base, &error_base)) + { + int major, minor; + XFixesQueryVersion(QX11Info::display(), &major, &minor); + result = (major >= 2); + } +#endif + + return result; +} + + +bool ThemePage::applyTheme(const CursorTheme *theme, const int size) +{ + // Require the Xcursor version that shipped with X11R6.9 or greater, since + // in previous versions the Xfixes code wasn't enabled due to a bug in the + // build system (freedesktop bug #975). +#if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105 + if (!theme) + return false; + + if (!haveXfixes()) + return false; + + QByteArray themeName = QFile::encodeName(theme->name()); + + // Set up the proper launch environment for newly started apps + OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), + QStringLiteral("/KLauncher"), + QDBusConnection::sessionBus()); + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), themeName); + + // Update the Xcursor X resources + runRdb(0); + + // Notify all applications that the cursor theme has changed + KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged); + + // Reload the standard cursors + QStringList names; + + // Qt cursors + names << "left_ptr" << "up_arrow" << "cross" << "wait" + << "left_ptr_watch" << "ibeam" << "size_ver" << "size_hor" + << "size_bdiag" << "size_fdiag" << "size_all" << "split_v" + << "split_h" << "pointing_hand" << "openhand" + << "closedhand" << "forbidden" << "whats_this" << "copy" << "move" << "link"; + + // X core cursors + names << "X_cursor" << "right_ptr" << "hand1" + << "hand2" << "watch" << "xterm" + << "crosshair" << "left_ptr_watch" << "center_ptr" + << "sb_h_double_arrow" << "sb_v_double_arrow" << "fleur" + << "top_left_corner" << "top_side" << "top_right_corner" + << "right_side" << "bottom_right_corner" << "bottom_side" + << "bottom_left_corner" << "left_side" << "question_arrow" + << "pirate"; + + foreach (const QString &name, names) + { + XFixesChangeCursorByName(QX11Info::display(), theme->loadCursor(name, size), QFile::encodeName(name)); + } + + return true; +#else + Q_UNUSED(theme) + return false; +#endif +} + + +void ThemePage::save() +{ + const CursorTheme *theme = selectedIndex().isValid() ? proxy->theme(selectedIndex()) : NULL; + const int size = selectedSize(); + + KConfig config("kcminputrc"); + KConfigGroup c(&config, "Mouse"); + if (theme) + { + c.writeEntry("cursorTheme", theme->name()); + }; + c.writeEntry("cursorSize", size); + preferredSize = size; + c.sync(); + + if (!applyTheme(theme, size)) + { + KMessageBox::information(this, + i18n("You have to restart KDE for these changes to take effect."), + i18n("Cursor Settings Changed"), "CursorSettingsChanged"); + } + + appliedIndex = selectedIndex(); + appliedSize = size; +} + + +void ThemePage::load() +{ + view->selectionModel()->clear(); + // Get the name of the theme libXcursor currently uses + QString currentTheme; + if (QX11Info::isPlatformX11()) { + currentTheme = XcursorGetTheme(QX11Info::display()); + } + + // Get the name of the theme KDE is configured to use + KConfig c("kcminputrc"); + KConfigGroup cg(&c, "Mouse"); + currentTheme = cg.readEntry("cursorTheme", currentTheme); + + // Find the theme in the listview + if (!currentTheme.isEmpty()) + appliedIndex = proxy->findIndex(currentTheme); + else + appliedIndex = proxy->defaultIndex(); + + // Disable the listview and the buttons if we're in kiosk mode + if (cg.isEntryImmutable("cursorTheme")) + { + view->setEnabled(false); + installButton->setEnabled(false); + removeButton->setEnabled(false); + } + + // Load cursor size + int size = cg.readEntry("cursorSize", 0); + if (size <= 0) + preferredSize = 0; + else + preferredSize = size; + updateSizeComboBox(); // This handles also the kiosk mode + + appliedSize = size; + + const CursorTheme *theme = proxy->theme(appliedIndex); + + if (appliedIndex.isValid()) + { + // Select the current theme + view->setCurrentIndex(appliedIndex); + view->scrollTo(appliedIndex, QListView::PositionAtCenter); + + // Update the preview widget as well + preview->setTheme(theme, size); + } + + if (!theme || !theme->isWritable()) + removeButton->setEnabled(false); +} + + +void ThemePage::defaults() +{ + view->selectionModel()->clear(); + QModelIndex defaultIndex = proxy->findIndex("Oxygen_Black"); + view->setCurrentIndex(defaultIndex); + preferredSize = 0; + updateSizeComboBox(); +} + + +void ThemePage::selectionChanged() +{ + updateSizeComboBox(); + updatePreview(); + + emit changed(appliedIndex != selectedIndex()); +} + +QModelIndex ThemePage::selectedIndex() const +{ + QModelIndexList selection = view->selectionModel()->selectedIndexes(); + if (!selection.isEmpty()) { + return (selection.at(0)); + } + return QModelIndex(); +} + +void ThemePage::sizeChanged() +{ + updatePreview(); + emit changed(selectedSize() != appliedSize); +} + +void ThemePage::preferredSizeChanged() +{ + int index = sizeComboBox->currentIndex(); + if (index >= 0) + preferredSize = sizeComboBox->itemData(index, Qt::UserRole).toInt(); + else + preferredSize = 0; +} + +void ThemePage::getNewClicked() +{ + KNS3::DownloadDialog dialog("xcursor.knsrc", this); + if (dialog.exec()) { + KNS3::Entry::List list = dialog.changedEntries(); + if (list.count() > 0) + model->refreshList(); + } +} + +void ThemePage::installClicked() +{ + // Get the URL for the theme we're going to install + QUrl url = KUrlRequesterDialog::getUrl(QUrl(), this, i18n("Drag or Type Theme URL")); + + if (url.isEmpty()) + return; + + QString tempFile; + if (!KIO::NetAccess::download(url, tempFile, this)) + { + QString text; + + if (url.isLocalFile()) + text = i18n("Unable to find the cursor theme archive %1.", + url.toDisplayString()); + else + text = i18n("Unable to download the cursor theme archive; " + "please check that the address %1 is correct.", + url.toDisplayString()); + + KMessageBox::sorry(this, text); + return; + } + + if (!installThemes(tempFile)) + KMessageBox::error(this, i18n("The file %1 does not appear to be a valid " + "cursor theme archive.", url.fileName())); + + KIO::NetAccess::removeTempFile(tempFile); +} + + +void ThemePage::removeClicked() +{ + // We don't have to check if the current index is valid, since + // the remove button will be disabled when there's no selection. + const CursorTheme *theme = proxy->theme(selectedIndex()); + + // Don't let the user delete the currently configured theme + if (selectedIndex() == appliedIndex) { + KMessageBox::sorry(this, i18n("You cannot delete the theme you are currently " + "using.
    You have to switch to another theme first.
    ")); + return; + } + + // Get confirmation from the user + QString question = i18n("Are you sure you want to remove the " + "%1 cursor theme?
    " + "This will delete all the files installed by this theme.
    ", + theme->title()); + + int answer = KMessageBox::warningContinueCancel(this, question, + i18n("Confirmation"), KStandardGuiItem::del()); + + if (answer != KMessageBox::Continue) + return; + + // Delete the theme from the harddrive + KIO::del(QUrl::fromLocalFile(theme->path())); // async + + // Remove the theme from the model + proxy->removeTheme(selectedIndex()); + + // TODO: + // Since it's possible to substitute cursors in a system theme by adding a local + // theme with the same name, we shouldn't remove the theme from the list if it's + // still available elsewhere. We could add a + // bool CursorThemeModel::tryAddTheme(const QString &name), and call that, but + // since KIO::del() is an asynchronos operation, the theme we're deleting will be + // readded to the list again before KIO has removed it. +} + + +bool ThemePage::installThemes(const QString &file) +{ + KTar archive(file); + + if (!archive.open(QIODevice::ReadOnly)) + return false; + + const KArchiveDirectory *archiveDir = archive.directory(); + QStringList themeDirs; + + // Extract the dir names of the cursor themes in the archive, and + // append them to themeDirs + foreach(const QString &name, archiveDir->entries()) + { + const KArchiveEntry *entry = archiveDir->entry(name); + if (entry->isDirectory() && entry->name().toLower() != "default") + { + const KArchiveDirectory *dir = static_cast(entry); + if (dir->entry("index.theme") && dir->entry("cursors")) + themeDirs << dir->name(); + } + } + + if (themeDirs.isEmpty()) + return false; + + // The directory we'll install the themes to + QString destDir = QDir::homePath() + "/.icons/"; + KStandardDirs::makeDir(destDir); // Make sure the directory exists + + // Process each cursor theme in the archive + foreach (const QString &dirName, themeDirs) + { + QDir dest(destDir + dirName); + if (dest.exists()) + { + QString question = i18n("A theme named %1 already exists in your icon " + "theme folder. Do you want replace it with this one?", dirName); + + int answer = KMessageBox::warningContinueCancel(this, question, + i18n("Overwrite Theme?"), + KStandardGuiItem::overwrite()); + + if (answer != KMessageBox::Continue) + continue; + + // ### If the theme that's being replaced is the current theme, it + // will cause cursor inconsistencies in newly started apps. + } + + // ### Should we check if a theme with the same name exists in a global theme dir? + // If that's the case it will effectively replace it, even though the global theme + // won't be deleted. Checking for this situation is easy, since the global theme + // will be in the listview. Maybe this should never be allowed since it might + // result in strange side effects (from the average users point of view). OTOH + // a user might want to do this 'upgrade' a global theme. + + const KArchiveDirectory *dir = static_cast + (archiveDir->entry(dirName)); + dir->copyTo(dest.path()); + model->addTheme(dest); + } + + archive.close(); + return true; +} + diff --git a/kcms/input/xcursor/themepage.h b/kcms/input/xcursor/themepage.h new file mode 100644 index 0000000000..cf82be79c4 --- /dev/null +++ b/kcms/input/xcursor/themepage.h @@ -0,0 +1,99 @@ +/* + * Copyright © 2003-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THEMEPAGE_H +#define THEMEPAGE_H + +#include +#include + +#include "ui_themepage.h" + +class CursorThemeModel; +class SortProxyModel; +class CursorTheme; + +class ThemePage : public QWidget, private Ui::ThemePage +{ + Q_OBJECT + + public: + ThemePage(QWidget* parent = 0); + ~ThemePage(); + + // Called by the KCM + void save(); + void load(); + void defaults(); + + static bool haveXfixes(); + + Q_SIGNALS: + void changed(bool); + + private Q_SLOTS: + void selectionChanged(); + /** Updates the preview. If the size has changed, it also emits changed() */ + void sizeChanged(); + /** Sets #preferredSize to the item that is currently selected in sizeComboBox. + If none is selected, it is set to 0. */ + void preferredSizeChanged(); + /** Updates the size combo box. It loads the size list of the selected cursor + theme with the corresponding icons and chooses an appropriate entry. It + enables the combo box and the label if the theme provides more than one + size, otherwise it disables it. If the size setting is looked in kiosk + mode, it stays always disabled. */ + void updateSizeComboBox(); + void getNewClicked(); + void installClicked(); + void removeClicked(); + + private: + /** @returns 0 if in the UI "automatic size" is selected, otherwise + returns the custom size. */ + int selectedSize() const; + /** Holds the last size that was choosen by the user. Example: The user chooses + theme1 which provides the sizes 24 and 36. He chooses 36. preferredSize gets + set to 36. Now, he switchs to theme2 which provides the sizes 30 and 40. + preferredSize still is 36, so the UI will default to 40, which is next to 36. + Now, he chooses theme3 which provides the sizes 34 and 44. preferredSize is + still 36, so the UI defaults to 34. Now the user changes manually to 44. This + will also change preferredSize. */ + int preferredSize; + void updatePreview(); + QModelIndex selectedIndex() const; + bool installThemes(const QString &file); + /** Applies a given theme, using XFixes, XCursor and KGlobalSettings. + @param theme The cursor theme to be applied. It is save to pass 0 here + (will result in \e false as return value). + @param size The size hint that is used to select the cursor size. + @returns If the changes could be applied. Will return \e false if \e theme is + 0 or if the XFixes and XCursor libraries aren't available in the required + version, otherwise returns \e true. */ + bool applyTheme(const CursorTheme *theme, const int size); + bool iconsIsWritable() const; + + CursorThemeModel *model; + SortProxyModel *proxy; + + int appliedSize; + // This index refers to the CursorThemeModel, not the proxy or the view + QPersistentModelIndex appliedIndex; +}; + +#endif // THEMEPAGE_H diff --git a/kcms/input/xcursor/themepage.ui b/kcms/input/xcursor/themepage.ui new file mode 100644 index 0000000000..be73663816 --- /dev/null +++ b/kcms/input/xcursor/themepage.ui @@ -0,0 +1,120 @@ + + + ThemePage + + + + 0 + 0 + 541 + 360 + + + + + + + Select the cursor theme you want to use (hover preview to test cursor): + + + + + + + + 0 + 0 + + + + + 0 + 48 + + + + + + + + + 0 + 1 + + + + true + + + QAbstractItemView::ScrollPerPixel + + + + + + + Get new color schemes from the Internet + + + Get New Theme... + + + + + + + Install From File... + + + + + + + Remove Theme + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + 0 + 0 + + + + + + + + Size: + + + + + + + + KPushButton + QPushButton +
    kpushbutton.h
    +
    + + PreviewWidget + QWidget +
    previewwidget.h
    + 1 +
    +
    + + +
    diff --git a/kcms/input/xcursor/xcursor.knsrc b/kcms/input/xcursor/xcursor.knsrc new file mode 100644 index 0000000000..27f5b7659e --- /dev/null +++ b/kcms/input/xcursor/xcursor.knsrc @@ -0,0 +1,6 @@ +[KNewStuff3] +ProvidersUrl=http://download.kde.org/ocs/providers.xml +Categories=X11 Mouse Theme +InstallPath=.icons +Uncompress=archive + diff --git a/kcms/input/xcursor/xcursortheme.cpp b/kcms/input/xcursor/xcursortheme.cpp new file mode 100644 index 0000000000..e80ea1e365 --- /dev/null +++ b/kcms/input/xcursor/xcursortheme.cpp @@ -0,0 +1,229 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "xcursortheme.h" + + +// Static variable holding alternative names for some cursors +QHash XCursorTheme::alternatives; + +XCursorTheme::XCursorTheme(const QDir &themeDir) + : CursorTheme(themeDir.dirName()) +{ + // Directory information + setName(themeDir.dirName()); + setPath(themeDir.path()); + setIsWritable(QFileInfo(themeDir.path()).isWritable()); // ### perhaps this shouldn't be cached + + if (themeDir.exists("index.theme")) + parseIndexFile(); + + QString cursorFile = path() + "/cursors/left_ptr"; + QList sizeList; + XcursorImages *images = XcursorFilenameLoadAllImages(qPrintable(cursorFile)); + if (images) + { + for (int i = 0; i < images->nimage; ++i) + { + if (!sizeList.contains(images->images[i]->size)) + sizeList.append(images->images[i]->size); + }; + XcursorImagesDestroy(images); + qSort(sizeList.begin(), sizeList.end()); + m_availableSizes = sizeList; + }; + if (!sizeList.isEmpty()) + { + QString sizeListString = QString::number(sizeList.takeFirst()); + while (!sizeList.isEmpty()) + { + sizeListString.append(", "); + sizeListString.append(QString::number(sizeList.takeFirst())); + }; + QString tempString = i18nc( + "@info/plain The argument is the list of available sizes (in pixel). Example: " + "'Available sizes: 24' or 'Available sizes: 24, 36, 48'", + "(Available sizes: %1)", + sizeListString); + if (m_description.isEmpty()) + m_description = tempString; + else + m_description = m_description + ' ' + tempString; + }; +} + + +void XCursorTheme::parseIndexFile() +{ + KConfig config(path() + "/index.theme", KConfig::NoGlobals); + KConfigGroup cg(&config, "Icon Theme"); + + m_title = cg.readEntry("Name", m_title); + m_description = cg.readEntry("Comment", m_description); + m_sample = cg.readEntry("Example", m_sample); + m_hidden = cg.readEntry("Hidden", false); + m_inherits = cg.readEntry("Inherits", QStringList()); +} + + +QString XCursorTheme::findAlternative(const QString &name) const +{ + if (alternatives.isEmpty()) + { + alternatives.reserve(18); + + // Qt uses non-standard names for some core cursors. + // If Xcursor fails to load the cursor, Qt creates it with the correct name using the + // core protcol instead (which in turn calls Xcursor). We emulate that process here. + // Note that there's a core cursor called cross, but it's not the one Qt expects. + alternatives.insert("cross", "crosshair"); + alternatives.insert("up_arrow", "center_ptr"); + alternatives.insert("wait", "watch"); + alternatives.insert("ibeam", "xterm"); + alternatives.insert("size_all", "fleur"); + alternatives.insert("pointing_hand", "hand2"); + + // Precomputed MD5 hashes for the hardcoded bitmap cursors in Qt and KDE. + // Note that the MD5 hash for left_ptr_watch is for the KDE version of that cursor. + alternatives.insert("size_ver", "00008160000006810000408080010102"); + alternatives.insert("size_hor", "028006030e0e7ebffc7f7070c0600140"); + alternatives.insert("size_bdiag", "fcf1c3c7cd4491d801f1e1c78f100000"); + alternatives.insert("size_fdiag", "c7088f0f3e6c8088236ef8e1e3e70000"); + alternatives.insert("whats_this", "d9ce0ab605698f320427677b458ad60b"); + alternatives.insert("split_h", "14fef782d02440884392942c11205230"); + alternatives.insert("split_v", "2870a09082c103050810ffdffffe0204"); + alternatives.insert("forbidden", "03b6e0fcb3499374a867c041f52298f0"); + alternatives.insert("left_ptr_watch", "3ecb610c1bf2410f44200f48c40d3599"); + alternatives.insert("hand2", "e29285e634086352946a0e7090d73106"); + alternatives.insert("openhand", "9141b49c8149039304290b508d208c40"); + alternatives.insert("closedhand", "05e88622050804100c20044008402080"); + } + + return alternatives.value(name, QString()); +} + + +XcursorImage *XCursorTheme::xcLoadImage(const QString &image, int size) const +{ + QByteArray cursorName = QFile::encodeName(image); + QByteArray themeName = QFile::encodeName(name()); + + return XcursorLibraryLoadImage(cursorName, themeName, size); +} + + +XcursorImages *XCursorTheme::xcLoadImages(const QString &image, int size) const +{ + QByteArray cursorName = QFile::encodeName(image); + QByteArray themeName = QFile::encodeName(name()); + + return XcursorLibraryLoadImages(cursorName, themeName, size); +} + + +int XCursorTheme::autodetectCursorSize() const +{ + /* This code is basically borrowed from display.c of the XCursor library + We can't use "int XcursorGetDefaultSize(Display *dpy)" because if + previously the cursor size was set to a custom value, it would return + this custom value. */ + int size = 0; + int dpi = 0; + Display *dpy = QX11Info::display(); + // The string "v" is owned and will be destroyed by Xlib + char *v = XGetDefault(dpy, "Xft", "dpi"); + if (v) + dpi = atoi(v); + if (dpi) + size = dpi * 16 / 72; + if (size == 0) + { + int dim; + if (DisplayHeight(dpy, DefaultScreen(dpy)) < + DisplayWidth(dpy, DefaultScreen(dpy))) + { + dim = DisplayHeight(dpy, DefaultScreen(dpy)); + } else { + dim = DisplayWidth(dpy, DefaultScreen(dpy)); + }; + size = dim / 48; + } + return size; +} + +qulonglong XCursorTheme::loadCursor(const QString &name, int size) const +{ + if (size <= 0) + size = autodetectCursorSize(); + + // Load the cursor images + XcursorImages *images = xcLoadImages(name, size); + + if (!images) + images = xcLoadImages(findAlternative(name), size); + + if (!images) + return None; + + // Create the cursor + Cursor handle = XcursorImagesLoadCursor(QX11Info::display(), images); + XcursorImagesDestroy(images); + + setCursorName(handle, name); + return handle; +} + + +QImage XCursorTheme::loadImage(const QString &name, int size) const +{ + if (size <= 0) + size = autodetectCursorSize(); + + // Load the image + XcursorImage *xcimage = xcLoadImage(name, size); + + if (!xcimage) + xcimage = xcLoadImage(findAlternative(name), size); + + if (!xcimage) { + return QImage(); + } + + // Convert the XcursorImage to a QImage, and auto-crop it + QImage image((uchar *)xcimage->pixels, xcimage->width, xcimage->height, + QImage::Format_ARGB32_Premultiplied ); + + image = autoCropImage(image); + XcursorImageDestroy(xcimage); + + return image; +} + diff --git a/kcms/input/xcursor/xcursortheme.h b/kcms/input/xcursor/xcursortheme.h new file mode 100644 index 0000000000..c05c8e0abf --- /dev/null +++ b/kcms/input/xcursor/xcursortheme.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2006-2007 Fredrik Höglund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 or at your option version 3 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef XCURSORTHEME_H +#define XCURSORTHEME_H + +#include + +#include "cursortheme.h" + +class QDir; + +struct _XcursorImage; +struct _XcursorImages; + +typedef _XcursorImage XcursorImage; +typedef _XcursorImages XcursorImages; + +/** + * The XCursorTheme class is a CursorTheme implementation for Xcursor themes. + */ +class XCursorTheme : public CursorTheme +{ + public: + /** + * Initializes itself from the @p dir information, and parses the + * index.theme file if the dir has one. + */ + XCursorTheme(const QDir &dir); + virtual ~XCursorTheme() {} + + const QStringList inherits() const { return m_inherits; } + QImage loadImage(const QString &name, int size = 0) const; + qulonglong loadCursor(const QString &name, int size = 0) const; + + protected: + XCursorTheme(const QString &title, const QString &desc) + : CursorTheme(title, desc) {} + void setInherits(const QStringList &val) { m_inherits = val; } + + private: + XcursorImage *xcLoadImage(const QString &name, int size) const; + XcursorImages *xcLoadImages(const QString &name, int size) const; + void parseIndexFile(); + QString findAlternative(const QString &name) const; + /** Returns the size that the XCursor library would use if no + cursor size is given. This depends mainly on Xft.dpi. */ + int autodetectCursorSize() const; + + QStringList m_inherits; + static QHash alternatives; +}; + +#endif // XCURSORTHEME_H + diff --git a/kcms/keyboard/CMakeLists.txt b/kcms/keyboard/CMakeLists.txt new file mode 100644 index 0000000000..73492b478d --- /dev/null +++ b/kcms/keyboard/CMakeLists.txt @@ -0,0 +1,177 @@ +set(CMAKE_REQUIRED_LIBRARIES -lXi) +check_function_exists(_XiGetDevicePresenceNotifyEvent XiGetDevicePresenceNotifyEvent_EXISTS) + +if( X11_Xinput_FOUND AND XiGetDevicePresenceNotifyEvent_EXISTS ) + set(XiGetDevicePresence_FOUND 1) + add_definitions(-DHAVE_XINPUT_AND_DEVICE_NOTIFY=1) +else() + message(STATUS "_XiGetDevicePresenceNotifyEvent is not found, libXi version 1.2.0 or later is required to keep layouts with keyboard hotplugging") +endif() + + + +### kded daemon ### + +set( kded_keyboard_SRCS + keyboard_daemon.cpp + layout_memory.cpp + layout_memory_persister.cpp + x11_helper.cpp +# xinput_helper.cpp + xkb_helper.cpp + keyboard_config.cpp + keyboard_hardware.cpp + numlockx.c + bindings.cpp +# next are temporary for sys tray UI + layout_tray_icon.cpp + layouts_menu.cpp + flags.cpp + xkb_rules.cpp +) + +add_library(kded_keyboard MODULE ${kded_keyboard_SRCS}) + +target_link_libraries(kded_keyboard + Qt5::Concurrent + Qt5::Widgets + Qt5::X11Extras + Qt5::Xml + KF5::ConfigCore + KF5::CoreAddons + KF5::DBusAddons + KF5::GlobalAccel + KF5::I18n + KF5::Notifications + KF5::Service + KF5::WindowSystem + KF5::Plasma + KF5::XmlGui + KF5::KDE4Support + ${X11_LIBRARIES} + ${X11_Xkbfile_LIB} + ) + +if(X11_XTest_FOUND) + target_link_libraries(kded_keyboard ${X11_XTest_LIB}) +endif() + +if(XiGetDevicePresence_FOUND) + target_link_libraries(kded_keyboard ${X11_Xinput_LIB}) +endif() + +install( TARGETS kded_keyboard DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES keyboard.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kded ) + + +### plasma applet ### +# +# set( plasma_applet_keyboard_SRCS +# keyboard_applet.cpp +# flags.cpp +# keyboard_config.cpp +# xkb_rules.cpp +# x11_helper.cpp +# xkb_helper.cpp +# layouts_menu.cpp +# ) +# +# add_library(plasma_applet_keyboard MODULE ${plasma_applet_keyboard_SRCS}) +# +# target_link_libraries(plasma_applet_keyboard +# KF5::Plasma ${KDE4_KDEUI_LIBS} Qt5::Xml ${X11_Xkbfile_LIB} ${X11_LIBRARIES} +# ${text_paint_LIB} +# ) +# +# install( TARGETS plasma_applet_keyboard DESTINATION ${PLUGIN_INSTALL_DIR} ) +# install( FILES plasma_applet_keyboard.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +# +# +### widget lib ### + +set( keyboard_layout_widget_SRCS + layout_widget.cpp + keyboard_config.cpp + flags.cpp + x11_helper.cpp + xkb_helper.cpp +) + +# MODULE? +add_library(keyboard_layout_widget MODULE ${keyboard_layout_widget_SRCS}) + +target_link_libraries(keyboard_layout_widget + Qt5::X11Extras + Qt5::Xml + KF5::I18n + KF5::Service + KF5::Plasma + KF5::KDE4Support + ${X11_LIBRARIES} + ${X11_Xkbfile_LIB} +) + +install( TARGETS keyboard_layout_widget DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +### kcm keyboard ### +include_directories("preview") + +set(kcm_keyboard_PART_SRCS + kcm_keyboard.cpp + kcm_keyboard_widget.cpp + kcm_view_models.cpp + kcm_add_layout_dialog.cpp + keyboard_config.cpp + x11_helper.cpp + xkb_helper.cpp + xkb_rules.cpp + flags.cpp + iso_codes.cpp + kcmmisc.cpp + bindings.cpp + preview/keyaliases.cpp + preview/keyboardlayout.cpp + preview/keyboardpainter.cpp + preview/keysymbols.cpp + preview/keysymhelper.cpp + preview/kbpreviewframe.cpp + preview/keysym2ucs.cpp +) + + +qt5_wrap_ui(kcm_keyboard_PART_SRCS kcm_keyboard.ui kcm_add_layout_dialog.ui kcmmiscwidget.ui) + +add_library(kcm_keyboard MODULE ${kcm_keyboard_PART_SRCS}) + +target_link_libraries(kcm_keyboard + Qt5::Concurrent + Qt5::Widgets + Qt5::X11Extras + Qt5::Xml + KF5::KCMUtils + KF5::GlobalAccel + KF5::I18n + KF5::KIOCore + KF5::KIOWidgets + KF5::WindowSystem + KF5::Plasma + KF5::XmlGui + KF5::KDE4Support + ${X11_Xkbfile_LIB} + ${X11_LIBRARIES} + ${text_paint_LIB} +) + +install(TARGETS kcm_keyboard DESTINATION ${PLUGIN_INSTALL_DIR} ) + +install( FILES kcm_keyboard.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +# Images + +add_subdirectory( pics ) + + +# Unit tests + +# add_subdirectory( tests ) diff --git a/kcms/keyboard/Messages.sh b/kcms/keyboard/Messages.sh new file mode 100755 index 0000000000..8d5275220b --- /dev/null +++ b/kcms/keyboard/Messages.sh @@ -0,0 +1,5 @@ +#! /usr/bin/env bash +$EXTRACTRC kcm*.ui >> rc.cpp +$XGETTEXT rc.cpp kcmmisc.cpp preview/*.cpp -o $podir/kcmkeyboard.pot +$XGETTEXT kcm_*.cpp keyboard_*.cpp layout_*.cpp flags.cpp layouts_menu.cpp bindings.cpp -o $podir/kxkb.pot +rm -f rc.cpp diff --git a/kcms/keyboard/README b/kcms/keyboard/README new file mode 100644 index 0000000000..25c50b47f6 --- /dev/null +++ b/kcms/keyboard/README @@ -0,0 +1,44 @@ +This is new project to replace keyboard (kcmmisc) and kxkb +to manage keyboard harware configuration and layouts. + +Components: + +kcm_init: +* no kcm_init - all initialization (including after keyboard hot-plug) is done by kded daemon + +Keyboard daemon: +* configures keyboard layouts on start +* provides DBUS API "org.kde.KXKB/kxkb" +* listens to window/desktop switching if layout switch mode is not global +* listens to new keyboard devices and reinitializes keyboard layouts and hardware settings +* listens to Global KDE Shortcut to switch layouts +* listens to Global Shortcut settings change +* listens to keyboard configuration changes +* provides systray icon for layout + +Keyboard applet: +* displays current layout with text and (optionally flag) +* allows to toggle layouts by click or set them with context menu + +Layout widget: +* displays current layout with text embedded in another widget (e.g. lock dialog) +* currently only short text (no flag or long text) +* dynamically loaded as a plugin + +KCM Module: +* allows to configure keyboard hardware, layouts and xkb options (advanced) + +Advantages (over old code): +* One UI module to configure keyboard layouts and hardware +* Allows to configure keyboard model/xkb options without configuring layouts +* Less cluttered layout configuration control (add layout is separate) +* Layout control/DBus interface is separate from the indicator +* Does not require libxklvier +* Takes language names from iso-codes project +* Takes country names/translations from xkeyboard-config project +* Information about current layouts is always taken from X server, so should be more robust +* Cleaner code (rewritten from scratch) +* Due to many points above should start faster (though no benchmarks performed) + +New features: +* Allow selection of keyboard layout by language diff --git a/kcms/keyboard/TODO b/kcms/keyboard/TODO new file mode 100644 index 0000000000..2e58efb289 --- /dev/null +++ b/kcms/keyboard/TODO @@ -0,0 +1,37 @@ +Tests: +* tests + +Core: +* better handling when layouts set manually and not present in config + +KCM UI: +* use listboxes instead of combos in add layout dialog +* allow multiple layouts to be added in same dialog? + +KCM UI cleanup: +* layouts, sizes, alignment etc in kcm + +plasma applet: + +kxkb part: +* tooltip (currently does not work in lockdlg for some reason) + +tray icon: + +Code cleanup: +* rules, iso_codes: + better error handling + +Improvement: +* remove XInput libs from plasma applet and layout widget (find a nicer way) +* optimize fetching groups and layouts (cache them) / fetch current group directly +* refactor/optimize/clean the code +* refactor client code for D-BUS API to use library instead? + +External improvements: +* Add KSharedConfig::configChanged() signal +* make iso-codes external package +* make flags external package + +Documentation: +* update documentation for keyboard module diff --git a/kcms/keyboard/bindings.cpp b/kcms/keyboard/bindings.cpp new file mode 100644 index 0000000000..21541e095d --- /dev/null +++ b/kcms/keyboard/bindings.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "bindings.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "x11_helper.h" +#include "flags.h" + + +static const char* actionName = I18N_NOOP("Switch to Next Keyboard Layout"); +static const char* COMPONENT_NAME = I18N_NOOP("KDE Keyboard Layout Switcher"); + + +KeyboardLayoutActionCollection::KeyboardLayoutActionCollection(QObject* parent, bool configAction_): + KActionCollection(parent, COMPONENT_NAME), + configAction(configAction_) +{ + QAction* toggleAction = addAction( actionName ); + toggleAction->setText( i18n(actionName) ); + KGlobalAccel::self()->setShortcut(toggleAction, + QList() << QKeySequence(Qt::ALT+Qt::CTRL+Qt::Key_K), + KGlobalAccel::NoAutoloading); + if( configAction ) { + toggleAction->setProperty("isConfigurationAction", true); + } +} + +KeyboardLayoutActionCollection::~KeyboardLayoutActionCollection() +{ + clear(); +} + +QAction* KeyboardLayoutActionCollection::getToggeAction() +{ + return action(0); +} + +QAction* KeyboardLayoutActionCollection::createLayoutShortcutActon(const LayoutUnit& layoutUnit, const Rules* rules, bool autoload) +{ + QString longLayoutName = Flags::getLongText( layoutUnit, rules ); + QString actionName = "Switch keyboard layout to "; + actionName += longLayoutName; + QAction* action = addAction( actionName ); + action->setText( i18n("Switch keyboard layout to %1", longLayoutName) ); + KGlobalAccel::GlobalShortcutLoading loading = autoload ? KGlobalAccel::Autoloading : KGlobalAccel::NoAutoloading; + QList shortcuts; + if (!autoload) { + shortcuts << layoutUnit.getShortcut(); + } + KGlobalAccel::self()->setShortcut(action, shortcuts, loading); + action->setData(layoutUnit.toString()); + if( configAction ) { + action->setProperty("isConfigurationAction", true); + } + return action; +} + +void KeyboardLayoutActionCollection::setToggleShortcut(const QKeySequence& keySequence) +{ + KGlobalAccel::self()->setShortcut(getToggeAction(), QList() << keySequence, KGlobalAccel::NoAutoloading); +} + +//KAction* KeyboardLayoutActionCollection::setShortcut(LayoutUnit& layoutUnit, const QKeySequence& keySequence, const Rules* rules) +//{ +// KAction* action = getAction(layoutUnit); +// if( action == NULL && ! keySequence.isEmpty() ) { +// action = createLayoutShortcutActon(layoutUnit, rules, false); +// } +// else if( action != NULL && keySequence.isEmpty() ){ +//// action->setGlobalShortcut(KShortcut(keySequence), KAction::ActiveShortcut, KAction::NoAutoloading); // do we need this? +// removeAction(action); +// action = NULL; +// } +//// if( configAction ) { +//// layoutUnit.setShortcut(keySequence); // shortcut was restored +//// } +// return action; +//} + +void KeyboardLayoutActionCollection::setLayoutShortcuts(QList& layoutUnits, const Rules* rules) +{ + for (QList::iterator i = layoutUnits.begin(); i != layoutUnits.end(); ++i) { + LayoutUnit& layoutUnit = *i; + if( ! layoutUnit.getShortcut().isEmpty() ) { + createLayoutShortcutActon(layoutUnit, rules, false); + } + } + kDebug() << "Cleaning component shortcuts on save" << KGlobalAccel::cleanComponent(COMPONENT_NAME); +} + +void KeyboardLayoutActionCollection::loadLayoutShortcuts(QList& layoutUnits, const Rules* rules) +{ + for (QList::iterator i = layoutUnits.begin(); i != layoutUnits.end(); ++i) { + LayoutUnit& layoutUnit = *i; + QAction* action = createLayoutShortcutActon(layoutUnit, rules, true); + const auto shortcut = KGlobalAccel::self()->shortcut(action); + if( ! shortcut.isEmpty() ) { + kDebug() << "Restored shortcut for" << layoutUnit.toString() << shortcut.first(); + layoutUnit.setShortcut(shortcut.first()); + } + else { + kDebug() << "Skipping empty shortcut for" << layoutUnit.toString(); + removeAction(action); + } + } + kDebug() << "Cleaning component shortcuts on load" << KGlobalAccel::cleanComponent(COMPONENT_NAME); +} + +//KAction* KeyboardLayoutActionCollection::getAction(const LayoutUnit& layoutUnit) +//{ +// for(int i=1; idata() == layoutUnit.toString() ) +// return static_cast(action(i)); +// } +// return NULL; +//} + +void KeyboardLayoutActionCollection::resetLayoutShortcuts() +{ + for(int i=1; isetShortcut(action(i), QList(), KGlobalAccel::NoAutoloading); + KGlobalAccel::self()->setDefaultShortcut(action(i), QList(), KGlobalAccel::NoAutoloading); + } +} diff --git a/kcms/keyboard/bindings.h b/kcms/keyboard/bindings.h new file mode 100644 index 0000000000..c5764556d9 --- /dev/null +++ b/kcms/keyboard/bindings.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef BINDINGS_H_ +#define BINDINGS_H_ + +#include + + +//class QObject; +class Rules; +class LayoutUnit; +template class QList; + + +class KeyboardLayoutActionCollection : public KActionCollection { +public: + KeyboardLayoutActionCollection(QObject* parent, bool configAction); + virtual ~KeyboardLayoutActionCollection(); + + QAction* getToggeAction(); +// KAction* getAction(const LayoutUnit& layoutUnit); + QAction* createLayoutShortcutActon(const LayoutUnit& layoutUnit, const Rules* rules, bool autoload); +// KAction* setShortcut(LayoutUnit& layoutUnit, const QKeySequence& keySequence, const Rules* rules); + void setLayoutShortcuts(QList& layoutUnits, const Rules* rules); + void setToggleShortcut(const QKeySequence& keySequence); + void loadLayoutShortcuts(QList& layoutUnits, const Rules* rules); + void resetLayoutShortcuts(); + +private: + bool configAction; +}; + +//KActionCollection* createGlobalActionCollection(QObject *parent, KAction** mainAction); +//KAction* createLayoutShortcutActon(KActionCollection* actionCollection, const LayoutUnit& layoutUnit, const Rules* rules); + +#endif /* BINDINGS_H_ */ diff --git a/kcms/keyboard/flags.cpp b/kcms/keyboard/flags.cpp new file mode 100644 index 0000000000..461accfcbc --- /dev/null +++ b/kcms/keyboard/flags.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "flags.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "x11_helper.h" + +//for text handling +#include "keyboard_config.h" +#include "xkb_rules.h" + + +static const int FLAG_MAX_WIDTH = 21; +static const int FLAG_MAX_HEIGHT = 14; +static const char flagTemplate[] = "l10n/%1/flag.png"; + +Flags::Flags(): + svg(NULL) +{ + transparentPixmap = new QPixmap(FLAG_MAX_WIDTH, FLAG_MAX_HEIGHT); + transparentPixmap->fill(Qt::transparent); +} + +Flags::~Flags() +{ + if( svg != NULL ) { + disconnect(svg, SIGNAL(repaintNeeded()), this, SLOT(themeChanged())); + delete svg; + } + delete transparentPixmap; +} + +const QIcon Flags::getIcon(const QString& layout) +{ + if( ! iconMap.contains(layout) ) { + iconMap[ layout ] = createIcon(layout); + } + return iconMap[ layout ]; +} + +QIcon Flags::createIcon(const QString& layout) +{ + QIcon icon; + if( ! layout.isEmpty() ) { + if( layout == "epo" ) { + QString file = KStandardDirs::locate("data", "kcmkeyboard/pics/epo.png"); + icon.addFile(file); + } + else { + QString countryCode = getCountryFromLayoutName( layout ); + if( ! countryCode.isEmpty() ) { + QString file = KStandardDirs::locate("locale", QString(flagTemplate).arg(countryCode)); + // kDebug() << "Creating icon for" << layout << "with" << file; + icon.addFile(file); + } + } + } + return icon; +} + + +//static +//const QStringList NON_COUNTRY_LAYOUTS = QString("ara,brai,epo,latam,mao").split(","); + +QString Flags::getCountryFromLayoutName(const QString& layout) const +{ + QString countryCode = layout; + + if( countryCode == "nec_vndr/jp" ) + return "jp"; + +// if( NON_COUNTRY_LAYOUTS.contain(layout) ) + if( countryCode.length() > 2 ) + return ""; + + return countryCode; +} + +//TODO: move this to some other class? + +QString Flags::getShortText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig) +{ + if( layoutUnit.isEmpty() ) + return QString("--"); + + QString layoutText = layoutUnit.layout; + + foreach(const LayoutUnit& lu, keyboardConfig.layouts) { + if( layoutUnit.layout == lu.layout && layoutUnit.variant == lu.variant ) { + layoutText = lu.getDisplayName(); + break; + } + } + +//TODO: good autolabel +// if( layoutText == layoutUnit.layout && layoutUnit.getDisplayName() != layoutUnit.layout ) { +// layoutText = layoutUnit.getDisplayName(); +// } + + return layoutText; +} + +QString Flags::getFullText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig, const Rules* rules) +{ + QString shortText = Flags::getShortText(layoutUnit, keyboardConfig); + QString longText = Flags::getLongText(layoutUnit, rules); + return i18nc("short layout label - full layout name", "%1 - %2", shortText, longText); +} + +static QString getDisplayText(const QString& layout, const QString& variant, const Rules* rules) +{ + if( variant.isEmpty() ) + return layout; + if( rules == NULL || rules->version == "1.0" ) + return i18nc("layout - variant", "%1 - %2", layout, variant); + return variant; +} + +QString Flags::getLongText(const LayoutUnit& layoutUnit, const Rules* rules) +{ + if( rules == NULL ) { + return getDisplayText(layoutUnit.layout, layoutUnit.variant, rules); + } + + QString layoutText = layoutUnit.layout; + const LayoutInfo* layoutInfo = rules->getLayoutInfo(layoutUnit.layout); + if( layoutInfo != NULL ) { + layoutText = layoutInfo->description; + + if( ! layoutUnit.variant.isEmpty() ) { + const VariantInfo* variantInfo = layoutInfo->getVariantInfo(layoutUnit.variant); + QString variantText = variantInfo != NULL ? variantInfo->description : layoutUnit.variant; + + layoutText = getDisplayText(layoutText, variantText, rules); + } + } + + return layoutText; +} + +static +QString getPixmapKey(const KeyboardConfig& keyboardConfig) +{ + switch(keyboardConfig.indicatorType) { + case KeyboardConfig::SHOW_FLAG: + return "_fl"; + case KeyboardConfig::SHOW_LABEL_ON_FLAG: + return "_bt"; + case KeyboardConfig::SHOW_LABEL: + return "_lb"; + } + return "_"; // should not happen +} + +void Flags::drawLabel(QPainter& painter, const QString& layoutText, bool flagShown) +{ + QFont font = painter.font(); + + QRect rect = painter.window(); +// int fontSize = layoutText.length() == 2 +// ? height * 7 / 10 +// : height * 5 / 10; + + int fontSize = rect.height();// * 7 /10; + + font.setPixelSize(fontSize); + font.setWeight(QFont::DemiBold); + + QFontMetrics fm = painter.fontMetrics(); + int width = fm.width(layoutText); + + if( width > rect.width() * 2 / 3 ) { + fontSize = round( (double)fontSize * ((double)rect.width()*2/3) / width ); + } + + int smallestReadableSize = KGlobalSettings::smallestReadableFont().pixelSize(); + if( fontSize < smallestReadableSize ) { + fontSize = smallestReadableSize; + } + font.setPixelSize(fontSize); + +#ifdef DONT_USE_PLASMA + painter.setFont(font); + painter.setPen(Qt::white); + painter.drawText(QRect(rect).adust(1,1,0,0), Qt::AlignCenter | Qt::AlignHCenter, layoutText); + painter.setPen(Qt::black); + painter.drawText(rect, Qt::AlignCenter | Qt::AlignHCenter, layoutText); +#else + // we init svg so that we get notification about theme change + getSvg(); + + Plasma::Theme theme; + QColor textColor = flagShown ? Qt::black : theme.color(Plasma::Theme::TextColor); + QPoint offset = QPoint(0, 0); + + auto shadowText = [&font, &textColor, &offset](QString text) { + //don't try to paint stuff on a future null pixmap because the text is empty + if (text.isEmpty()) { + return QPixmap(); + } + + // Draw text + QFontMetrics fm(font); + QRect textRect = fm.boundingRect(text); + QPixmap textPixmap(textRect.width(), fm.height()); + textPixmap.fill(Qt::transparent); + QPainter p(&textPixmap); + p.setPen(textColor); + p.setFont(font); + // FIXME: the center alignment here is odd: the rect should be the size needed by + // the text, but for some fonts and configurations this is off by a pixel or so + // and "centering" the text painting 'fixes' that. Need to research why + // this is the case and determine if we should be painting it differently here, + // doing soething different with the boundingRect call or if it's a problem + // in Qt itself + p.drawText(textPixmap.rect(), Qt::AlignCenter, text); + p.end(); + + return textPixmap; + }; + + // QPixmap pixmap = Plasma::PaintUtils::texturedText(layoutText, font, svg); + QPixmap labelPixmap = shadowText(layoutText); + + int y = round((rect.height() - labelPixmap.height()) / 2.0); + int x = round((rect.width() - labelPixmap.width()) / 2.0); + painter.drawPixmap(QPoint(x, y), labelPixmap); +#endif +} + +const QIcon Flags::getIconWithText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig) +{ + QString keySuffix(getPixmapKey(keyboardConfig)); + QString key(layoutUnit.toString() + keySuffix); + if( iconOrTextMap.contains(key) ) { + return iconOrTextMap[ key ]; + } + + if( keyboardConfig.indicatorType == KeyboardConfig::SHOW_FLAG ) { + QIcon icon = getIcon(layoutUnit.layout); + if( ! icon.isNull() ) { + iconOrTextMap[ key ] = icon; + return icon; + } + } + + QString layoutText = Flags::getShortText(layoutUnit, keyboardConfig); + + const QSize TRAY_ICON_SIZE(21, 14); + QPixmap pixmap = QPixmap(TRAY_ICON_SIZE); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); +// p.setRenderHint(QPainter::SmoothPixmapTransform); +// p.setRenderHint(QPainter::Antialiasing); + + if( keyboardConfig.indicatorType == KeyboardConfig::SHOW_LABEL_ON_FLAG ) { + QIcon iconf = createIcon(layoutUnit.layout); + iconf.paint(&painter, painter.window(), Qt::AlignCenter); + } + + drawLabel(painter, layoutText, keyboardConfig.isFlagShown()); + + painter.end(); + + QIcon icon(pixmap); + iconOrTextMap[ key ] = icon; + + return icon; +} + +Plasma::Svg* Flags::getSvg() +{ + if( svg == NULL ) { + svg = new Plasma::Svg; + svg->setImagePath("widgets/labeltexture"); + svg->setContainsMultipleImages(true); + connect(svg, SIGNAL(repaintNeeded()), this, SLOT(themeChanged())); + } + return svg; +} + +void Flags::themeChanged() +{ +// kDebug() << "Theme changed, new text color" << Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor); + clearCache(); + emit pixmapChanged(); +} + +void Flags::clearCache() +{ +// kDebug() << "Clearing flag pixmap cache"; + iconOrTextMap.clear(); +} diff --git a/kcms/keyboard/flags.h b/kcms/keyboard/flags.h new file mode 100644 index 0000000000..06ec831b90 --- /dev/null +++ b/kcms/keyboard/flags.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef FLAGS_H_ +#define FLAGS_H_ + +#include +#include +#include + +class QPixmap; +class QIcon; +class LayoutUnit; +class KeyboardConfig; +class Rules; +class QPainter; +namespace Plasma { + class Svg; +} + +class Flags : public QObject +{ + Q_OBJECT + +public: + Flags(); + virtual ~Flags(); + + const QIcon getIcon(const QString& layout); + const QIcon getIconWithText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig); + const QPixmap& getTransparentPixmap() const { return *transparentPixmap; } + + static QString getLongText(const LayoutUnit& layoutUnit, const Rules* rules); + static QString getShortText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig); + static QString getFullText(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig, const Rules* rules); + +public Q_SLOTS: + void themeChanged(); + void clearCache(); + +Q_SIGNALS: + void pixmapChanged(); + +private: + QIcon createIcon(const QString& layout); + QString getCountryFromLayoutName(const QString& fullLayoutName) const; + void drawLabel(QPainter& painter, const QString& layoutText, bool flagShown); + Plasma::Svg* getSvg(); + + QMap iconMap; + QMap iconOrTextMap; + QPixmap* transparentPixmap; + Plasma::Svg* svg; +}; + +#endif /* FLAGS_H_ */ diff --git a/kcms/keyboard/iso_codes.cpp b/kcms/keyboard/iso_codes.cpp new file mode 100644 index 0000000000..3e3b210b9f --- /dev/null +++ b/kcms/keyboard/iso_codes.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "iso_codes.h" + +#include +#include +#include + +#include + + +class IsoCodesPrivate { +public: + IsoCodesPrivate(const QString& isoCode_, const QString& isoCodesXmlDir_): + isoCode(isoCode_), + isoCodesXmlDir(isoCodesXmlDir_), + loaded(false) + {} + void buildIsoEntryList(); + + const QString isoCode; + const QString isoCodesXmlDir; + QList isoEntryList; + bool loaded; +}; + +class XmlHandler : public QXmlDefaultHandler +{ +public: + XmlHandler(const QString& isoCode_, QList& isoEntryList_): + isoCode(isoCode_), + qName("iso_"+isoCode+"_entry"), + isoEntryList(isoEntryList_) {} + + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &attributes); +// bool fatalError(const QXmlParseException &exception); +// QString errorString() const; + +private: + const QString isoCode; + const QString qName; + QList& isoEntryList; +}; + +bool XmlHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, + const QString &qName, const QXmlAttributes &attributes) +{ + if( qName == this->qName ) { + IsoCodeEntry entry; + for(int i=0; i IsoCodes::getEntryList() +{ + if( ! d->loaded ) { + d->buildIsoEntryList(); + } + return d->isoEntryList; +} + +//const char* IsoCodes::iso_639="639"; +const char* IsoCodes::iso_639_3="639_3"; +const char* IsoCodes::attr_name="name"; +//const char* IsoCodes::attr_iso_639_2B_code="iso_639_2B_code"; +//const char* IsoCodes::attr_iso_639_2T_code="iso_639_2T_code"; +//const char* IsoCodes::attr_iso_639_1_code="iso_639_1_code"; +const char* IsoCodes::attr_iso_639_3_id="id"; + +const IsoCodeEntry* IsoCodes::getEntry(const QString& attributeName, const QString& attributeValue) +{ + if( ! d->loaded ) { + d->buildIsoEntryList(); + } + for(QList::Iterator it = d->isoEntryList.begin(); it != d->isoEntryList.end(); ++it) { + const IsoCodeEntry* isoCodeEntry = &(*it); + if( isoCodeEntry->value(attributeName) == attributeValue ) + return isoCodeEntry; + } + return NULL; +} + +void IsoCodesPrivate::buildIsoEntryList() +{ + loaded = true; + + QFile file(QString("%1/iso_%2.xml").arg(isoCodesXmlDir, isoCode)); + if( !file.open(QFile::ReadOnly | QFile::Text) ) { + kError() << "Can't open the xml file" << file.fileName(); + return; + } + + XmlHandler xmlHandler(isoCode, isoEntryList); + + QXmlSimpleReader reader; + reader.setContentHandler(&xmlHandler); + reader.setErrorHandler(&xmlHandler); + + QXmlInputSource xmlInputSource(&file); + + if( ! reader.parse(xmlInputSource) ) { + kError() << "Failed to parse the xml file" << file.fileName(); + return; + } + + kDebug() << "Loaded" << isoEntryList.count() << ("iso entry definitions for iso"+isoCode) << "from" << file.fileName(); +} diff --git a/kcms/keyboard/iso_codes.h b/kcms/keyboard/iso_codes.h new file mode 100644 index 0000000000..6a337392e9 --- /dev/null +++ b/kcms/keyboard/iso_codes.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ISO_CODES_H_ +#define ISO_CODES_H_ + +#include +#include +#include + + +/** + * Represents an item for iso-* standards which consists of attributes and their values + */ +struct IsoCodeEntry: public QMap +{ +}; + +class IsoCodesPrivate; + +/** + * Represents a set of codes for iso-* standards. + * Uses iso-codes project to read and localize the values. + */ +class IsoCodes +{ +public: +// static const char* iso_639; + static const char* iso_639_3; + + static const char* attr_name; +// static const char* attr_iso_639_2B_code; +// static const char* attr_iso_639_2T_code; +// static const char* attr_iso_639_1_code; + static const char* attr_iso_639_3_id; + + /** + * @param isoCode Code for iso standard, i.e. "639", for convenience there's iso_* constants defined + */ + explicit IsoCodes(const QString& isoCode, const QString& isoCodesXmlDir="/usr/share/xml/iso-codes"); + ~IsoCodes(); + + /** + * @return Returns the list of items for this iso-* standard + */ + QList getEntryList(); + /** + * @return Returns the item for which given attribute has specified value + */ + const IsoCodeEntry* getEntry(const QString& attributeName, const QString& attributeValue); + +private: + IsoCodesPrivate* const d; +}; + +#endif /* ISO_CODES_H_ */ diff --git a/kcms/keyboard/kcm_add_layout_dialog.cpp b/kcms/keyboard/kcm_add_layout_dialog.cpp new file mode 100644 index 0000000000..444da8eaf5 --- /dev/null +++ b/kcms/keyboard/kcm_add_layout_dialog.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcm_add_layout_dialog.h" +#include +#include + +#include "xkb_rules.h" +#include "flags.h" +#include "iso_codes.h" + +#include "ui_kcm_add_layout_dialog.h" + + +AddLayoutDialog::AddLayoutDialog(const Rules* rules_, Flags* flags_, bool showLabel, QWidget* parent): + QDialog(parent), + rules(rules_), + flags(flags_), + selectedLanguage("no_language") +{ + layoutDialogUi = new Ui_AddLayoutDialog(); + layoutDialogUi->setupUi(this); + + QSet languages; + foreach(const LayoutInfo* layoutInfo, rules->layoutInfos) { + QSet langs = QSet::fromList(layoutInfo->languages); + languages.unite( langs ); + } + IsoCodes isoCodes(IsoCodes::iso_639_3); + foreach(const QString& lang, languages) { + const IsoCodeEntry* isoCodeEntry = isoCodes.getEntry(IsoCodes::attr_iso_639_3_id, lang); +// const IsoCodeEntry* isoCodeEntry = isoCodes.getEntry(IsoCodes::attr_iso_639_2B_code, lang); +// if( isoCodeEntry == NULL ) { +// isoCodeEntry = isoCodes.getEntry(IsoCodes::attr_iso_639_2T_code, lang); +// } + QString name = isoCodeEntry != NULL ? i18n(isoCodeEntry->value(IsoCodes::attr_name).toUtf8()) : lang; + layoutDialogUi->languageComboBox->addItem(name, lang); + } + layoutDialogUi->languageComboBox->model()->sort(0); + layoutDialogUi->languageComboBox->insertItem(0, i18n("Any language"), ""); + layoutDialogUi->languageComboBox->setCurrentIndex(0); + + if( showLabel ) { + layoutDialogUi->labelEdit->setMaxLength(LayoutUnit::MAX_LABEL_LENGTH); + } + else { + layoutDialogUi->labelLabel->setVisible(false); + layoutDialogUi->labelEdit->setVisible(false); + } + + languageChanged(0); + connect(layoutDialogUi->languageComboBox, SIGNAL(activated(int)), this, SLOT(languageChanged(int))); + connect(layoutDialogUi->layoutComboBox, SIGNAL(activated(int)), this, SLOT(layoutChanged(int))); + connect(layoutDialogUi->prevbutton,SIGNAL(clicked()),this,SLOT(preview())); +} + +void AddLayoutDialog::languageChanged(int langIdx) +{ + QString lang = layoutDialogUi->languageComboBox->itemData(langIdx).toString(); + if( lang == selectedLanguage ) + return; + + QPixmap emptyPixmap(layoutDialogUi->layoutComboBox->iconSize()); + emptyPixmap.fill(Qt::transparent); + + layoutDialogUi->layoutComboBox->clear(); + int defaultIndex = -1; + int i = 0; + foreach(const LayoutInfo* layoutInfo, rules->layoutInfos) { + if( lang.isEmpty() || layoutInfo->isLanguageSupportedByLayout(lang) ) { + if( flags ) { + QIcon icon(flags->getIcon(layoutInfo->name)); + if( icon.isNull() ) { + icon = QIcon(emptyPixmap); // align text with no icons + } + layoutDialogUi->layoutComboBox->addItem(icon, layoutInfo->description, layoutInfo->name); + } + else { + layoutDialogUi->layoutComboBox->addItem(layoutInfo->description, layoutInfo->name); + } + + // try to guess best default layout selection for given language + if( ! lang.isEmpty() && defaultIndex == -1 && layoutInfo->isLanguageSupportedByDefaultVariant(lang) ) { + defaultIndex = i; + } + i++; + } + } + if( defaultIndex == -1 ) { + defaultIndex = 0; + } + + layoutDialogUi->layoutComboBox->model()->sort(0); + layoutDialogUi->layoutComboBox->setCurrentIndex(defaultIndex); + layoutChanged(defaultIndex); + + selectedLanguage = lang; +} + +void AddLayoutDialog::layoutChanged(int layoutIdx) +{ + QString layoutName = layoutDialogUi->layoutComboBox->itemData(layoutIdx).toString(); + if( layoutName == selectedLayout ) + return; + + QString lang = layoutDialogUi->languageComboBox->itemData(layoutDialogUi->languageComboBox->currentIndex()).toString(); + + layoutDialogUi->variantComboBox->clear(); + const LayoutInfo* layoutInfo = rules->getLayoutInfo(layoutName); + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + if( lang.isEmpty() || layoutInfo->isLanguageSupportedByVariant(variantInfo, lang) ) { + layoutDialogUi->variantComboBox->addItem(variantInfo->description, variantInfo->name); + } + } + + layoutDialogUi->variantComboBox->model()->sort(0); + + if( lang.isEmpty() || layoutInfo->isLanguageSupportedByDefaultVariant(lang) ) { + layoutDialogUi->variantComboBox->insertItem(0, i18nc("variant", "Default"), ""); + } + layoutDialogUi->variantComboBox->setCurrentIndex(0); + + layoutDialogUi->labelEdit->setText(layoutName); + + selectedLayout = layoutName; +} + +void AddLayoutDialog::accept() +{ + selectedLayoutUnit.layout = layoutDialogUi->layoutComboBox->itemData(layoutDialogUi->layoutComboBox->currentIndex()).toString(); + selectedLayoutUnit.variant = layoutDialogUi->variantComboBox->itemData(layoutDialogUi->variantComboBox->currentIndex()).toString(); + QString label = layoutDialogUi->labelEdit->text(); + if( label == selectedLayoutUnit.layout ) { + label = ""; + } + selectedLayoutUnit.setDisplayName( label ); + selectedLayoutUnit.setShortcut(layoutDialogUi->kkeysequencewidget->keySequence()); + QDialog::accept(); +} + + +void AddLayoutDialog::preview(){ + int index = layoutDialogUi->variantComboBox->currentIndex(); + QString variant = layoutDialogUi->variantComboBox->itemData(index).toString(); + KeyboardPainter* layoutPreview = new KeyboardPainter(); + layoutPreview->generateKeyboardLayout(selectedLayout, variant); + layoutPreview->setModal(true); + layoutPreview->exec(); + delete layoutPreview; +} diff --git a/kcms/keyboard/kcm_add_layout_dialog.h b/kcms/keyboard/kcm_add_layout_dialog.h new file mode 100644 index 0000000000..6b82271787 --- /dev/null +++ b/kcms/keyboard/kcm_add_layout_dialog.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KCM_ADD_LAYOUT_DIALOG_H_ +#define KCM_ADD_LAYOUT_DIALOG_H_ + +#include + +#include "keyboard_config.h" +#include "preview/keyboardpainter.h" + +class Rules; +class Flags; +class Ui_AddLayoutDialog; + +class AddLayoutDialog: public QDialog +{ + Q_OBJECT + +public: + AddLayoutDialog(const Rules* rules, Flags* flags, bool showLabel, QWidget* parent=NULL); + + LayoutUnit getSelectedLayoutUnit() { return selectedLayoutUnit; } + QString getvariant(QString variant); + void accept(); + +public Q_SLOTS: + void languageChanged(int langIdx); + void layoutChanged(int layoutIdx); + void preview(); + +private: + const Rules* rules; + Flags* flags; + Ui_AddLayoutDialog* layoutDialogUi; + QString selectedLanguage; + QString selectedLayout; + LayoutUnit selectedLayoutUnit; +}; + + +#endif /* KCM_ADD_LAYOUT_DIALOG_H_ */ diff --git a/kcms/keyboard/kcm_add_layout_dialog.ui b/kcms/keyboard/kcm_add_layout_dialog.ui new file mode 100644 index 0000000000..78304fbc1e --- /dev/null +++ b/kcms/keyboard/kcm_add_layout_dialog.ui @@ -0,0 +1,149 @@ + + + AddLayoutDialog + + + + 0 + 0 + 525 + 270 + + + + Add Layout + + + + + + + 0 + 0 + + + + + + + + Layout: + + + + + + + + 0 + 0 + + + + + + + + Shortcut: + + + + + + + Variant: + + + + + + + + + + Label: + + + + + + + Limit selection by language: + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + Preview + + + + + + + + KKeySequenceWidget + QWidget +
    kkeysequencewidget.h
    +
    +
    + + + + buttonBox + accepted() + AddLayoutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddLayoutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/kcms/keyboard/kcm_keyboard.cpp b/kcms/keyboard/kcm_keyboard.cpp new file mode 100644 index 0000000000..30dc5fad2f --- /dev/null +++ b/kcms/keyboard/kcm_keyboard.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "kcm_keyboard.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +//#include + +#include "kcm_keyboard_widget.h" +#include "x11_helper.h" +#include "keyboard_config.h" +#include "xkb_rules.h" +#include "keyboard_dbus.h" + +#include "xkb_helper.h" + +//temp hack +#include "kcmmisc.h" + + +K_PLUGIN_FACTORY(KeyboardModuleFactory, registerPlugin();) +K_EXPORT_PLUGIN(KeyboardModuleFactory("kcmkeyboard")) + +KCMKeyboard::KCMKeyboard(QWidget *parent, const QVariantList &args) + : KCModule(parent/*, name*/) +{ + KAboutData *about = + new KAboutData("kcmkeyboard", QString(), i18n("KDE Keyboard Control Module"), + 0, QString(), KAboutData::License_GPL, + i18n("(c) 2010 Andriy Rysin")); + + setAboutData( about ); + setQuickHelp( i18n("

    Keyboard

    This control module can be used to configure keyboard" + " parameters and layouts.")); + + + rules = Rules::readRules(Rules::READ_EXTRAS); + + keyboardConfig = new KeyboardConfig(); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(KDialog::spacingHint()); + + widget = new KCMKeyboardWidget(rules, keyboardConfig, args, parent); + layout->addWidget(widget); + + connect(widget, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); + + setButtons(Help|Default|Apply); +} + +KCMKeyboard::~KCMKeyboard() +{ + delete keyboardConfig; + delete rules; +} + +void KCMKeyboard::defaults() +{ + keyboardConfig->setDefaults(); + widget->updateUI(); + widget->getKcmMiscWidget()->defaults(); + emit changed(true); +} + +void KCMKeyboard::load() +{ + keyboardConfig->load(); + widget->updateUI(); + widget->getKcmMiscWidget()->load(); +} + +//static void initializeKeyboardSettings(); +void KCMKeyboard::save() +{ + keyboardConfig->save(); + widget->save(); + widget->getKcmMiscWidget()->save(); + + QDBusMessage message = QDBusMessage::createSignal(KEYBOARD_DBUS_OBJECT_PATH, KEYBOARD_DBUS_SERVICE_NAME, KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE); + QDBusConnection::sessionBus().send(message); +} + +#include "kcm_keyboard.moc" diff --git a/kcms/keyboard/kcm_keyboard.desktop b/kcms/keyboard/kcm_keyboard.desktop new file mode 100644 index 0000000000..3aef59650a --- /dev/null +++ b/kcms/keyboard/kcm_keyboard.desktop @@ -0,0 +1,241 @@ +[Desktop Entry] +Exec=kcmshell5 kcm_keyboard +Icon=preferences-desktop-keyboard +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/keyboard/index.html +Categories=Qt;KDE;X-KDE-settings-hardware; + +X-KDE-Library=kcm_keyboard +X-KDE-Init-Symbol=keyboard +X-KDE-ParentApp=kcontrol +X-KDE-OnlyShowOnQtPlatforms=xcb; + +X-KDE-System-Settings-Parent-Category=input-devices +X-KDE-Weight=50 +OnlyShowIn=KDE; + +Name=Keyboard +Name[af]=Sleutelbord +Name[ar]=لوحة المفاتيح +Name[ast]=Tecláu +Name[be]=Клавіятура +Name[be@latin]=Klavijatura +Name[bg]=Клавиатура +Name[bn]=কীবোর্ড +Name[bn_IN]=কি-বোর্ড +Name[br]=Stokellaoueg +Name[bs]=Tastatura +Name[ca]=Teclat +Name[ca@valencia]=Teclat +Name[cs]=Klávesnice +Name[csb]=Klawiatura +Name[cy]=Bysellfwrdd +Name[da]=Tastatur +Name[de]=Tastatur +Name[el]=Πληκτρολόγιο +Name[en_GB]=Keyboard +Name[eo]=Klavaro +Name[es]=Teclado +Name[et]=Klaviatuur +Name[eu]=Teklatua +Name[fa]=صفحه کلید +Name[fi]=Näppäimistö +Name[fr]=Clavier +Name[fy]=Toetseboerd +Name[ga]=Méarchlár +Name[gl]=Teclado +Name[gu]=કીબોર્ડ +Name[he]=מקלדת +Name[hi]=कुंजीपट +Name[hne]=कुंजीपट +Name[hr]=Tipkovnica +Name[hsb]=Tastatura +Name[hu]=Billentyűzet +Name[ia]=Claviero +Name[id]=Papan Ketik +Name[is]=Lyklaborð +Name[it]=Tastiera +Name[ja]=キーボード +Name[ka]=კლავიატურა +Name[kk]=Перенетақта +Name[km]=ក្ដារ​ចុច +Name[kn]=ಕೀಲಿಮಣೆ +Name[ko]=키보드 +Name[ku]=Klavye +Name[lt]=Klaviatūra +Name[lv]=Tastatūra +Name[mai]=कुँजीपटल +Name[mk]=Тастатура +Name[ml]=കീബോര്‍ഡ് +Name[mr]=कळफलक +Name[nb]=Tastatur +Name[nds]=Tastatuur +Name[ne]=कुञ्जीपाटी +Name[nl]=Toetsenbord +Name[nn]=Tastatur +Name[oc]=Clavièr +Name[or]=କିବୋର୍ଡ଼ +Name[pa]=ਕੀਬੋਰਡ +Name[pl]=Klawiatura +Name[pt]=Teclado +Name[pt_BR]=Teclado +Name[ro]=Tastatură +Name[ru]=Клавиатура +Name[se]=Boallobeavdi +Name[si]=යතුරුපුවරුව +Name[sk]=Klávesnica +Name[sl]=Tipkovnica +Name[sr]=Тастатура +Name[sr@ijekavian]=Тастатура +Name[sr@ijekavianlatin]=Tastatura +Name[sr@latin]=Tastatura +Name[sv]=Tangentbord +Name[ta]=விசைப்பலகை +Name[te]=కీబోర్డ్ +Name[tg]=Клавиатура +Name[th]=แป้นพิมพ์ +Name[tr]=Klavye +Name[ug]=ھەرپتاختا +Name[uk]=Клавіатура +Name[uz]=Tugmatag +Name[uz@cyrillic]=Тугматаг +Name[vi]=Bàn phím +Name[wa]=Taprece +Name[xh]=Ibhodi enezitshixo +Name[x-test]=xxKeyboardxx +Name[zh_CN]=键盘 +Name[zh_TW]=鍵盤 + +Comment=Keyboard settings +Comment[af]=Sleutelbord instellings +Comment[ar]=إعدادات لوحة المفاتيح +Comment[ast]=Preferencies del tecláu +Comment[be]=Настаўленні клавіятуры +Comment[be@latin]=Nałady klavijatury +Comment[bg]=Настройки на клавиатура +Comment[bn]=কীবোর্ড সেটিংস +Comment[bn_IN]=কি-বোর্ড সংক্রান্ত বৈশিষ্ট্য +Comment[br]=Kefluniañ ar stokellaoueg +Comment[bs]=Postavke tastature +Comment[ca]=Arranjament del teclat +Comment[ca@valencia]=Arranjament del teclat +Comment[cs]=Nastavení klávesnice +Comment[csb]=Nastôw klawiaturë +Comment[cy]=Gosodiadau Bysellfwrdd +Comment[da]=Tastaturindstillinger +Comment[de]=Einstellung der Tastatur +Comment[el]=Ρυθμίσεις πληκτρολογίου +Comment[en_GB]=Keyboard settings +Comment[eo]=Agordo de la klavaro +Comment[es]=Preferencias del teclado +Comment[et]=Klaviatuuri seadistused +Comment[eu]=Teklatuaren ezarpenak +Comment[fa]=تنظیمات صفحه کلید +Comment[fi]=Näppäimistön asetukset +Comment[fr]=Configuration du clavier +Comment[fy]=Toetseboerd ynstelle +Comment[ga]=Socruithe Méarchláir +Comment[gl]=Configuración do teclado +Comment[gu]=કીબોર્ડ ગોઠવણીઓ +Comment[he]=הגדרות המקלדת +Comment[hi]=कुंजीपट विन्यास +Comment[hne]=कुंजीपट सेटिंग +Comment[hr]=Postavke tipkovnice +Comment[hsb]=Nastajenja za tastaturu +Comment[hu]=A billentyűzet beállításai +Comment[ia]=Preferentias de claviero +Comment[id]=Pengaturan Papan Ketik +Comment[is]=Stillingar lyklaborðs +Comment[it]=Impostazioni della tastiera +Comment[ja]=キーボードの設定 +Comment[ka]=კლავიატურის კონფიგურაცია +Comment[kk]=Перенетақтаны баптау +Comment[km]=ការ​កំណត់​ក្ដារចុច +Comment[kn]=ಕೀಲಿಮಣೆ ಸಂಯೋಜನೆಗಳು +Comment[ko]=키보드 설정 +Comment[ku]=Mîhengên Klavyeyê +Comment[lt]=Klaviatūros parametrai +Comment[lv]=Tastatūras parametri +Comment[mai]=कुंजीपट बिन्यास +Comment[mk]=Поставувања на тастатурата +Comment[ml]=കീബോര്‍ഡിന്റെ സജ്ജീകരണങ്ങള്‍ +Comment[mr]=कळफलक संयोजना +Comment[nb]=Tastaturinnstillinger +Comment[nds]=Tastatuurinstellen +Comment[ne]=कुञ्जीपाटी सेटिङ +Comment[nl]=Toetsenbord instellen +Comment[nn]=Tastaturinnstillingar +Comment[oc]=Paramètres de clavièr +Comment[or]=କିବୋର୍ଡ଼ ସଂରଚନା +Comment[pa]=ਕੀਬੋਰਡ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia klawiatury +Comment[pt]=Configuração do teclado +Comment[pt_BR]=Configurações do teclado +Comment[ro]=Configurări tastatură +Comment[ru]=Настройка клавиатуры +Comment[se]=Boallobeavdeheivehusat +Comment[si]=යතුරුපුවරු සැකසුම් +Comment[sk]=Nastavenie klávesnice +Comment[sl]=Nastavitve tipkovnice +Comment[sr]=Поставке тастатуре +Comment[sr@ijekavian]=Поставке тастатуре +Comment[sr@ijekavianlatin]=Postavke tastature +Comment[sr@latin]=Postavke tastature +Comment[sv]=Anpassa tangentbordets inställningar +Comment[ta]=விசைப்பலகை அமைப்புகள் +Comment[te]=కీబోర్డ్ అమరికలు +Comment[tg]=Танзимоти клавиатура +Comment[th]=ตั้งค่าต่าง ๆ ของแป้นพิมพ์ +Comment[tr]=Klavye ayarları +Comment[ug]=ھەرپتاختا تەڭشەك +Comment[uk]=Параметри клавіатури +Comment[uz]=Tugmatagning moslamalari +Comment[uz@cyrillic]=Тугматагнинг мосламалари +Comment[vi]=Thiết lập bàn phím +Comment[wa]=Apontiaedje del taprece +Comment[xh]=Izicwangciso zebhodi enezitshixo +Comment[x-test]=xxKeyboard settingsxx +Comment[zh_CN]=键盘设置 +Comment[zh_TW]=鍵盤設定 + +X-KDE-Keywords=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key +X-KDE-Keywords[bs]=tastatura,ponavljanje tastature,kliknute glasnoću,umetnuti uređaj,ponoviti,glasnoća,NumLock,NumPad,tip tastature,model tastature,izgled tastature,izgled tipki,jezik,rezervna tastatura,uključivanje tastature,Ctrl tipka,velika slova,esperanto,cirkumfleks,ukloniti X server,LED tastatura,sastavljene tipke +X-KDE-Keywords[ca]=Teclat,Repetició de teclat,Volum de clic,Dispositius d'entrada,repetició,volum,BloqNum,NumPad,Tipus de teclat,Model de teclat,Disposició de teclat,Idioma,Teclat alternatiu,Commutació de teclat,Tecla Ctrl,Bloq Maj,Esperanto,Circumflex,Matar servidor X,LED de teclat,Tecla compose +X-KDE-Keywords[ca@valencia]=Teclat,Repetició de teclat,Volum de clic,Dispositius d'entrada,repetició,volum,BloqNum,NumPad,Tipus de teclat,Model de teclat,Disposició de teclat,Idioma,Teclat alternatiu,Commutació de teclat,Tecla Ctrl,Bloq Maj,Esperanto,Circumflex,Matar servidor X,LED de teclat,Tecla compose +X-KDE-Keywords[da]=Tastatur,keyboard,tastaturgentagelse,klikvolume,inputenheder,gentag,lydstyrke,NumLock,numerisk tastatur,tastaturtype,tastaturmodel,tastaturlayout,tastelayout,sprog,alternativt tastatur,skift af tastatur,Ctrl-tast,lås skift,caps lock,Esperanto,Circumflex,dræb X Server,LED-tastatur,Compose-tast +X-KDE-Keywords[de]=Tastatur,Tastenwiederholung,Klicklautstärke,Eingabegeräte,Wiederholung,Lautstärke,Zahlen-Feststelltaste,Zahlenblock,Tastaturtyp,Tastaturmodell,Tastaturlayout,Tastenlayout,Sprache,Alternative Tastatur,Tastaturwechsel,Tastaturbelegung,Strg-Taste,Umschalttaste,Esperanto,Zirkumflex,X-Server beenden,Tastatur LED,Compose-Taste +X-KDE-Keywords[el]=πληκτρολόγιο,επανάληψη πληκτρολογίου,ένταση κλικ,συσκευές εισόδου,επανάληψη,ένταση,NumLock,αριθμητικό πληκτρολόγιο,τύπος πληκτρολογίου,μοντέλο πληκτρολογίου,διάταξη πληκτρολογίου,γλώσσα,εναλλακτικό πληκτρολόγιο,εναλλαγή πληκτρολογίου,πλήκτρο Ctrl,Caps Lock,Esperanto,Circumflex,Kill X Server,LED πληκτρολόγιο,πλήκτρο σύνθεσης +X-KDE-Keywords[es]=Teclado,Repetición del teclado,Volumen del clic,Dispositivos de entrada,repetición,volumen,Bloqueo numérico,Teclado numérico,Tipo de teclado,Modelo de teclado,Disposición del teclado,Disposición de las teclas,Idioma,Teclado alternativo,Cambiar de teclado,Tecla Ctrl,Bloqueo de mayúsculas,Esperanto,Circunflejo,Matar el servidor X,LED del teclado,Tecla de composición +X-KDE-Keywords[et]=Klaviatuur,Klaviatuurikordus,Klõpsu helitugevus,Sisendseadmed,kordus,helitugevus,NumLock,NumPad,numbriklahvistik,Klaviatuurimudel,Klaviatuuri mudel,Klaviatuuripaigutus,Klaviatuuri paigutus,Paigutus,Keel,Alternatiivne klaviatuur,Klaviatuuri vahetamine,Ctrl,Caps Lock,Esperanto,Tsirkumfleks,Katus,X-serveri tapmine,LDE-klaviatuur,Muuteklahv +X-KDE-Keywords[eu]=teklatu, teklatuaren errepikapen,klikaren bolumena,sarrerako gailuak,errepikatu,bolumena,BlokZenb,zenbakizko teklatu,teklatu mota,teklatuaren modeloa,teklatu-diseinua,teklen diseinua,hizkuntza, teklatu alternatiboa,teklatua aldatzea,ktrl tekla,BlokMaius,esperanto,zirkunflexua,hil X zerbitzaria,LED teklatua,tekla konposatua +X-KDE-Keywords[fi]=näppäimistö,näppäimistön toisto,näppäilyäänen voimakkuus,syöttölaitteet,toisto,numlock,numerolukko,numpad,numeronäppäimistö,näppäimistön tyyppi,näppäimistön malli,näppäimistöasettelu,näppäinasettelu,kieli,vaihtoehtoinen näppäimistö,näppäimistön vaihto,näppäimistön vaihtaminen,Ctrl-näppäin,caps lock,Esperanto,tapa X-palvelin,LED-näppäimistö,compose-näppäin +X-KDE-Keywords[fr]=Clavier, Répétition du clavier, Volume du clic, Périphériques d'entrée, répétition, volume, Verrouillage numérique, Pavé numérique, Type de clavier, Modèle de clavier, Disposition du clavier, Disposition des touches, Langue, Clavier alternatif, Changement de clavier, Touche Ctrl, Verrouillage des majuscule, Espéranto, Circonflexe, Tuer le serveur X, Voyants du clavier, Touche de composition +X-KDE-Keywords[gl]=teclado, repetición de teclas, volume do clic, dispositivo de entrada, repetición, volume, Bloq núm, Bloq num, tipo de teclado, modelo, disposición idioma, teclado alternativo, troco de teclado, Ctrl, Bloq maiús, til, circunflexo, matar o servidor X, LED +X-KDE-Keywords[hu]=Billentyűzet,Billentyűzet ismétlés,Kattintási hangerő,Beviteli eszközök,ismétlés,hangerő,NumLock,Numerikus billentyűzet,Billentyűzet típus,Billentyűzet modell,Billentyűzetkiosztás,Nyelv,Alternatív billentyűzet,Billentyűzetváltó,Ctrl billentyű,Caps Lock,Eszperantó,Ékezet,X kiszolgáló kilövése,LED billentyűzet,Levélírás billentyű +X-KDE-Keywords[ia]=Claviero,Repetition de claviero,Volumine de click,Dispositivos de ingresso,volumine, NumLock,Typo de claviero,modello de Claviero,disposition de claviero, Disposition de clave, Linguage,Claviero alternate,commutation de claviero,Clave Ctrl,Caps Lock,Esperanto, Circumflexe,Occide X Server, Claviero LED, Clave composite +X-KDE-Keywords[it]=tastiera,ripetizione tastiera,clic volume,dispositivi di ingresso,ripetizione,volume,bloc num,tastierino numerico,tipo tastiera, modello tastiera,mappatura tastiera,mappatura tasto,lingua,tastiera alternativa,cambio tastiera, tasto ctrl,maiusc,esperanto,circonflesso,uccidi server x,led tastiera,tasto composizione +X-KDE-Keywords[kk]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key +X-KDE-Keywords[km]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key +X-KDE-Keywords[ko]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,키보드,키보드 반복,입력 장치, 숫자패드,키보드 종류,레이아웃,입력,언어 +X-KDE-Keywords[mr]=की-बोर्ड,की-बोर्ड रिपीट, क्लिक व्हॉल्यूम, इनपुट डिव्हाइस, रिपीट, व्हॉल्यूम,न्यूम.लॉक,न्यूम.पॅड,की-बोर्ड टाईप, की-बोर्ड मॉडेल, की-बोर्ड लेआउट, कि लेआउट, कि लेआउट, भाषा, वैकल्पिक की-बोर्ड,की-बोर्ड स्विचिंग,सी.टी.आर.एल.की, कॅप्स लॉक, एस्परांटो,सर्कम्फलेकस,किल एक्स सर्व्हर, एल.इ.डी की बोर्ड, कंपोज की +X-KDE-Keywords[nb]=Tastatur,Tasterepetisjon,Klikklydstyrke,Inndataenheter,repeter,lydstyrke,NumLock,NumPad.Tastaturtype,Tastaturmodell,Tastaturutforming,Tateutlegg,Språk,Alternativt tastatur,Tastaturbytte,CTRL-tast,Caps Lock,Esperanto,circumflex,Drep X-tjener,LED-tastatur,Sammensett-tast +X-KDE-Keywords[nds]=Tastatuur,Tastwedderhalen,Tastluutstärk,Ingaavreedschappen, wedderhalen,Luutstärk,NumLock,TallenRast,Tallenblock,Tastatuurtyp,Tastatuurmodell,Tasttoornen,Layout,Spraak,Anner Tastatuur,Tastatuurwesseln,Ctrl,Strg,GrootschrRast, Grootschriev-Rast,Caps Lock,capslock,butenlannsch,Bookstaven,Akzent,Sünnertekens,X-Server afscheten +X-KDE-Keywords[nl]=Toetsenbord,Toetsenbordherhaling,Klikvolume,Invoerapparaten,herhaling,volume,NumLock,NumPad,Type toetsenbord,Model toetsenbord,Toetsenbordindeling,Toetsenindeling,Taal,Toetsenbord alterneren,Toetsenbord omschakelen,Ctrl-toets,Caps Lock,Esperanto,Circumflex,Kill X Server,LED-toetsenbord,Compositietoets +X-KDE-Keywords[pl]=Klawiatura,Powtórzenia klawiatury,Głośność kliknięcia,Urządzenia wejścia,powtórzenie,głośność +X-KDE-Keywords[pt]=Teclado,repetição do teclado,volume do 'click',dispositivos de entrada,repetição,volume,NumLock,NumPad,Tipo de teclado,Modelo de teclado,Disposição do teclado,Disposição das teclas,Língua,Teclado alternativo,Mudança de teclado,Ctrl,Caps Lock,Esperanto,Circunflexo,Matar o Servidor X,LED do Teclado,Tecla Compose +X-KDE-Keywords[pt_BR]=Teclado,repetição do teclado,volume do clique,dispositivos de entrada,repetição,volume,NumLock,NumPad,Tipo de teclado,Modelo de teclado,Leiaute do teclado,Leiaute das teclas,Idioma,Teclado alternativo,Mudança de teclado,Tecla Ctrl,Caps Lock,Esperanto,Circunflexo,Finalizar o servidor X,LED do teclado,Tecla Compose +X-KDE-Keywords[ru]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,клавиатура,повтор клавиши,громкость щелчка,громкость клика,устройство ввода,повтор,громкость,цифровая клавиатура,тип клавиатуры,модель клавиатуры,раскладка клавиатуры,расположение клавиш,язык,альтернативная клавиатура,переключение клавиатуры +X-KDE-Keywords[sk]=Klávesnica,opakovanie klávesnice,hlasitosť kliknutia,vstupné zariadenia,opakovanie, halsitosť,NumLock, NumPad,typ klávesnice,model klávesnice,rozloženie klávesnice,rozloženie kláves,jazyk, alternatívna klávesnica,prepínanie klávesnice,Ctrl,Caps Lock,Esperanto, Circumflex,zabiť X server,LED klávesnica,zložená klávesa +X-KDE-Keywords[sl]=tipkovnica,ponavljanje tipkovnice,tipkovno ponavljanje,glasnost klika,vhodne naprave,ponavljanje,glasnost,numlock,številčnica,vrsta tipkovnice,model tipkovnice,razporeditev tipk,razporeditev tipkovnice,jezik,nadomestna tipkovnica,alternativna tipkovnica,preklapljanje tipkovnice,ctrl,tipka ctrl,capslock,esperanto,cirkumfleks,končaj strežnik x,led,lučke,tipka za sestavljanje +X-KDE-Keywords[sr]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,тастатура,понављање тастатуре,јачина клика,улазни уређаји,понављање,јачина,NumLock,нумеричка тастатура,тип тастатуре,модел тастатуре,распоред тастатуре,распоред тастера,језик,алтернативна тастатура,мењач тастатуре,Ctrl,CapsLock,есперанто,циркумфлекс,ЛЕД тастатура,укини икс сервер +X-KDE-Keywords[sr@ijekavian]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,тастатура,понављање тастатуре,јачина клика,улазни уређаји,понављање,јачина,NumLock,нумеричка тастатура,тип тастатуре,модел тастатуре,распоред тастатуре,распоред тастера,језик,алтернативна тастатура,мењач тастатуре,Ctrl,CapsLock,есперанто,циркумфлекс,ЛЕД тастатура,укини икс сервер +X-KDE-Keywords[sr@ijekavianlatin]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,tastatura,ponavljanje tastature,jačina klika,ulazni uređaji,ponavljanje,jačina,NumLock,numerička tastatura,tip tastature,model tastature,raspored tastature,raspored tastera,jezik,alternativna tastatura,menjač tastature,Ctrl,CapsLock,esperanto,cirkumfleks,LED tastatura,ukini X server +X-KDE-Keywords[sr@latin]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,tastatura,ponavljanje tastature,jačina klika,ulazni uređaji,ponavljanje,jačina,NumLock,numerička tastatura,tip tastature,model tastature,raspored tastature,raspored tastera,jezik,alternativna tastatura,menjač tastature,Ctrl,CapsLock,esperanto,cirkumfleks,LED tastatura,ukini X server +X-KDE-Keywords[sv]=Tangentbord,Tangentbordsupprepning,Klickvolym,Indataenheter,upprepa,volym,NumLock,Numeriskt tangentbord,Tangentbordstyp,Tangentbordsmodell,Tangentbordslayout,Språk,Alternativt tangentbord,Tangentbordsbyte,Ctrl-tangent,Caps Lock,Esperanto,Circumflex,Döda X-server,LED-tangentbord,Compose-tangent +X-KDE-Keywords[tr]=Klavye,Klavye tekrarlaması,Tıklama sesi,Girdi Aygıtları,tekrarla,ses,NumLock,Sayısal Tuşlar,Klavye tipi,Klavye modeli,Klavye düzeni,Tuş düzeni,Dil,Klavyeyi Değiştir,Klavye Seçimi,Ctrl Tuşu,Caps Lock,Esperanto,İnceltme İşareti,X Sunucuyu Sonlandır,LED Klavye,Tuş Düzeni Oluştur +X-KDE-Keywords[uk]=клавіатура,повторення натискань,гучність клацання,пристрої введення,повторення,гучність,тип клавіатури,розкладка клавіатури,мова,інша клавіатура,інша розкладка,перемикання розкладок,клавіша Ctrl,есперанто,циркумфлекс,лампочки на клавіатурі,клавіша перемикання,Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key +X-KDE-Keywords[vi]=Bàn phím, lặp bàn phím, âm lượng nhấn nút,thiết bị nhập,lặp lại,âm lượng,NumLock,NumPad, kiểu bàn phím,mẫu bàn phím,loại bàn phím, bố trí phím,ngôn ngữ,bàn phím thay thế,chuyển đổi bàn phím,phím Ctrl,Caps Lock, Esperanto,Circumflex,tắt máy chủ X,bàn phím LED,phím tổ hợp +X-KDE-Keywords[x-test]=xxKeyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Keyxx +X-KDE-Keywords[zh_CN]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key,键盘,键盘重复,按键音量,输入设备,重复,音量,数字锁定,数字键盘,小键盘,键盘类型,键盘模型,键盘布局,按键布局,语言,额外键盘,键盘切换,Ctrl 键,大写锁定,杀死 X 服务器,LED 键盘灯,Compose 键,死键 +X-KDE-Keywords[zh_TW]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,NumLock,NumPad,Keyboard type,Keyboard model,Keyboard layout,Key layout,Language,Alternate Keyboard,Keyboard switching,Ctrl Key,Caps Lock,Esperanto,Circumflex,Kill X Server,LED Keyboard,Compose Key diff --git a/kcms/keyboard/kcm_keyboard.h b/kcms/keyboard/kcm_keyboard.h new file mode 100644 index 0000000000..c88fe3dd85 --- /dev/null +++ b/kcms/keyboard/kcm_keyboard.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KCM_KEYBOARD_H_ +#define KCM_KEYBOARD_H_ + +#include + +class KCMKeyboardWidget; +class KeyboardConfig; +class Rules; + +class KCMKeyboard: public KCModule +{ +Q_OBJECT + +public: + KCMKeyboard(QWidget *parent, const QVariantList &); + virtual ~KCMKeyboard(); + + void save(); + void load(); + void defaults(); + +private: + Rules* rules; + KeyboardConfig* keyboardConfig; + KCMKeyboardWidget *widget; +}; + +#endif /* KCM_KEYBOARD_H_ */ diff --git a/kcms/keyboard/kcm_keyboard.ui b/kcms/keyboard/kcm_keyboard.ui new file mode 100644 index 0000000000..57bb251baf --- /dev/null +++ b/kcms/keyboard/kcm_keyboard.ui @@ -0,0 +1,481 @@ + + + TabWidget + + + + 0 + 0 + 677 + 485 + + + + 1 + + + + Hardware + + + + 0 + + + + + + 1 + 0 + + + + Keyboard &model: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + keyboardModelComboBox + + + + + + + + 2 + 0 + + + + Here you can choose a keyboard model. This setting is independent of your keyboard layout and refers to the "hardware" model, i.e. the way your keyboard is manufactured. Modern keyboards that come with your computer usually have two extra keys and are referred to as "104-key" models, which is probably what you want if you do not know what kind of keyboard you have. + + + + 15 + + + QComboBox::AdjustToContentsOnFirstShow + + + + + + + + 1 + 1 + + + + + 0 + 100 + + + + + 0 + 0 + + + + + 0 + + + + + + + + + Layouts + + + + + + + + Layout Indicator + + + + + + Show layout indicator + + + + + + + Show for single layout + + + + + + + Show flag + + + + + + + Show label + + + + + + + Show label on flag + + + + + + + + + + If you select "Application" or "Window" switching policy, changing the keyboard layout will only affect the current application or window. + + + Switching Policy + + + + + + &Global + + + true + + + + + + + &Desktop + + + + + + + &Application + + + + + + + &Window + + + + + + + + + + Shortcuts for Switching Layout + + + + + + Main shortcuts: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + xkbGrpShortcutBtn + + + + + + + This is a shortcut for switching layouts which is handled by X.org. It allows modifier-only shortcuts. + + + None + + + + + + + ... + + + + + + + 3rd level shortcuts: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + xkb3rdLevelShortcutBtn + + + + + + + This is a shortcut for switching to a third level of the active layout (if it has one) which is handled by X.org. It allows modifier-only shortcuts. + + + None + + + + + + + ... + + + + + + + Alternative shortcut: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kdeKeySequence + + + + + + + This is a shortcut for switching layouts which is handled by KDE. It does not support modifier-only shortcuts and also may not work in some situations (e.g. if popup is active or from screensaver). + + + false + + + + + + + + + + + + true + + + Configure layouts + + + false + + + true + + + + + + + + Add + + + + + + + false + + + Remove + + + + + + + false + + + Move Up + + + + + + + false + + + Move Down + + + + + + + false + + + Preview + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + Spare layouts + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 20 + + + + + + + + + + + true + + + + + + Main layout count: + + + + + + + + + + Qt::Horizontal + + + + 431 + 20 + + + + + + + + + + + + + + + Advanced + + + + + + &Configure keyboard options + + + + + + + false + + + QAbstractItemView::NoSelection + + + true + + + + + + + + + KButtonGroup + QGroupBox +
    kbuttongroup.h
    + 1 +
    + + KKeySequenceWidget + QWidget +
    kkeysequencewidget.h
    +
    +
    + + + + changed(bool) + +
    diff --git a/kcms/keyboard/kcm_keyboard_widget.cpp b/kcms/keyboard/kcm_keyboard_widget.cpp new file mode 100644 index 0000000000..21685eb527 --- /dev/null +++ b/kcms/keyboard/kcm_keyboard_widget.cpp @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcm_keyboard_widget.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "keyboard_config.h" +#include "preview/keyboardpainter.h" +#include "xkb_rules.h" +#include "flags.h" +#include "x11_helper.h" +#include "kcm_view_models.h" +#include "kcm_add_layout_dialog.h" +#include "bindings.h" + +#include "kcmmisc.h" + +#include "ui_kcm_add_layout_dialog.h" + + +static const QString GROUP_SWITCH_GROUP_NAME("grp"); +static const QString LV3_SWITCH_GROUP_NAME("lv3"); +//static const QString RESET_XKB_OPTIONS("-option"); + +static const int TAB_HARDWARE = 0; +static const int TAB_LAYOUTS = 1; +static const int TAB_ADVANCED = 2; + +static const int MIN_LOOPING_COUNT = 2; + + +KCMKeyboardWidget::KCMKeyboardWidget(Rules* rules_, KeyboardConfig* keyboardConfig_, + const QVariantList &args, QWidget* /*parent*/): + rules(rules_), + actionCollection(NULL), + uiUpdating(false) +{ + flags = new Flags(); + keyboardConfig = keyboardConfig_; + + uiWidget = new Ui::TabWidget; + uiWidget->setupUi(this); + + kcmMiscWidget = new KCMiscKeyboardWidget(uiWidget->lowerHardwareWidget); + uiWidget->lowerHardwareWidget->layout()->addWidget( kcmMiscWidget ); + connect(kcmMiscWidget, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); + + if( rules != NULL ) { + initializeKeyboardModelUI(); + initializeXkbOptionsUI(); + initializeLayoutsUI(); + } + else { + uiWidget->tabLayouts->setEnabled(false); + uiWidget->tabAdvanced->setEnabled(false); + uiWidget->keyboardModelComboBox->setEnabled(false); + } + + handleParameters(args); +} + +KCMKeyboardWidget::~KCMKeyboardWidget() +{ + delete flags; +} + +void KCMKeyboardWidget::handleParameters(const QVariantList &args) +{ + // TODO: improve parameter handling + setCurrentIndex(TAB_HARDWARE); + foreach(const QVariant& arg, args) { + if( arg.type() == QVariant::String ) { + QString str = arg.toString(); + if( str == "--tab=layouts" ) { + setCurrentIndex(TAB_LAYOUTS); + } + else if( str == "--tab=advanced" ) { + setCurrentIndex(TAB_ADVANCED); + } + } + } +} + +void KCMKeyboardWidget::save() +{ + if( rules == NULL ) + return; + + if( actionCollection != NULL ) { + actionCollection->resetLayoutShortcuts(); + actionCollection->clear(); + delete actionCollection; + } + actionCollection = new KeyboardLayoutActionCollection(this, true); + actionCollection->setToggleShortcut(uiWidget->kdeKeySequence->keySequence()); + actionCollection->setLayoutShortcuts(keyboardConfig->layouts, rules); + + //TODO: skip if no change in shortcuts? + KGlobalSettings::emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_SHORTCUTS); +} + +void KCMKeyboardWidget::updateUI() +{ + if( rules == NULL ) + return; + + uiWidget->layoutsTableView->setModel(uiWidget->layoutsTableView->model()); + layoutsTableModel->refresh(); + uiWidget->layoutsTableView->resizeRowsToContents(); + + uiUpdating = true; + updateHardwareUI(); + updateXkbOptionsUI(); + updateSwitcingPolicyUI(); + updateLayoutsUI(); + updateShortcutsUI(); + uiUpdating = false; +} + +void KCMKeyboardWidget::uiChanged() +{ + if( rules == NULL ) + return; + + ((LayoutsTableModel*)uiWidget->layoutsTableView->model())->refresh(); +// this collapses the tree so use more fine-grained updates +// ((LayoutsTableModel*)uiWidget->xkbOptionsTreeView->model())->refresh(); + + if( uiUpdating ) + return; + + keyboardConfig->showIndicator = uiWidget->showIndicatorChk->isChecked(); + keyboardConfig->showSingle = uiWidget->showSingleChk->isChecked(); + + keyboardConfig->configureLayouts = uiWidget->layoutsGroupBox->isChecked(); + keyboardConfig->keyboardModel = uiWidget->keyboardModelComboBox->itemData(uiWidget->keyboardModelComboBox->currentIndex()).toString(); + + if( uiWidget->showFlagRadioBtn->isChecked() ) { + keyboardConfig->indicatorType = KeyboardConfig::SHOW_FLAG; + } + else + if( uiWidget->showLabelRadioBtn->isChecked() ) { + keyboardConfig->indicatorType = KeyboardConfig::SHOW_LABEL; + } + else { +// if( uiWidget->showFlagRadioBtn->isChecked() ) { + keyboardConfig->indicatorType = KeyboardConfig::SHOW_LABEL_ON_FLAG; + } + + keyboardConfig->resetOldXkbOptions = uiWidget->configureKeyboardOptionsChk->isChecked(); + + if( uiWidget->switchByDesktopRadioBtn->isChecked() ) { + keyboardConfig->switchingPolicy = KeyboardConfig::SWITCH_POLICY_DESKTOP; + } + else + if( uiWidget->switchByApplicationRadioBtn->isChecked() ) { + keyboardConfig->switchingPolicy = KeyboardConfig::SWITCH_POLICY_APPLICATION; + } + else + if( uiWidget->switchByWindowRadioBtn->isChecked() ) { + keyboardConfig->switchingPolicy = KeyboardConfig::SWITCH_POLICY_WINDOW; + } + else { + keyboardConfig->switchingPolicy = KeyboardConfig::SWITCH_POLICY_GLOBAL; + } + + updateXkbShortcutsButtons(); + + updateLoopCount(); + int loop = uiWidget->layoutLoopCountSpinBox->text().isEmpty() + ? KeyboardConfig::NO_LOOPING + : uiWidget->layoutLoopCountSpinBox->value(); + keyboardConfig->layoutLoopCount = loop; + + layoutsTableModel->refresh(); + + emit changed(true); +} + +void KCMKeyboardWidget::initializeKeyboardModelUI() +{ + foreach(ModelInfo* modelInfo, rules->modelInfos) { + QString vendor = modelInfo->vendor; + if( vendor.isEmpty() ) { + vendor = i18nc("unknown keyboard model vendor", "Unknown"); + } + uiWidget->keyboardModelComboBox->addItem(i18nc("vendor | keyboard model", "%1 | %2", vendor, modelInfo->description), modelInfo->name); + } + uiWidget->keyboardModelComboBox->model()->sort(0); + connect(uiWidget->keyboardModelComboBox, SIGNAL(activated(int)), this, SLOT(uiChanged())); +} + +void KCMKeyboardWidget::addLayout() +{ + if( keyboardConfig->layouts.count() >= X11Helper::ARTIFICIAL_GROUP_LIMIT_COUNT ) { // artificial limit now + QMessageBox msgBox; + msgBox.setText(i18np("Only up to %1 keyboard layout is supported", "Only up to %1 keyboard layouts are supported", X11Helper::ARTIFICIAL_GROUP_LIMIT_COUNT)); + // more information https://bugs.freedesktop.org/show_bug.cgi?id=19501 + msgBox.exec(); + return; + } + + AddLayoutDialog dialog(rules, keyboardConfig->isFlagShown() ? flags : NULL, keyboardConfig->isLabelShown(), this); + dialog.setModal(true); + if( dialog.exec() == QDialog::Accepted ) { + keyboardConfig->layouts.append( dialog.getSelectedLayoutUnit() ); + layoutsTableModel->refresh(); + uiWidget->layoutsTableView->resizeRowsToContents(); + uiChanged(); + } + + updateLoopCount(); +} + +static +inline int min(int x, int y) { return x < y ? x : y; } + +void KCMKeyboardWidget::updateLoopCount() +{ + int maxLoop = min(X11Helper::MAX_GROUP_COUNT, keyboardConfig->layouts.count() - 1); + uiWidget->layoutLoopCountSpinBox->setMaximum(maxLoop); + + bool layoutsConfigured = uiWidget->layoutsGroupBox->isChecked(); + + if( maxLoop < MIN_LOOPING_COUNT ) { + uiWidget->layoutLoopingCheckBox->setEnabled(false); + uiWidget->layoutLoopingCheckBox->setChecked(false); + } + else if( maxLoop >= X11Helper::MAX_GROUP_COUNT ) { + uiWidget->layoutLoopingCheckBox->setEnabled(false); + uiWidget->layoutLoopingCheckBox->setChecked(true); + } + else { + uiWidget->layoutLoopingCheckBox->setEnabled(layoutsConfigured); + } + + uiWidget->layoutLoopingGroupBox->setEnabled( + layoutsConfigured && uiWidget->layoutLoopingCheckBox->isChecked()); + + if( uiWidget->layoutLoopingCheckBox->isChecked() ) { + if( uiWidget->layoutLoopCountSpinBox->text().isEmpty() ) { + uiWidget->layoutLoopCountSpinBox->setValue(maxLoop); +// keyboardConfig->layoutLoopCount = maxLoop; + } + } + else { + uiWidget->layoutLoopCountSpinBox->clear(); +// keyboardConfig->layoutLoopCount = KeyboardConfig::NO_LOOPING; + } +} + +void KCMKeyboardWidget::initializeLayoutsUI() +{ + layoutsTableModel = new LayoutsTableModel(rules, flags, keyboardConfig, uiWidget->layoutsTableView); + uiWidget->layoutsTableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed); + uiWidget->layoutsTableView->setModel(layoutsTableModel); + uiWidget->layoutsTableView->setIconSize( flags->getTransparentPixmap().size() ); + + //TODO: do we need to delete this delegate or parent will take care of it? + VariantComboDelegate* variantDelegate = new VariantComboDelegate(keyboardConfig, rules, uiWidget->layoutsTableView); + uiWidget->layoutsTableView->setItemDelegateForColumn(LayoutsTableModel::VARIANT_COLUMN, variantDelegate); + + LabelEditDelegate* labelDelegate = new LabelEditDelegate(keyboardConfig, uiWidget->layoutsTableView); + uiWidget->layoutsTableView->setItemDelegateForColumn(LayoutsTableModel::DISPLAY_NAME_COLUMN, labelDelegate); + + KKeySequenceWidgetDelegate* shortcutDelegate = new KKeySequenceWidgetDelegate(keyboardConfig, uiWidget->layoutsTableView); + uiWidget->layoutsTableView->setItemDelegateForColumn(LayoutsTableModel::SHORTCUT_COLUMN, shortcutDelegate); + + //TODO: is it ok to hardcode sizes? any better approach? + uiWidget->layoutsTableView->setColumnWidth(LayoutsTableModel::MAP_COLUMN, 70); + uiWidget->layoutsTableView->setColumnWidth(LayoutsTableModel::LAYOUT_COLUMN, 200); + uiWidget->layoutsTableView->setColumnWidth(LayoutsTableModel::VARIANT_COLUMN, 200); + uiWidget->layoutsTableView->setColumnWidth(LayoutsTableModel::DISPLAY_NAME_COLUMN, 50); + uiWidget->layoutsTableView->setColumnWidth(LayoutsTableModel::SHORTCUT_COLUMN, 130); + + connect(layoutsTableModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(uiChanged())); + + uiWidget->layoutLoopCountSpinBox->setMinimum(MIN_LOOPING_COUNT); + +#ifdef DRAG_ENABLED + uiWidget->layoutsTableView->setDragEnabled(true); + uiWidget->layoutsTableView->setAcceptDrops(true); +#endif + + uiWidget->moveUpBtn->setIcon(QIcon::fromTheme("arrow-up")); + uiWidget->moveDownBtn->setIcon(QIcon::fromTheme("arrow-down")); + uiWidget->addLayoutBtn->setIcon(QIcon::fromTheme("list-add")); + uiWidget->removeLayoutBtn->setIcon(QIcon::fromTheme("list-remove")); + + QIcon clearIcon = qApp->isLeftToRight() ? QIcon::fromTheme("edit-clear-locationbar-rtl") : QIcon::fromTheme("edit-clear-locationbar-ltr"); + uiWidget->xkbGrpClearBtn->setIcon(clearIcon); + uiWidget->xkb3rdLevelClearBtn->setIcon(clearIcon); + + QIcon configIcon = QIcon::fromTheme("configure"); + uiWidget->xkbGrpShortcutBtn->setIcon(configIcon); + uiWidget->xkb3rdLevelShortcutBtn->setIcon(configIcon); + + uiWidget->kdeKeySequence->setModifierlessAllowed(false); + + connect(uiWidget->addLayoutBtn, SIGNAL(clicked(bool)), this, SLOT(addLayout())); + connect(uiWidget->removeLayoutBtn, SIGNAL(clicked(bool)), this, SLOT(removeLayout())); +// connect(uiWidget->layoutsTable, SIGNAL(itemSelectionChanged()), this, SLOT(layoutSelectionChanged())); + connect(uiWidget->layoutsTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(layoutSelectionChanged())); + +// connect(uiWidget->moveUpBtn, SIGNAL(triggered(QAction*)), this, SLOT(moveUp())); +// connect(uiWidget->moveDownBtn, SIGNAL(triggered(QAction*)), this, SLOT(moveDown())); + connect(uiWidget->moveUpBtn, SIGNAL(clicked(bool)), this, SLOT(moveUp())); + connect(uiWidget->moveDownBtn, SIGNAL(clicked(bool)), this, SLOT(moveDown())); + + connect(uiWidget->previewbutton,SIGNAL(clicked(bool)),this,SLOT(previewLayout())); + + connect(uiWidget->xkbGrpClearBtn, SIGNAL(clicked(bool)), this, SLOT(clearGroupShortcuts())); + connect(uiWidget->xkb3rdLevelClearBtn, SIGNAL(clicked(bool)), this, SLOT(clear3rdLevelShortcuts())); + +// connect(uiWidget->xkbGrpClearBtn, SIGNAL(triggered(QAction*)), this, SLOT(uiChanged())); +// connect(uiWidget->xkb3rdLevelClearBtn, SIGNAL(triggered(QAction*)), this, SLOT(uiChanged())); + connect(uiWidget->kdeKeySequence, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(uiChanged())); + connect(uiWidget->switchingPolicyButtonGroup, SIGNAL(clicked(int)), this, SLOT(uiChanged())); + + connect(uiWidget->xkbGrpShortcutBtn, SIGNAL(clicked(bool)), this, SLOT(scrollToGroupShortcut())); + connect(uiWidget->xkb3rdLevelShortcutBtn, SIGNAL(clicked(bool)), this, SLOT(scrollTo3rdLevelShortcut())); + + // connect(uiWidget->configureLayoutsChk, SIGNAL(toggled(bool)), uiWidget->layoutsGroupBox, SLOT(setEnabled(bool))); + connect(uiWidget->layoutsGroupBox, SIGNAL(toggled(bool)), this, SLOT(configureLayoutsChanged())); + + connect(uiWidget->showIndicatorChk, SIGNAL(clicked(bool)), this, SLOT(uiChanged())); + connect(uiWidget->showIndicatorChk, SIGNAL(toggled(bool)), uiWidget->showSingleChk, SLOT(setEnabled(bool))); + connect(uiWidget->showFlagRadioBtn, SIGNAL(clicked(bool)), this, SLOT(uiChanged())); + connect(uiWidget->showLabelRadioBtn, SIGNAL(clicked(bool)), this, SLOT(uiChanged())); + connect(uiWidget->showLabelOnFlagRadioBtn, SIGNAL(clicked(bool)), this, SLOT(uiChanged())); + connect(uiWidget->showSingleChk, SIGNAL(toggled(bool)), this, SLOT(uiChanged())); + + connect(uiWidget->layoutLoopingCheckBox, SIGNAL(clicked(bool)), this, SLOT(uiChanged())); + connect(uiWidget->layoutLoopCountSpinBox, SIGNAL(valueChanged(int)), this, SLOT(uiChanged())); +} + +void KCMKeyboardWidget::previewLayout(){ + QMessageBox q; + QModelIndex index = uiWidget->layoutsTableView->currentIndex() ; + QModelIndex idcountry = index.sibling(index.row(),0) ; + QString country=uiWidget->layoutsTableView->model()->data(idcountry).toString(); + QModelIndex idvariant = index.sibling(index.row(),2) ; + QString variant=uiWidget->layoutsTableView->model()->data(idvariant).toString(); + if(index.row()==-1 || index.column()==-1){ + q.setText(i18n("No layout selected ")); + q.exec(); + } + else{ + KeyboardPainter* layoutPreview = new KeyboardPainter(); + const LayoutInfo* layoutInfo = rules->getLayoutInfo(country); + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + if(variant==variantInfo->description){ + variant=variantInfo->name; + break; + } + } + layoutPreview->generateKeyboardLayout(country,variant); + layoutPreview->exec(); + layoutPreview->setModal(true); + } +} + +void KCMKeyboardWidget::configureLayoutsChanged() +{ + if( uiWidget->layoutsGroupBox->isChecked() && keyboardConfig->layouts.isEmpty() ) { + populateWithCurrentLayouts(); + } + uiChanged(); +} + +static QPair getSelectedRowRange(const QModelIndexList& selected) +{ + if( selected.isEmpty() ) { + return QPair(-1, -1); + } + + QList rows; + foreach(const QModelIndex& index, selected) { + rows << index.row(); + } + qSort(rows); + return QPair(rows[0], rows[rows.size()-1]); +} + +void KCMKeyboardWidget::layoutSelectionChanged() +{ + QModelIndexList selected = uiWidget->layoutsTableView->selectionModel()->selectedIndexes(); + uiWidget->removeLayoutBtn->setEnabled( ! selected.isEmpty() ); + QPair rowsRange( getSelectedRowRange(selected) ); + uiWidget->moveUpBtn->setEnabled( ! selected.isEmpty() && rowsRange.first > 0); + uiWidget->previewbutton->setEnabled(! selected.isEmpty()); + uiWidget->moveDownBtn->setEnabled( ! selected.isEmpty() && rowsRange.second < keyboardConfig->layouts.size()-1 ); +} + +void KCMKeyboardWidget::removeLayout() +{ + if( ! uiWidget->layoutsTableView->selectionModel()->hasSelection() ) + return; + + QModelIndexList selected = uiWidget->layoutsTableView->selectionModel()->selectedIndexes(); + QPair rowsRange( getSelectedRowRange(selected) ); + foreach(const QModelIndex& idx, selected) { + if( idx.column() == 0 ) { + keyboardConfig->layouts.removeAt(rowsRange.first); + } + } + layoutsTableModel->refresh(); + uiChanged(); + + if( keyboardConfig->layouts.size() > 0 ) { + int rowToSelect = rowsRange.first; + if( rowToSelect >= keyboardConfig->layouts.size() ) { + rowToSelect--; + } + + QModelIndex topLeft = layoutsTableModel->index(rowToSelect, 0, QModelIndex()); + QModelIndex bottomRight = layoutsTableModel->index(rowToSelect, layoutsTableModel->columnCount(topLeft)-1, QModelIndex()); + QItemSelection selection(topLeft, bottomRight); + uiWidget->layoutsTableView->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent); + uiWidget->layoutsTableView->setFocus(); + } + + layoutSelectionChanged(); + + updateLoopCount(); +} + +void KCMKeyboardWidget::moveUp() +{ + moveSelectedLayouts(-1); +} + +void KCMKeyboardWidget::moveDown() +{ + moveSelectedLayouts(1); +} + +void KCMKeyboardWidget::moveSelectedLayouts(int shift) +{ + QItemSelectionModel* selectionModel = uiWidget->layoutsTableView->selectionModel(); + if( selectionModel == NULL || !selectionModel->hasSelection() ) + return; + + QModelIndexList selected = selectionModel->selectedRows(); + if( selected.count() < 1 ) + return; + + int newFirstRow = selected[0].row() + shift; + int newLastRow = selected[ selected.size()-1 ].row() + shift; + + if( newFirstRow >= 0 && newLastRow <= keyboardConfig->layouts.size() - 1 ) { + QList selectionRows; + foreach(const QModelIndex& index, selected) { + int newRowIndex = index.row() + shift; + keyboardConfig->layouts.move(index.row(), newRowIndex); + selectionRows << newRowIndex; + } + uiChanged(); + + QItemSelection selection; + foreach(int row, selectionRows) { + QModelIndex topLeft = layoutsTableModel->index(row, 0, QModelIndex()); + QModelIndex bottomRight = layoutsTableModel->index(row, layoutsTableModel->columnCount(topLeft)-1, QModelIndex()); + selection << QItemSelectionRange(topLeft, bottomRight); + } + uiWidget->layoutsTableView->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent); + uiWidget->layoutsTableView->setFocus(); + } +} + +void KCMKeyboardWidget::scrollToGroupShortcut() +{ + this->setCurrentIndex(TAB_ADVANCED); + if( ! uiWidget->configureKeyboardOptionsChk->isChecked() ) { + uiWidget->configureKeyboardOptionsChk->setChecked(true); + } + ((XkbOptionsTreeModel*)uiWidget->xkbOptionsTreeView->model())->gotoGroup(GROUP_SWITCH_GROUP_NAME, uiWidget->xkbOptionsTreeView); +} + +void KCMKeyboardWidget::scrollTo3rdLevelShortcut() +{ + this->setCurrentIndex(TAB_ADVANCED); + if( ! uiWidget->configureKeyboardOptionsChk->isChecked() ) { + uiWidget->configureKeyboardOptionsChk->setChecked(true); + } + ((XkbOptionsTreeModel*)uiWidget->xkbOptionsTreeView->model())->gotoGroup(LV3_SWITCH_GROUP_NAME, uiWidget->xkbOptionsTreeView); +} + +void KCMKeyboardWidget::clearGroupShortcuts() +{ + clearXkbGroup(GROUP_SWITCH_GROUP_NAME); +} + +void KCMKeyboardWidget::clear3rdLevelShortcuts() +{ + clearXkbGroup(LV3_SWITCH_GROUP_NAME); +} + +void KCMKeyboardWidget::clearXkbGroup(const QString& groupName) +{ + for(int ii=keyboardConfig->xkbOptions.count()-1; ii>=0; ii--) { + if( keyboardConfig->xkbOptions[ii].startsWith(groupName + Rules::XKB_OPTION_GROUP_SEPARATOR) ) { + keyboardConfig->xkbOptions.removeAt(ii); + } + } + ((XkbOptionsTreeModel*)uiWidget->xkbOptionsTreeView->model())->reset(); + uiWidget->xkbOptionsTreeView->update(); + updateXkbShortcutsButtons(); + emit changed(true); +} + +static +bool xkbOptionGroupLessThan(const OptionGroupInfo* og1, const OptionGroupInfo* og2) +{ + return og1->description.toLower() < og2->description.toLower(); +} +static +bool xkbOptionLessThan(const OptionInfo* o1, const OptionInfo* o2) +{ + return o1->description.toLower() < o2->description.toLower(); +} + +void KCMKeyboardWidget::initializeXkbOptionsUI() +{ + qSort(rules->optionGroupInfos.begin(), rules->optionGroupInfos.end(), xkbOptionGroupLessThan); + foreach(OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { + qSort(optionGroupInfo->optionInfos.begin(), optionGroupInfo->optionInfos.end(), xkbOptionLessThan); + } + + XkbOptionsTreeModel* model = new XkbOptionsTreeModel(rules, keyboardConfig, uiWidget->xkbOptionsTreeView); + uiWidget->xkbOptionsTreeView->setModel(model); + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(uiChanged())); + + connect(uiWidget->configureKeyboardOptionsChk, SIGNAL(toggled(bool)), this, SLOT(configureXkbOptionsChanged())); + // connect(uiWidget->configureKeyboardOptionsChk, SIGNAL(toggled(bool)), this, SLOT(uiChanged())); + connect(uiWidget->configureKeyboardOptionsChk, SIGNAL(toggled(bool)), uiWidget->xkbOptionsTreeView, SLOT(setEnabled(bool))); +} + +void KCMKeyboardWidget::configureXkbOptionsChanged() +{ + if( uiWidget->configureKeyboardOptionsChk->isChecked() && keyboardConfig->xkbOptions.isEmpty() ) { + populateWithCurrentXkbOptions(); + } + ((LayoutsTableModel*)uiWidget->xkbOptionsTreeView->model())->refresh(); + uiChanged(); +} + +void KCMKeyboardWidget::updateSwitcingPolicyUI() +{ + switch (keyboardConfig->switchingPolicy){ + case KeyboardConfig::SWITCH_POLICY_DESKTOP: + uiWidget->switchByDesktopRadioBtn->setChecked(true); + break; + case KeyboardConfig::SWITCH_POLICY_APPLICATION: + uiWidget->switchByApplicationRadioBtn->setChecked(true); + break; + case KeyboardConfig::SWITCH_POLICY_WINDOW: + uiWidget->switchByWindowRadioBtn->setChecked(true); + break; + default: + case KeyboardConfig::SWITCH_POLICY_GLOBAL: + uiWidget->switchByGlobalRadioBtn->setChecked(true); + } +} + +void KCMKeyboardWidget::updateXkbShortcutButton(const QString& groupName, QPushButton* button) +{ + QStringList grpOptions; + if( keyboardConfig->resetOldXkbOptions ) { + QRegExp regexp = QRegExp("^" + groupName + Rules::XKB_OPTION_GROUP_SEPARATOR); + grpOptions = keyboardConfig->xkbOptions.filter(regexp); + } + switch( grpOptions.size() ) { + case 0: + button->setText(i18nc("no shortcuts defined", "None")); + break; + case 1: { + const QString& option = grpOptions.first(); + const OptionGroupInfo* optionGroupInfo = rules->getOptionGroupInfo(groupName); + const OptionInfo* optionInfo = optionGroupInfo->getOptionInfo(option); + if( optionInfo == NULL || optionInfo->description == NULL ) { + kError() << "Could not find option info for " << option; + button->setText(grpOptions.first()); + } + else { + button->setText(optionInfo->description); + } + } + break; + default: + button->setText(i18np("%1 shortcut", "%1 shortcuts", grpOptions.size())); + } +} + +void KCMKeyboardWidget::updateXkbShortcutsButtons() +{ + updateXkbShortcutButton(GROUP_SWITCH_GROUP_NAME, uiWidget->xkbGrpShortcutBtn); + updateXkbShortcutButton(LV3_SWITCH_GROUP_NAME, uiWidget->xkb3rdLevelShortcutBtn); +} + +void KCMKeyboardWidget::updateShortcutsUI() +{ + updateXkbShortcutsButtons(); + + delete actionCollection; + actionCollection = new KeyboardLayoutActionCollection(this, true); + QAction* toggleAction = actionCollection->getToggeAction(); + const auto shortcuts = KGlobalAccel::self()->shortcut(toggleAction); + uiWidget->kdeKeySequence->setKeySequence(shortcuts.isEmpty() ? QKeySequence() : shortcuts.first()); + actionCollection->loadLayoutShortcuts(keyboardConfig->layouts, rules); + layoutsTableModel->refresh(); +} + +void KCMKeyboardWidget::updateXkbOptionsUI() +{ + uiWidget->configureKeyboardOptionsChk->setChecked(keyboardConfig->resetOldXkbOptions); +} + +void KCMKeyboardWidget::updateLayoutsUI() { + uiWidget->layoutsGroupBox->setChecked(keyboardConfig->configureLayouts); + uiWidget->showIndicatorChk->setChecked(keyboardConfig->showIndicator); + uiWidget->showSingleChk->setChecked(keyboardConfig->showSingle); + uiWidget->showFlagRadioBtn->setChecked(keyboardConfig->indicatorType == KeyboardConfig::SHOW_FLAG); + uiWidget->showLabelRadioBtn->setChecked(keyboardConfig->indicatorType == KeyboardConfig::SHOW_LABEL); + uiWidget->showLabelOnFlagRadioBtn->setChecked(keyboardConfig->indicatorType == KeyboardConfig::SHOW_LABEL_ON_FLAG); + + bool loopingOn = keyboardConfig->configureLayouts && keyboardConfig->layoutLoopCount + != KeyboardConfig::NO_LOOPING; + uiWidget->layoutLoopingCheckBox->setChecked(loopingOn); + uiWidget->layoutLoopingGroupBox->setEnabled(loopingOn); + if( loopingOn ) { + uiWidget->layoutLoopCountSpinBox->setValue(keyboardConfig->layoutLoopCount); + } + else { + uiWidget->layoutLoopCountSpinBox->clear(); + } +} + +void KCMKeyboardWidget::updateHardwareUI() +{ + int idx = uiWidget->keyboardModelComboBox->findData(keyboardConfig->keyboardModel); + if( idx != -1 ) { + uiWidget->keyboardModelComboBox->setCurrentIndex(idx); + } +} + +void KCMKeyboardWidget::populateWithCurrentLayouts() +{ + QList layouts = X11Helper::getLayoutsList(); + foreach(LayoutUnit layoutUnit, layouts) { + keyboardConfig->layouts.append(layoutUnit); + } +} + +void KCMKeyboardWidget::populateWithCurrentXkbOptions() +{ + XkbConfig xkbConfig; + if( X11Helper::getGroupNames(QX11Info::display(), &xkbConfig, X11Helper::ALL) ) { + foreach(QString xkbOption, xkbConfig.options) { + keyboardConfig->xkbOptions.append(xkbOption); + } + } +} diff --git a/kcms/keyboard/kcm_keyboard_widget.h b/kcms/keyboard/kcm_keyboard_widget.h new file mode 100644 index 0000000000..657ddda3bc --- /dev/null +++ b/kcms/keyboard/kcm_keyboard_widget.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KCM_KEYBOARD_WIDGET_H_ +#define KCM_KEYBOARD_WIDGET_H_ + +#include "ui_kcm_keyboard.h" + +#include +#include + +class QWidget; +class KeyboardConfig; +class Rules; +class Flags; +class KComponentData; +class QString; +class QPushButton; +class LayoutsTableModel; +class KCMiscKeyboardWidget; +class KeyboardLayoutActionCollection; + +class KCMKeyboardWidget: public QTabWidget +{ + Q_OBJECT + +public: + KCMKeyboardWidget(Rules* rules, KeyboardConfig* keyboardConfig, + const QVariantList &args, QWidget* parent=0); + virtual ~KCMKeyboardWidget(); + + void updateUI(); + void save(); + + //temp hack + KCMiscKeyboardWidget* getKcmMiscWidget() const { return kcmMiscWidget; } + +Q_SIGNALS: + void changed(bool state); + +private Q_SLOTS: + void addLayout(); + void removeLayout(); + void layoutSelectionChanged(); + void uiChanged(); + void scrollToGroupShortcut(); + void scrollTo3rdLevelShortcut(); + void clearGroupShortcuts(); + void clear3rdLevelShortcuts(); + void updateXkbShortcutsButtons(); + void moveUp(); + void moveDown(); + void configureLayoutsChanged(); + void configureXkbOptionsChanged(); + void previewLayout(); + +private: + Rules *rules; + Flags *flags; + Ui::TabWidget *uiWidget; + KeyboardConfig *keyboardConfig; + KeyboardLayoutActionCollection* actionCollection; + LayoutsTableModel* layoutsTableModel; + KCMiscKeyboardWidget* kcmMiscWidget; + bool uiUpdating; + + void initializeLayoutsUI(); + void initializeXkbOptionsUI(); + void initializeKeyboardModelUI(); + void updateHardwareUI(); + void updateLayoutsUI(); + void updateShortcutsUI(); + void updateXkbOptionsUI(); + void updateSwitcingPolicyUI(); + void updateXkbShortcutButton(const QString& groupName, QPushButton* button); + void clearXkbGroup(const QString& groupName); + void moveSelectedLayouts(int shift); + void populateWithCurrentLayouts(); + void populateWithCurrentXkbOptions(); + void updateLoopCount(); + void handleParameters(const QVariantList &args); +}; + + +#endif /* KCM_KEYBOARD_WIDGET_H_ */ diff --git a/kcms/keyboard/kcm_view_models.cpp b/kcms/keyboard/kcm_view_models.cpp new file mode 100644 index 0000000000..093bca9c4f --- /dev/null +++ b/kcms/keyboard/kcm_view_models.cpp @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcm_view_models.h" + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef DRAG_ENABLED +#include +#endif + +#include "keyboard_config.h" +#include "xkb_rules.h" +#include "flags.h" +#include "x11_helper.h" +#include "bindings.h" + +const int LayoutsTableModel::MAP_COLUMN = 0; +const int LayoutsTableModel::LAYOUT_COLUMN = 1; +const int LayoutsTableModel::VARIANT_COLUMN = 2; +const int LayoutsTableModel::DISPLAY_NAME_COLUMN = 3; +const int LayoutsTableModel::SHORTCUT_COLUMN = 4; +static const int COLUMN_COUNT = 5; + +LayoutsTableModel::LayoutsTableModel(Rules* rules_, Flags *flags_, KeyboardConfig* keyboardConfig_, QObject* parent): + QAbstractTableModel(parent), + keyboardConfig(keyboardConfig_), + rules(rules_), + countryFlags(flags_) +{ +} + +void LayoutsTableModel::refresh() +{ + beginResetModel(); + endResetModel(); +} + +int LayoutsTableModel::rowCount(const QModelIndex &/*parent*/) const +{ + return keyboardConfig->layouts.count(); +} + +int LayoutsTableModel::columnCount(const QModelIndex&) const +{ + return COLUMN_COUNT; +} + +Qt::ItemFlags LayoutsTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + if( index.column() == DISPLAY_NAME_COLUMN + || index.column() == VARIANT_COLUMN + || index.column() == SHORTCUT_COLUMN ) { + flags |= Qt::ItemIsEditable; + } + +#ifdef DRAG_ENABLED + flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +#endif + + return flags; +} + +#ifdef DRAG_ENABLED +QStringList LayoutsTableModel::mimeTypes() const +{ + QStringList types; + types << "application/keyboard-layout-item"; + return types; +} + +QMimeData *LayoutsTableModel::mimeData(const QModelIndexList &indexes) const + { + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + QSet rows; + foreach (const QModelIndex& index, indexes) { + if (index.isValid()) { + rows << index.row(); + } + } + foreach (int row, rows) { + stream << row; + } + + mimeData->setData("application/keyboard-layout-item", encodedData); + return mimeData; +} +#endif + +QVariant LayoutsTableModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= keyboardConfig->layouts.size()) + return QVariant(); + + const LayoutUnit& layoutUnit = keyboardConfig->layouts.at(index.row()); + + if (role == Qt::DecorationRole) { + switch( index.column() ) { + case DISPLAY_NAME_COLUMN: { +// if( keyboardConfig->isFlagShown() ) { + QIcon icon = countryFlags->getIconWithText(layoutUnit, *keyboardConfig); + return icon.isNull() ? countryFlags->getTransparentPixmap() : icon; +// } + } +//TODO: show the cells are editable +// case VARIANT_COLUMN: { +// case DISPLAY_NAME_COLUMN: { +// int sz = 5; +// QPixmap pm = QPixmap(sz, sz+5); +// pm.fill(Qt::transparent); +// QPainter p(&pm); +// QPoint points[] = { QPoint(0, 0), QPoint(0, sz), QPoint(sz, 0) }; +// p.drawPolygon(points, 3); +// return pm; +// } + break; + } + } + else + if( role == Qt::BackgroundRole ) { + if( keyboardConfig->layoutLoopCount != KeyboardConfig::NO_LOOPING + && index.row() >= keyboardConfig->layoutLoopCount ) { + return QBrush(Qt::lightGray); + } + } + else + if (role == Qt::DisplayRole) { + switch( index.column() ) { + case MAP_COLUMN: + return layoutUnit.layout; + break; + case LAYOUT_COLUMN: { + const LayoutInfo* layoutInfo = rules->getLayoutInfo(layoutUnit.layout); + return layoutInfo != NULL ? layoutInfo->description : layoutUnit.layout; + } + case VARIANT_COLUMN: { + if( layoutUnit.variant.isEmpty() ) + return QVariant(); + const LayoutInfo* layoutInfo = rules->getLayoutInfo(layoutUnit.layout); + if( layoutInfo == NULL ) + return QVariant(); + const VariantInfo* variantInfo = layoutInfo->getVariantInfo(layoutUnit.variant); + return variantInfo != NULL ? variantInfo->description : layoutUnit.variant; + } + break; + case DISPLAY_NAME_COLUMN: +// if( keyboardConfig->indicatorType == KeyboardConfig::SHOW_LABEL ) { +// return layoutUnit.getDisplayName(); +// } + break; + case SHORTCUT_COLUMN: { + return layoutUnit.getShortcut().toString(); + } + break; + } + } + else if (role==Qt::EditRole ) { + switch( index.column() ) { + case DISPLAY_NAME_COLUMN: + return layoutUnit.getDisplayName(); + break; + case VARIANT_COLUMN: + return layoutUnit.variant; + break; + case SHORTCUT_COLUMN: + return layoutUnit.getShortcut().toString(); + break; + default:; + } + } + else if( role == Qt::TextAlignmentRole ) { + switch( index.column() ) { + case MAP_COLUMN: + case DISPLAY_NAME_COLUMN: + case SHORTCUT_COLUMN: + return Qt::AlignCenter; + break; + default:; + } + } + return QVariant(); +} + +QVariant LayoutsTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + const QString headers[] = {i18nc("layout map name", "Map"), i18n("Layout"), i18n("Variant"), i18n("Label"), i18n("Shortcut")}; + return headers[section]; + } + + return QVariant(); +} + +bool LayoutsTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::EditRole + || (index.column() != DISPLAY_NAME_COLUMN && index.column() != VARIANT_COLUMN && index.column() != SHORTCUT_COLUMN) ) + return false; + + if (index.row() >= keyboardConfig->layouts.size()) + return false; + + LayoutUnit& layoutUnit = keyboardConfig->layouts[index.row()]; + + switch( index.column() ) { + case DISPLAY_NAME_COLUMN: { + QString displayText = value.toString().left(3); + layoutUnit.setDisplayName(displayText); + countryFlags->clearCache(); // regenerate the label + } + break; + case VARIANT_COLUMN: { + QString variant = value.toString(); + layoutUnit.variant = variant; + } + break; + case SHORTCUT_COLUMN: { + QString shortcut = value.toString(); + layoutUnit.setShortcut(QKeySequence(shortcut)); + } + break; + } + emit dataChanged(index, index); + + return true; +} + +// +// LabelEditDelegate +// +LabelEditDelegate::LabelEditDelegate(const KeyboardConfig* keyboardConfig_, QObject *parent): + QStyledItemDelegate(parent), + keyboardConfig(keyboardConfig_) +{} + +QWidget *LabelEditDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option , + const QModelIndex & index ) const +{ + if( keyboardConfig->indicatorType == KeyboardConfig::SHOW_FLAG ) + return NULL; + + QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); + QLineEdit* lineEdit = static_cast(widget); + if( lineEdit != NULL ) { + lineEdit->setMaxLength(LayoutUnit::MAX_LABEL_LENGTH); + } + return widget; +} + +//void LabelEditDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const +//{ +// QStyleOptionViewItem option2(option); +//// option2.decorationPosition = QStyleOptionViewItem::Right; +// option2.decorationAlignment = Qt::AlignHCenter | Qt::AlignVCenter; +// QStyledItemDelegate::paint(painter, option2, index); +//} + + +// +// VariantComboDelegate +// +//TODO: reuse this function in kcm_add_layout_dialog.cpp +static void populateComboWithVariants(QComboBox* combo, const QString& layout, const Rules* rules) +{ + combo->clear(); + const LayoutInfo* layoutInfo = rules->getLayoutInfo(layout); + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + combo->addItem(variantInfo->description, variantInfo->name); + } + combo->model()->sort(0); + combo->insertItem(0, i18nc("variant", "Default"), ""); + combo->setCurrentIndex(0); +} + +VariantComboDelegate::VariantComboDelegate(const KeyboardConfig* keyboardConfig_, const Rules* rules_, QObject *parent): + QStyledItemDelegate(parent), + keyboardConfig(keyboardConfig_), + rules(rules_) +{} + +QWidget *VariantComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, + const QModelIndex & index ) const +{ + QComboBox *editor = new QComboBox(parent); + const LayoutUnit& layoutUnit = keyboardConfig->layouts[index.row()]; + populateComboWithVariants(editor, layoutUnit.layout, rules); + return editor; +} + +void VariantComboDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QComboBox *combo = static_cast(editor); + QString variant = index.model()->data(index, Qt::EditRole).toString(); + int itemIndex = combo->findData(variant); + if( itemIndex == -1 ) { + itemIndex = 0; + } + combo->setCurrentIndex(itemIndex); +} + +void VariantComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + QComboBox *combo = static_cast(editor); + QString variant = combo->itemData(combo->currentIndex()).toString(); + model->setData(index, variant, Qt::EditRole); +} + +void VariantComboDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +// +// KKeySequenceWidgetDelegate +// +KKeySequenceWidgetDelegate::KKeySequenceWidgetDelegate(const KeyboardConfig* keyboardConfig_, QObject *parent): + QStyledItemDelegate(parent), + keyboardConfig(keyboardConfig_) +{} + +QWidget *KKeySequenceWidgetDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, + const QModelIndex & index ) const +{ + itemsBeingEdited.insert(index); + + KKeySequenceWidget *editor = new KKeySequenceWidget(parent); + editor->setFocusPolicy(Qt::StrongFocus); + editor->setModifierlessAllowed(false); + + const LayoutUnit& layoutUnit = keyboardConfig->layouts[index.row()]; + editor->setKeySequence(layoutUnit.getShortcut()); + + editor->captureKeySequence(); + + return editor; +} + +//void KKeySequenceWidgetDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +//{ +// KKeySequenceWidget *kkeysequencewidget = static_cast(editor); +// QString shortcut = index.model()->data(index, Qt::EditRole).toString(); +// kkeysequencewidget->setKeySequence(QKeySequence(shortcut)); +// kkeysequencewidget->captureKeySequence(); +//// kDebug() << "set editor data"; +//} + +void KKeySequenceWidgetDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + KKeySequenceWidget *kkeysequencewidget = static_cast(editor); + QString shortcut = kkeysequencewidget->keySequence().toString(); + model->setData(index, shortcut, Qt::EditRole); + itemsBeingEdited.remove(index); +} + +void KKeySequenceWidgetDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (itemsBeingEdited.contains(index)) { +// StyledBackgroundPainter::drawBackground(painter,option,index); + } + else { + QStyledItemDelegate::paint(painter,option,index); + } +} +// +// Xkb Options Tree View +// + +int XkbOptionsTreeModel::rowCount(const QModelIndex& parent) const { + if( ! parent.isValid() ) + return rules->optionGroupInfos.count(); + if( ! parent.parent().isValid() ) + return rules->optionGroupInfos[parent.row()]->optionInfos.count(); + return 0; +} + +QVariant XkbOptionsTreeModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + + if (role == Qt::DisplayRole) { + if( ! index.parent().isValid() ) { + return rules->optionGroupInfos[row]->description; + } + else { + int groupRow = index.parent().row(); + const OptionGroupInfo* xkbGroup = rules->optionGroupInfos[groupRow]; + return xkbGroup->optionInfos[row]->description; + } + } + else if (role==Qt::CheckStateRole ) { + if( index.parent().isValid() ) { + int groupRow = index.parent().row(); + const OptionGroupInfo* xkbGroup = rules->optionGroupInfos[groupRow]; + const QString& xkbOptionName = xkbGroup->optionInfos[row]->name; + return keyboardConfig->xkbOptions.indexOf(xkbOptionName) == -1 + ? Qt::Unchecked : Qt::Checked; + } + else { + int groupRow = index.row(); + const OptionGroupInfo* xkbGroup = rules->optionGroupInfos[groupRow]; + foreach(const OptionInfo* optionInfo, xkbGroup->optionInfos) { + if( keyboardConfig->xkbOptions.indexOf(optionInfo->name) != -1 ) + return Qt::PartiallyChecked; + } + return Qt::Unchecked; + } + } + return QVariant(); +} + +bool XkbOptionsTreeModel::setData(const QModelIndex & index, const QVariant & value, int role) { + int groupRow = index.parent().row(); + if( groupRow < 0 ) return false; + + const OptionGroupInfo* xkbGroup = rules->optionGroupInfos[groupRow]; + const OptionInfo* option = xkbGroup->optionInfos[index.row()]; + + if( value.toInt() == Qt::Checked ) { + if( xkbGroup->exclusive ) { + // clear if exclusive (TODO: radiobutton) + int idx = keyboardConfig->xkbOptions.indexOf(QRegExp(xkbGroup->name + ".*")); + if( idx >= 0 ) { + for(int i=0; ioptionInfos.count(); i++) + if( xkbGroup->optionInfos[i]->name == keyboardConfig->xkbOptions[idx] ) { + setData(createIndex(i, index.column(), (quint32)index.internalId()-index.row()+i), Qt::Unchecked, role); + break; + } + // m_kxkbConfig->m_options.removeAt(idx); + // idx = m_kxkbConfig->m_options.indexOf(QRegExp(xkbGroupNm+".*")); + } + } + if( keyboardConfig->xkbOptions.indexOf(option->name) < 0 ) { + keyboardConfig->xkbOptions.append(option->name); + } + } + else { + keyboardConfig->xkbOptions.removeAll(option->name); + } + + emit dataChanged(index, index); + emit dataChanged(index.parent(), index.parent()); + return true; +} + +void XkbOptionsTreeModel::gotoGroup(const QString& groupName, QTreeView* view) { + const OptionGroupInfo* optionGroupInfo = rules->getOptionGroupInfo(groupName); + int index = rules->optionGroupInfos.indexOf((OptionGroupInfo*)optionGroupInfo); + if( index != -1 ) { + QModelIndex modelIdx = createIndex(index,0); +// view->selectionModel()->setCurrentIndex(createIndex(index,0), QItemSelectionModel::NoUpdate); + view->setExpanded(modelIdx, true); + view->scrollTo(modelIdx, QAbstractItemView::PositionAtTop); + view->selectionModel()->setCurrentIndex(modelIdx, QItemSelectionModel::Current); + view->setFocus(Qt::OtherFocusReason); + } +// else { +// kDebug() << "can't scroll to group" << group; +// } +} + diff --git a/kcms/keyboard/kcm_view_models.h b/kcms/keyboard/kcm_view_models.h new file mode 100644 index 0000000000..285f90dce5 --- /dev/null +++ b/kcms/keyboard/kcm_view_models.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KCM_VIEW_MODELS_H_ +#define KCM_VIEW_MODELS_H_ + +#include +#include +#include +#include + +class QTreeView; +class KeyboardConfig; +class Rules; +class Flags; +class KeyboardLayoutActionCollection; + +class LayoutsTableModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + LayoutsTableModel(Rules* rules, Flags *flags, KeyboardConfig* keyboardConfig, QObject *parent = 0); + + int columnCount(const QModelIndex&) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); +#ifdef DRAG_ENABLED + Qt::DropActions supportedDropActions() const { + return Qt::MoveAction; + } + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; +#endif + void refresh(); + + static const int MAP_COLUMN; + static const int LAYOUT_COLUMN; + static const int VARIANT_COLUMN; + static const int DISPLAY_NAME_COLUMN; + static const int SHORTCUT_COLUMN; + + private: + KeyboardConfig* keyboardConfig; + const Rules *rules; + Flags *countryFlags; +}; + +class LabelEditDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit LabelEditDelegate(const KeyboardConfig* keyboardConfig, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +// void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + const KeyboardConfig* keyboardConfig; +}; + +class VariantComboDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + VariantComboDelegate(const KeyboardConfig* keyboardConfig, const Rules* rules, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + const KeyboardConfig* keyboardConfig; + const Rules* rules; +}; + +class KKeySequenceWidgetDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + KKeySequenceWidgetDelegate(const KeyboardConfig* keyboardConfig_, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +// void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + +private: + const KeyboardConfig* keyboardConfig; + mutable QSet itemsBeingEdited; +}; + + +class XkbOptionsTreeModel: public QAbstractItemModel +{ +public: + XkbOptionsTreeModel(Rules* rules_, KeyboardConfig* keyboardConfig_, QObject *parent) + : QAbstractItemModel(parent), + keyboardConfig(keyboardConfig_), + rules(rules_) { } + + int columnCount(const QModelIndex& /*parent*/) const { return 1; } + int rowCount(const QModelIndex& parent) const; + QModelIndex parent(const QModelIndex& index) const { + if (!index.isValid() ) + return QModelIndex(); + if( index.internalId() < 100 ) + return QModelIndex(); + return createIndex(((index.internalId() - index.row())/100) - 1, index.column()); + } + QModelIndex index(int row, int column, const QModelIndex& parent) const { + if(!parent.isValid()) return createIndex(row, column); + return createIndex(row, column, (100 * (parent.row()+1)) + row); + } + Qt::ItemFlags flags ( const QModelIndex & index ) const { + if( ! index.isValid() ) + return 0; + + if( !index.parent().isValid() ) + return Qt::ItemIsEnabled; + + return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + } + + bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ); + QVariant data(const QModelIndex& index, int role) const; + void reset() { QAbstractItemModel::reset(); } + void gotoGroup(const QString& group, QTreeView* view); + +private: + KeyboardConfig* keyboardConfig; + Rules *rules; +}; + +#endif /* KCM_VIEW_MODELS_H_ */ diff --git a/kcms/keyboard/kcmmisc.cpp b/kcms/keyboard/kcmmisc.cpp new file mode 100644 index 0000000000..6f787ea372 --- /dev/null +++ b/kcms/keyboard/kcmmisc.cpp @@ -0,0 +1,238 @@ +/* + * kcmmisc.cpp + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Layout management, cleanups: + * Copyright (c) 1999 Dirk A. Mueller + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcmmisc.h" +#include "ui_kcmmiscwidget.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + + +KCMiscKeyboardWidget::KCMiscKeyboardWidget(QWidget *parent) + : QWidget(parent), + ui(*new Ui_KeyboardConfigWidget) +{ + ui.setupUi(this); + + ui.delay->setRange(100, 5000, 50); + ui.delay->setSliderEnabled(false); + ui.rate->setRange(0.2, 50, 5, false); + + sliderMax = (int)floor (0.5 + 2*(log(5000.0L)-log(100.0L)) / (log(5000.0L)-log(4999.0L))); + ui.delaySlider->setRange(0, sliderMax); + ui.delaySlider->setSingleStep(sliderMax/100); + ui.delaySlider->setPageStep(sliderMax/10); + ui.delaySlider->setTickInterval(sliderMax/10); + + ui.rateSlider->setRange(20, 5000); + ui.rateSlider->setSingleStep(30); + ui.rateSlider->setPageStep(500); + ui.rateSlider->setTickInterval(498); + + connect(ui.keyboardRepeatButtonGroup, SIGNAL(clicked()), this, SLOT(changed())); + connect(ui.keyboardRepeatButtonGroup, SIGNAL(changed(int)), this, SLOT(keyboardRepeatStateChanged(int))); + connect(ui.delay, SIGNAL(valueChanged(int)), this, SLOT(delaySpinboxChanged(int))); + connect(ui.delaySlider, SIGNAL(valueChanged(int)), this, SLOT(delaySliderChanged(int))); + connect(ui.rate, SIGNAL(valueChanged(double)), this, SLOT(rateSpinboxChanged(double))); + connect(ui.rateSlider, SIGNAL(valueChanged(int)), this, SLOT(rateSliderChanged(int))); + + connect(ui.click, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(ui.numlockButtonGroup, SIGNAL(released(int)), this, SLOT(changed())); + +// Not sure why we need this - if XKB is not found the whole keyboard module won't be compiled +//#if !defined(HAVE_XTEST) && !defined(HAVE_XKB) +// ui.numlockButtonGroup->setDisabled( true ); +//#endif +//#if !defined(HAVE_XKB) && !defined(HAVE_XF86MISC) +// ui.delay->setDisabled( true ); +// ui.rate->setDisabled( true ); +//#endif +} + +KCMiscKeyboardWidget::~KCMiscKeyboardWidget() +{ + delete &ui; +} + +int KCMiscKeyboardWidget::getClick() +{ + return ui.click->value(); +} + +// set the slider and LCD values +void KCMiscKeyboardWidget::setRepeat(TriState keyboardRepeat, int delay_, double rate_) +{ + TriStateHelper::setTriState( ui.keyboardRepeatButtonGroup, keyboardRepeat ); +// ui.repeatBox->setChecked(r == AutoRepeatModeOn); + ui.delay->setValue(delay_); + ui.rate->setValue(rate_); + delaySpinboxChanged(delay_); + rateSpinboxChanged(rate_); +} + +void KCMiscKeyboardWidget::setClickVolume(int v) +{ + ui.click->setValue(v); +} + +TriState TriStateHelper::getTriState(const KButtonGroup* group) +{ + int selected = group->selected(); + return selected < 0 ? STATE_UNCHANGED : getTriState(selected); +} + +void TriStateHelper::setTriState(KButtonGroup* group, TriState state) +{ + group->setSelected( getInt(state) ); +} + +void KCMiscKeyboardWidget::load() +{ + KConfigGroup config(KSharedConfig::openConfig("kcminputrc", KConfig::NoGlobals), "Keyboard"); + + ui.delay->blockSignals(true); + ui.rate->blockSignals(true); + ui.click->blockSignals(true); + + // need to read as string to support old "true/false" parameter + QString key = config.readEntry("KeyboardRepeating", TriStateHelper::getString(STATE_ON)); + if( key == "true" || key == TriStateHelper::getString(STATE_ON)) { + keyboardRepeat = STATE_ON; + } + else if( key == "false" || key == TriStateHelper::getString(STATE_OFF)) { + keyboardRepeat = STATE_OFF; + } + else { + keyboardRepeat = STATE_UNCHANGED; + } + +// keyboardRepeat = (key ? AutoRepeatModeOn : AutoRepeatModeOff); + float delay = config.readEntry( "RepeatDelay", 660 ); + float rate = config.readEntry( "RepeatRate", 25. ); + setRepeat(keyboardRepeat, delay, rate); + + XKeyboardState kbd; + XGetKeyboardControl(QX11Info::display(), &kbd); + + clickVolume = config.readEntry("ClickVolume", kbd.key_click_percent); + setClickVolume(clickVolume); + // setRepeat(kbd.global_auto_repeat, ui.delay->value(), ui.rate->value()); + + numlockState = TriStateHelper::getTriState(config.readEntry( "NumLock", TriStateHelper::getInt(STATE_UNCHANGED) )); + TriStateHelper::setTriState( ui.numlockButtonGroup, numlockState ); + + ui.delay->blockSignals(false); + ui.rate->blockSignals(false); + ui.click->blockSignals(false); +} + +void KCMiscKeyboardWidget::save() +{ + KConfigGroup config(KSharedConfig::openConfig("kcminputrc", KConfig::NoGlobals), "Keyboard"); + + clickVolume = getClick(); + keyboardRepeat = TriStateHelper::getTriState(ui.keyboardRepeatButtonGroup); + numlockState = TriStateHelper::getTriState(ui.numlockButtonGroup); + + config.writeEntry("ClickVolume",clickVolume); + config.writeEntry("KeyboardRepeating", TriStateHelper::getInt(keyboardRepeat)); + config.writeEntry("RepeatRate", ui.rate->value() ); + config.writeEntry("RepeatDelay", ui.delay->value() ); + config.writeEntry("NumLock", TriStateHelper::getInt(numlockState) ); + config.sync(); +} + +void KCMiscKeyboardWidget::defaults() +{ + setClickVolume(50); + setRepeat(STATE_ON, 660, 25); + TriStateHelper::setTriState( ui.numlockButtonGroup, STATE_UNCHANGED ); + emit changed(true); +} + +QString KCMiscKeyboardWidget::quickHelp() const +{ + return QString(); + + /* "

    Keyboard

    This module allows you to choose options" + " for the way in which your keyboard works. The actual effect of" + " setting these options depends upon the features provided by your" + " keyboard hardware and the X server on which KDE is running.

    " + " For example, you may find that changing the key click volume" + " has no effect because this feature is not available on your system." */ +} + +void KCMiscKeyboardWidget::delaySliderChanged (int value) { + double alpha = sliderMax / (log(5000.0L) - log(100.0L)); + double linearValue = exp (value/alpha + log(100.0L)); + + ui.delay->setValue((int)floor(0.5 + linearValue)); + + emit changed(true); +} + +void KCMiscKeyboardWidget::delaySpinboxChanged (int value) { + double alpha = sliderMax / (log(5000.0L) - log(100.0L)); + double logVal = alpha * (log((double)value)-log(100.0L)); + + ui.delaySlider->setValue ((int)floor (0.5 + logVal)); + + emit changed(true); +} + +void KCMiscKeyboardWidget::rateSliderChanged (int value) { + ui.rate->setValue(value/100.0); + + emit changed(true); +} + +void KCMiscKeyboardWidget::rateSpinboxChanged (double value) { + ui.rateSlider->setValue ((int)(value*100)); + + emit changed(true); +} + +void KCMiscKeyboardWidget::changed() +{ + emit changed(true); +} + +void KCMiscKeyboardWidget::keyboardRepeatStateChanged(int selection) +{ + bool enabled = selection == TriStateHelper::getInt(STATE_ON); + ui.keyboardRepeatParamsGroupBox->setEnabled(enabled); + changed(); +} diff --git a/kcms/keyboard/kcmmisc.h b/kcms/keyboard/kcmmisc.h new file mode 100644 index 0000000000..411bdd2b0f --- /dev/null +++ b/kcms/keyboard/kcmmisc.h @@ -0,0 +1,91 @@ +/* + * keyboard.h + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KCMMISC_H__ +#define __KCMMISC_H__ + +#include +#include + +class Ui_KeyboardConfigWidget; + +enum TriState { + STATE_ON = 0, STATE_OFF = 1, STATE_UNCHANGED = 2 +}; + +class KButtonGroup; +class TriStateHelper { +public: + static void setTriState(KButtonGroup* group, TriState state); + static TriState getTriState(const KButtonGroup* group); + + static TriState getTriState(int state) { return static_cast(state); } + static int getInt(TriState state) { return static_cast(state); } + static const char* getString(TriState state) { + return state == STATE_ON ? "0" : state == STATE_OFF ? "1" : "2"; + } +}; + +class KCMiscKeyboardWidget : public QWidget +{ + Q_OBJECT +public: + KCMiscKeyboardWidget(QWidget *parent); + ~KCMiscKeyboardWidget(); + + void save(); + void load(); + void defaults(); + + QString quickHelp() const; + +private Q_SLOTS: + void changed(); + + void delaySliderChanged (int value); + void delaySpinboxChanged (int value); + void rateSliderChanged (int value); + void rateSpinboxChanged (double value); + void keyboardRepeatStateChanged(int selection); + +Q_SIGNALS: + void changed(bool state); + +private: + void setClickVolume( int ); + void setRepeat( TriState flag, int delay, double rate); + void setRepeatRate( int ); + + int getClick(); + int getRepeatRate(); + + int sliderMax; + int clickVolume; + enum TriState keyboardRepeat; + enum TriState numlockState; + + Ui_KeyboardConfigWidget& ui; +}; + +#endif + diff --git a/kcms/keyboard/kcmmiscwidget.ui b/kcms/keyboard/kcmmiscwidget.ui new file mode 100644 index 0000000000..37fbaf4b9c --- /dev/null +++ b/kcms/keyboard/kcmmiscwidget.ui @@ -0,0 +1,335 @@ + + + KeyboardConfigWidget + + + + 0 + 0 + 577 + 361 + + + + + + + If supported, this option allows you to setup the state of NumLock after KDE startup.<p>You can configure NumLock to be turned on or off, or configure KDE not to set NumLock state. + + + NumLock on KDE Startup + + + + + + true + + + + + + T&urn on + + + + + + + Turn o&ff + + + + + + + Leave unchan&ged + + + + + + + + + + + + + Keyboard Repeat + + + + + + true + + + + + + T&urn on + + + + + + + Turn o&ff + + + + + + + Leave unchan&ged + + + + + + + + + + Qt::Horizontal + + + + 159 + 20 + + + + + + + + + + + false + + + + + + + 0 + 0 + + + + &Delay: + + + delay + + + + + + + If supported, this option allows you to set the delay after which a pressed key will start generating keycodes. The 'Repeat rate' option controls the frequency of these keycodes. + + + 0 + + + 10000 + + + 1000 + + + 1000 + + + 5000 + + + Qt::Horizontal + + + 1000 + + + + + + + If supported, this option allows you to set the delay after which a pressed key will start generating keycodes. The 'Repeat rate' option controls the frequency of these keycodes. + + + ms + + + + + + + + 0 + 0 + + + + &Rate: + + + rate + + + + + + + If supported, this option allows you to set the rate at which keycodes are generated while a key is pressed. + + + 0 + + + 10000 + + + 1000 + + + 1000 + + + 5000 + + + Qt::Horizontal + + + 1000 + + + + + + + If supported, this option allows you to set the rate at which keycodes are generated while a key is pressed. + + + repeats/s + + + + + + + + + + + + + + + + + + Test area: + + + + + + + Allows to test keyboard repeat and click volume (just don't forget to apply the changes). + + + + + + + + + Key Click + + + + + + + + If supported, this option allows you to hear audible clicks from your computer's speakers when you press the keys on your keyboard. This might be useful if your keyboard does not have mechanical keys, or if the sound that the keys make is very soft.<p>You can change the loudness of the key click feedback by dragging the slider button or by clicking the up/down arrows on the spin box. Setting the volume to 0% turns off the key click. + + + Key click &volume: + + + click + + + + + + + 100 + + + 10 + + + Qt::Horizontal + + + QSlider::NoTicks + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + KDoubleNumInput + QWidget +

    knuminput.h
    + 1 + + + KButtonGroup + QGroupBox +
    kbuttongroup.h
    + 1 +
    + + KIntNumInput + QWidget +
    knuminput.h
    + 1 +
    + + + + diff --git a/kcms/keyboard/keyboard.desktop b/kcms/keyboard/keyboard.desktop new file mode 100644 index 0000000000..4f4e97b610 --- /dev/null +++ b/kcms/keyboard/keyboard.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Type=Service +Icon=preferences-desktop-keyboard +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=keyboard +X-KDE-DBus-ModuleName=keyboard +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=false +X-KDE-Kded-phase=1 +OnlyShowIn=KDE; + +Name=Keyboard Daemon +Name[ar]=مراقب لوحة المفاتيح +Name[ast]=Degorriu de tecláu +Name[bg]=Демон на клавиатурата +Name[bn]=কীবোর্ড ডীমন +Name[bs]=Demon tastature +Name[ca]=Dimoni del teclat +Name[ca@valencia]=Dimoni del teclat +Name[cs]=Démon klávesnice +Name[da]=Tastatur-dæmon +Name[de]=Tastatur-Dienst +Name[el]=Δαίμονας Πληκτρολογίου +Name[en_GB]=Keyboard Dæmon +Name[es]=Demonio de teclado +Name[et]=Klaviatuurideemon +Name[eu]=Teklatuaren daimona +Name[fi]=Näppäimistötaustaprosessi +Name[fr]=Démon du clavier +Name[ga]=Deamhan Méarchláir +Name[gl]=Servizo do teclado +Name[gu]=કીબોર્ડ ડિમન +Name[he]=תהליך רקע של המקלדת +Name[hi]=कुंजीपट डेमन +Name[hr]=Tipkovnički servis +Name[hu]=Billentyűzetszolgáltatás +Name[ia]=Demone de claviero +Name[id]=Jurik Papan Ketik +Name[is]=Lyklaborðspúki +Name[it]=Demone della tastiera +Name[ja]=キーボードデーモン +Name[kk]=Перенетақта қызметі +Name[km]=ដេមិន​​ក្ដារចុច +Name[kn]=ಕೀಲಿಮಣೆ ನೇಪಥಿಕ +Name[ko]=키보드 데몬 +Name[lt]=Klaviatūros tarnyba +Name[lv]=Tastatūras dēmons +Name[mr]=कळफलक डीमन +Name[nb]=Tastatur-nisse +Name[nds]=Tastatuur-Dämoon +Name[nl]=Toetsenbord-daemon +Name[nn]=Tastaturteneste +Name[pa]=ਕੀ-ਬੋਰਡ ਡੈਮਨ +Name[pl]=Demon klawiatury +Name[pt]=Servidor do Teclado +Name[pt_BR]=Daemon de teclado +Name[ro]=Demon de tastatură +Name[ru]=Фоновая служба клавиатуры +Name[si]=යතුරුපුවරු ඩීමනය +Name[sk]=Démon klávesnice +Name[sl]=Ozadnji program za tipkovnico +Name[sr]=Демон тастатуре +Name[sr@ijekavian]=Демон тастатуре +Name[sr@ijekavianlatin]=Demon tastature +Name[sr@latin]=Demon tastature +Name[sv]=Tangentbordsdemon +Name[th]=ดีมอนแป้นพิมพ์ +Name[tr]=Klavye Servisi +Name[ug]=ھەرپتاختا نازارەتچى +Name[uk]=Фонова служба клавіатури +Name[vi]=Trình nền bàn phím +Name[wa]=Démon del taprece +Name[x-test]=xxKeyboard Daemonxx +Name[zh_CN]=键盘守护进程 +Name[zh_TW]=鍵盤伺服程式 diff --git a/kcms/keyboard/keyboard_applet.cpp b/kcms/keyboard/keyboard_applet.cpp new file mode 100644 index 0000000000..85422d8bda --- /dev/null +++ b/kcms/keyboard/keyboard_applet.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keyboard_applet.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "x11_helper.h" +#include "xkb_rules.h" +#include "keyboard_config.h" +#include "keyboard_dbus.h" +#include "layouts_menu.h" +//#include "utils.h" + + +K_EXPORT_PLASMA_APPLET(keyboard, KeyboardApplet) + +KeyboardApplet::KeyboardApplet(QObject *parent, const QVariantList &args): + Plasma::Applet(parent, args), + xEventNotifier(), + rules(Rules::readRules(Rules::READ_EXTRAS)), + keyboardConfig(new KeyboardConfig()), + layoutsMenu(new LayoutsMenu(*keyboardConfig, *rules, flags)) +{ + if( ! X11Helper::xkbSupported(NULL) ) { + setFailedToLaunch(true, i18n("XKB extension failed to initialize")); + return; + } + + m_svg = new Plasma::Svg(this); + m_svg->setImagePath("widgets/labeltexture"); + m_svg->setContainsMultipleImages(true); + resize(48,48); + + setHasConfigurationInterface(false); + + setAspectRatioMode(Plasma::KeepAspectRatio); + //setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + setBackgroundHints(DefaultBackground); + connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged())); + + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.connect(QString(), KEYBOARD_DBUS_OBJECT_PATH, KEYBOARD_DBUS_SERVICE_NAME, KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE, this, SLOT(configChanged())); +} + +KeyboardApplet::~KeyboardApplet() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.disconnect(QString(), KEYBOARD_DBUS_OBJECT_PATH, KEYBOARD_DBUS_SERVICE_NAME, KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE, this, SLOT(configChanged())); + + delete layoutsMenu; + delete rules; +} + +void KeyboardApplet::keyboardConfigChanged() +{ + readConfig(); + update(); +} + +void KeyboardApplet::readConfig() +{ +// KConfigGroup config = Plasma::Applet::config("KeyboardLayout"); + keyboardConfig->load(); +// drawFlag = keyboardConfig->readEntry("ShowFlag", true); +} + +void KeyboardApplet::configChanged() +{ + Applet::configChanged(); + readConfig(); +} + +void KeyboardApplet::init() +{ + Applet::init(); + + readConfig(); + connect(&xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); + connect(&xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutChanged())); + xEventNotifier.start(); + + layoutChanged(); +} + +void KeyboardApplet::destroy() +{ + xEventNotifier.stop(); + disconnect(&xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutChanged())); + disconnect(&xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); + Applet::destroy(); +} + +void KeyboardApplet::layoutChanged() +{ + generatePixmap(); + updateTooltip(); + update(); +} + +void KeyboardApplet::updateTooltip() +{ + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + if( layoutUnit.isEmpty() ) + return; + + const QIcon icon(getFlag(layoutUnit.layout)); + Plasma::ToolTipContent data(name(), flags.getLongText(layoutUnit, rules), icon); + Plasma::ToolTipManager::self()->setContent(this, data); +} + +const QIcon KeyboardApplet::getFlag(const QString& layout) +{ + return keyboardConfig->isFlagShown() ? flags.getIcon(layout) : QIcon(); +} + +void KeyboardApplet::paintInterface(QPainter *p, const QStyleOptionGraphicsItem */*option*/, const QRect &contentsRect) +{ + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + if( layoutUnit.isEmpty() ) + return; + + const QIcon icon(getFlag(layoutUnit.layout)); + if( ! icon.isNull() ) { + p->save(); + p->setRenderHint(QPainter::SmoothPixmapTransform); + p->setRenderHint(QPainter::Antialiasing); + QPixmap pixmap = icon.pixmap(contentsRect.size()); + p->drawPixmap(contentsRect, pixmap); + p->restore(); + } + if( icon.isNull() || keyboardConfig->isLabelShown() ) { + QRect finalRect(m_pixmap.rect()); + finalRect.moveCenter(contentsRect.center()); + p->drawPixmap(finalRect, m_pixmap); + } +} + +void KeyboardApplet::mousePressEvent ( QGraphicsSceneMouseEvent * event ) +{ + if( event->button() == Qt::LeftButton ) { + X11Helper::switchToNextLayout(); + } + event->ignore(); +} + +void KeyboardApplet::constraintsEvent(Plasma::Constraints constraints) +{ + if (constraints & Plasma::FormFactorConstraint) { + int iconSize; + if (formFactor() == Plasma::Planar || + formFactor() == Plasma::MediaCenter) { + iconSize = IconSize(KIconLoader::Desktop); + } else { + iconSize = IconSize(KIconLoader::Small); + } + setMinimumSize(iconSize, iconSize); + } + if (constraints & Plasma::SizeConstraint) { + generatePixmap(); + } +} + +void KeyboardApplet::generatePixmap() +{ + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + QRect contentsRect = KeyboardApplet::contentsRect().toRect(); + QString shortText = Flags::getShortText(layoutUnit, *keyboardConfig); + + QPixmap pixmap(contentsRect.size()); + pixmap.fill(Qt::transparent); + + QFont font = Plasma::Theme::defaultTheme()->font(Plasma::Theme::DesktopFont); + int height = qMin(contentsRect.height(), contentsRect.width()); + int fontSize = shortText.length() == 2 + ? height * 13 / 15 + : height * 5 / 15; + + int smallestReadableSize = KGlobalSettings::smallestReadableFont().pixelSize(); + if( fontSize < smallestReadableSize ) { + fontSize = smallestReadableSize; + } + font.setPixelSize(fontSize); + if( keyboardConfig->isFlagShown() ) { + m_pixmap = Plasma::PaintUtils::shadowText(shortText, font, Qt::black, Qt::white, QPoint(), 3); + } + else { + m_pixmap = Plasma::PaintUtils::texturedText(shortText, font, m_svg); + } +} + +QList KeyboardApplet::contextualActions() +{ + return layoutsMenu->contextualActions(); +} diff --git a/kcms/keyboard/keyboard_applet.h b/kcms/keyboard/keyboard_applet.h new file mode 100644 index 0000000000..2fb9c98fde --- /dev/null +++ b/kcms/keyboard/keyboard_applet.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARD_APPLET_H_ +#define KEYBOARD_APPLET_H_ + +#include + +#include + +#include "x11_helper.h" +#include "flags.h" + +class QGraphicsSceneMouseEvent; +class QAction; +class KConfigDialog; +class QActionGroup; +class Rules; +class KeyboardConfig; +class LayoutsMenu; + +class KeyboardApplet : public Plasma::Applet +{ + Q_OBJECT + +public Q_SLOTS: + void init(); + void destroy(); + +private Q_SLOTS: + void configChanged(); + void layoutChanged(); + +public: + KeyboardApplet(QObject *parent, const QVariantList &args); + virtual ~KeyboardApplet(); + + void constraintsEvent(Plasma::Constraints constraints); + void paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect); + +protected: + void mousePressEvent ( QGraphicsSceneMouseEvent *event ); + QList contextualActions(); + void generatePixmap(); +// void createConfigurationInterface(KConfigDialog *parent); + +private Q_SLOTS: + void keyboardConfigChanged(); + +private: + void readConfig(); + const QIcon getFlag(const QString& layout); + void updateTooltip(); + +// bool drawFlag; + Flags flags; + XEventNotifier xEventNotifier; + const Rules* rules; + KeyboardConfig* keyboardConfig; + LayoutsMenu* layoutsMenu; + Plasma::Svg *m_svg; + QPixmap m_pixmap; +}; + +#endif /* KEYBOARD_APPLET_H_ */ diff --git a/kcms/keyboard/keyboard_config.cpp b/kcms/keyboard/keyboard_config.cpp new file mode 100644 index 0000000000..f3ff97ca84 --- /dev/null +++ b/kcms/keyboard/keyboard_config.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keyboard_config.h" + +#include +#include +#include + + +static const char* SWITCHING_POLICIES[] = {"Global", "Desktop", "WinClass", "Window", NULL }; +static const char* LIST_SEPARATOR = ","; +//static const char* DEFAULT_LAYOUT = "us"; +static const char* DEFAULT_MODEL = "pc104"; + +static const QString CONFIG_FILENAME("kxkbrc"); +static const QString CONFIG_GROUPNAME("Layout"); + +const int KeyboardConfig::NO_LOOPING = -1; + +KeyboardConfig::KeyboardConfig() +{ + setDefaults(); +} + +QString KeyboardConfig::getSwitchingPolicyString(SwitchingPolicy switchingPolicy) { + return SWITCHING_POLICIES[switchingPolicy]; +} + +static int findStringIndex(const char* strings[], const QString& toFind, int defaultIndex) +{ + for(int i=0; strings[i] != NULL; i++) { + if( toFind == strings[i] ) { + return i; + } + } + return defaultIndex; +} + +void KeyboardConfig::setDefaults() +{ + keyboardModel = DEFAULT_MODEL; + resetOldXkbOptions = false; + xkbOptions.clear(); + + // init layouts options + configureLayouts = false; + layouts.clear(); +// layouts.append(LayoutUnit(DEFAULT_LAYOUT)); + layoutLoopCount = NO_LOOPING; + + // switch cotrol options + switchingPolicy = SWITCH_POLICY_GLOBAL; +// stickySwitching = false; +// stickySwitchingDepth = 2; + + // display options + showIndicator = true; + indicatorType = SHOW_LABEL; + showSingle = false; +} + +static +KeyboardConfig::IndicatorType getIndicatorType(bool showFlag, bool showLabel) +{ + if( showFlag ) { + if( showLabel ) + return KeyboardConfig::SHOW_LABEL_ON_FLAG; + else + return KeyboardConfig::SHOW_FLAG; + } + else { + return KeyboardConfig::SHOW_LABEL; + } +} + + +void KeyboardConfig::load() +{ + KConfigGroup config(KSharedConfig::openConfig( CONFIG_FILENAME, KConfig::NoGlobals ), CONFIG_GROUPNAME); + + keyboardModel = config.readEntry("Model", ""); + + resetOldXkbOptions = config.readEntry("ResetOldOptions", false); + QString options = config.readEntry("Options", ""); + xkbOptions = options.split(LIST_SEPARATOR, QString::SkipEmptyParts); + + configureLayouts = config.readEntry("Use", false); + QString layoutsString = config.readEntry("LayoutList", ""); + QStringList layoutStrings = layoutsString.split(LIST_SEPARATOR, QString::SkipEmptyParts); +// if( layoutStrings.isEmpty() ) { +// layoutStrings.append(DEFAULT_LAYOUT); +// } + layouts.clear(); + foreach(const QString& layoutString, layoutStrings) { + layouts.append(LayoutUnit(layoutString)); + } + if( layouts.isEmpty() ) { + configureLayouts = false; + } + + layoutLoopCount = config.readEntry("LayoutLoopCount", NO_LOOPING); + + QString layoutMode = config.readEntry("SwitchMode", "Global"); + switchingPolicy = static_cast(findStringIndex(SWITCHING_POLICIES, layoutMode, SWITCH_POLICY_GLOBAL)); + + showIndicator = config.readEntry("ShowLayoutIndicator", true); + + bool showFlag = config.readEntry("ShowFlag", false); + bool showLabel = config.readEntry("ShowLabel", true); + indicatorType = getIndicatorType(showFlag, showLabel); + + showSingle = config.readEntry("ShowSingle", false); + + QString labelsStr = config.readEntry("DisplayNames", ""); + QStringList labels = labelsStr.split(LIST_SEPARATOR, QString::KeepEmptyParts); + for(int i=0; i KeyboardConfig::getDefaultLayouts() const +{ + QList defaultLayoutList; + int i = 0; + foreach(const LayoutUnit& layoutUnit, layouts) { + defaultLayoutList.append(layoutUnit); + if( layoutLoopCount != KeyboardConfig::NO_LOOPING && i >= layoutLoopCount-1 ) + break; + i++; + } + return defaultLayoutList; +} + +QList KeyboardConfig::getExtraLayouts() const +{ + if( layoutLoopCount == KeyboardConfig::NO_LOOPING ) + return QList(); + + return layouts.mid(layoutLoopCount, layouts.size()); +} diff --git a/kcms/keyboard/keyboard_config.h b/kcms/keyboard/keyboard_config.h new file mode 100644 index 0000000000..b86418de47 --- /dev/null +++ b/kcms/keyboard/keyboard_config.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARD_CONFIG_H_ +#define KEYBOARD_CONFIG_H_ + +#include "x11_helper.h" + +#include +#include +#include +#include +#include + +/** + * This class provides configuration options for keyboard module + */ +class KeyboardConfig +{ +public: + static const int MAX_LABEL_LEN = 3; + static const int NO_LOOPING; // = -1; + + enum SwitchingPolicy { + SWITCH_POLICY_GLOBAL = 0, + SWITCH_POLICY_DESKTOP = 1, + SWITCH_POLICY_APPLICATION = 2, + SWITCH_POLICY_WINDOW = 3 + }; + + enum IndicatorType { + SHOW_LABEL = 0, + SHOW_FLAG = 1, + SHOW_LABEL_ON_FLAG = 2 + }; + + QString keyboardModel; + // resetOldXkbOptions is now also "set xkb options" + bool resetOldXkbOptions; + QStringList xkbOptions; + + // init layouts options + bool configureLayouts; + QList layouts; + int layoutLoopCount; + + // switch cotrol options + SwitchingPolicy switchingPolicy; +// bool stickySwitching; +// int stickySwitchingDepth; + + // display options + bool showIndicator; + IndicatorType indicatorType; + bool showSingle; + + KeyboardConfig(); + bool isSpareLayoutsEnabled() const { + return layoutLoopCount != KeyboardConfig::NO_LOOPING; + } + QList getDefaultLayouts() const; + QList getExtraLayouts() const; + bool isFlagShown() const { + return indicatorType == SHOW_FLAG || indicatorType == SHOW_LABEL_ON_FLAG; + } + bool isLabelShown() const { + return indicatorType == SHOW_LABEL || indicatorType == SHOW_LABEL_ON_FLAG; + } + + void setDefaults(); + void load(); + void save(); + + static QString getSwitchingPolicyString(SwitchingPolicy switchingPolicy); +}; + +#endif /* KEYBOARD_CONFIG_H_ */ diff --git a/kcms/keyboard/keyboard_daemon.cpp b/kcms/keyboard/keyboard_daemon.cpp new file mode 100644 index 0000000000..825cca45fb --- /dev/null +++ b/kcms/keyboard/keyboard_daemon.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keyboard_daemon.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "x11_helper.h" +#if 0 +#include "xinput_helper.h" +#endif +#include "xkb_helper.h" +#include "keyboard_dbus.h" +#include "xkb_rules.h" +#include "bindings.h" +#include "keyboard_hardware.h" +#include "layout_tray_icon.h" +#include "layout_memory_persister.h" +#include "layouts_menu.h" + + +K_PLUGIN_FACTORY(KeyboardFactory, registerPlugin();) +K_EXPORT_PLUGIN(KeyboardFactory("keyboard", "kxkb")) + +KeyboardDaemon::KeyboardDaemon(QObject *parent, const QList&) + : KDEDModule(parent), + actionCollection(NULL), + xEventNotifier(NULL), + layoutTrayIcon(NULL), + layoutMemory(keyboardConfig), + rules(Rules::readRules(Rules::READ_EXTRAS)) +{ + if( ! X11Helper::xkbSupported(NULL) ) + return; //TODO: shut down the daemon? + + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.registerService(KEYBOARD_DBUS_SERVICE_NAME); + dbus.registerObject(KEYBOARD_DBUS_OBJECT_PATH, this, QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableSignals); + dbus.connect(QString(), KEYBOARD_DBUS_OBJECT_PATH, KEYBOARD_DBUS_SERVICE_NAME, KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE, this, SLOT(configureKeyboard())); + + configureKeyboard(); + registerListeners(); + + LayoutMemoryPersister layoutMemoryPersister(layoutMemory); + if( layoutMemoryPersister.restore("kxkb") ) { + if( layoutMemoryPersister.getGlobalLayout().isValid() ) { + X11Helper::setLayout(layoutMemoryPersister.getGlobalLayout()); + } + } +} + +KeyboardDaemon::~KeyboardDaemon() +{ + LayoutMemoryPersister layoutMemoryPersister(layoutMemory); + layoutMemoryPersister.setGlobalLayout(X11Helper::getCurrentLayout()); + layoutMemoryPersister.save("kxkb"); + + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.disconnect(QString(), KEYBOARD_DBUS_OBJECT_PATH, KEYBOARD_DBUS_SERVICE_NAME, KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE, this, SLOT(configureKeyboard())); + dbus.unregisterObject(KEYBOARD_DBUS_OBJECT_PATH); + dbus.unregisterService(KEYBOARD_DBUS_SERVICE_NAME); + + unregisterListeners(); + unregisterShortcut(); + + delete xEventNotifier; + delete layoutTrayIcon; + delete rules; +} + +void KeyboardDaemon::configureKeyboard() +{ + kDebug() << "Configuring keyboard"; + init_keyboard_hardware(); + + keyboardConfig.load(); + XkbHelper::initializeKeyboardLayouts(keyboardConfig); + layoutMemory.configChanged(); + + setupTrayIcon(); + + unregisterShortcut(); + registerShortcut(); +} + +void KeyboardDaemon::configureMouse() +{ + QStringList modules; + modules << "mouse"; + QProcess::startDetached("kcminit", modules); +} + +void KeyboardDaemon::setupTrayIcon() +{ + bool show = keyboardConfig.showIndicator + && ( keyboardConfig.showSingle || X11Helper::getLayoutsList().size() > 1 ); + + if( show && ! layoutTrayIcon ) { + layoutTrayIcon = new LayoutTrayIcon(rules, keyboardConfig); + } + else if( ! show && layoutTrayIcon ) { + delete layoutTrayIcon; + layoutTrayIcon = NULL; + } +} + +void KeyboardDaemon::registerShortcut() +{ + if( actionCollection == NULL ) { + actionCollection = new KeyboardLayoutActionCollection(this, false); + + QAction* toggleLayoutAction = actionCollection->getToggeAction(); + connect(toggleLayoutAction, SIGNAL(triggered()), this, SLOT(switchToNextLayout())); + actionCollection->loadLayoutShortcuts(keyboardConfig.layouts, rules); + connect(actionCollection, SIGNAL(actionTriggered(QAction*)), this, SLOT(setLayout(QAction*))); + + connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(globalSettingsChanged(int))); + } +} + +void KeyboardDaemon::unregisterShortcut() +{ + // register KDE keyboard shortcut for switching layouts + if( actionCollection != NULL ) { + disconnect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(globalSettingsChanged(int))); + + disconnect(actionCollection, SIGNAL(actionTriggered(QAction*)), this, SLOT(setLayout(QAction*))); + disconnect(actionCollection->getToggeAction(), SIGNAL(triggered()), this, SLOT(switchToNextLayout())); + + delete actionCollection; + actionCollection = NULL; + } +} + +void KeyboardDaemon::registerListeners() +{ +#warning XInputEventNotifier needs porting to QAbstractNativeEventFilter +#if 0 + if( xEventNotifier == NULL ) { + xEventNotifier = new XInputEventNotifier(); + } + connect(xEventNotifier, SIGNAL(newPointerDevice()), this, SLOT(configureMouse())); + connect(xEventNotifier, SIGNAL(newKeyboardDevice()), this, SLOT(configureKeyboard())); + connect(xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutMapChanged())); + connect(xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); + xEventNotifier->start(); +#endif +} + +void KeyboardDaemon::unregisterListeners() +{ +#warning XInputEventNotifier needs porting to QAbstractNativeEventFilter +#if 0 + if( xEventNotifier != NULL ) { + xEventNotifier->stop(); + disconnect(xEventNotifier, SIGNAL(newPointerDevice()), this, SLOT(configureMouse())); + disconnect(xEventNotifier, SIGNAL(newKeyboardDevice()), this, SLOT(configureKeyboard())); + disconnect(xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); + disconnect(xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutMapChanged())); + } +#endif +} + +void KeyboardDaemon::globalSettingsChanged(int category) +{ + if ( category == KGlobalSettings::SETTINGS_SHORTCUTS) { +//TODO: optimize this? seems like we'll get configReload and globalShortcuts from kcm so we'll reconfigure twice + unregisterShortcut(); + registerShortcut(); + } +} + +void KeyboardDaemon::layoutChanged() +{ + //TODO: pass newLayout into layoutTrayIcon? + LayoutUnit newLayout = X11Helper::getCurrentLayout(); + + layoutMemory.layoutChanged(); + if( layoutTrayIcon != NULL ) { + layoutTrayIcon->layoutChanged(); + } + + if( newLayout != currentLayout ) { + currentLayout = newLayout; + emit currentLayoutChanged(newLayout.toString()); + } +} + +void KeyboardDaemon::layoutMapChanged() +{ + keyboardConfig.load(); + layoutMemory.layoutMapChanged(); + emit layoutListChanged(); + if( layoutTrayIcon != NULL ) { + layoutTrayIcon->layoutMapChanged(); + } +} + +void KeyboardDaemon::switchToNextLayout() +{ + kDebug() << "Toggling layout"; + X11Helper::switchToNextLayout(); +} + +bool KeyboardDaemon::setLayout(QAction* action) +{ + if( action == actionCollection->getToggeAction() ) + return false; + + LayoutUnit layoutUnit(action->data().toString()); + return LayoutsMenu::switchToLayout(layoutUnit, keyboardConfig); // need this to be able to switch to spare layouts +// return X11Helper::setLayout(LayoutUnit(action->data().toString())); +} + +bool KeyboardDaemon::setLayout(const QString& layout) +{ + return X11Helper::setLayout(LayoutUnit(layout)); +} + +QString KeyboardDaemon::getCurrentLayout() +{ + return X11Helper::getCurrentLayout().toString(); +} + +QStringList KeyboardDaemon::getLayoutsList() +{ + return X11Helper::getLayoutsListAsString( X11Helper::getLayoutsList() ); +} + +#include "keyboard_daemon.moc" diff --git a/kcms/keyboard/keyboard_daemon.h b/kcms/keyboard/keyboard_daemon.h new file mode 100644 index 0000000000..4edb968bb0 --- /dev/null +++ b/kcms/keyboard/keyboard_daemon.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARD_DAEMON_H_ +#define KEYBOARD_DAEMON_H_ + +#include +#include +#include + +#include "layout_memory.h" +#include "keyboard_dbus.h" +#include "bindings.h" + + +class KActionCollection; +class XInputEventNotifier; +class LayoutTrayIcon; +class KeyboardConfig; +class KAction; +class Rules; + +class Q_DECL_EXPORT KeyboardDaemon : public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.KeyboardLayouts") + + KeyboardConfig keyboardConfig; + KeyboardLayoutActionCollection *actionCollection; + XInputEventNotifier* xEventNotifier; + LayoutTrayIcon* layoutTrayIcon; + LayoutMemory layoutMemory; + LayoutUnit currentLayout; + const Rules* rules; + + void registerListeners(); + void registerShortcut(); + void unregisterListeners(); + void unregisterShortcut(); + void setupTrayIcon(); + +private Q_SLOTS: + void switchToNextLayout(); + void globalSettingsChanged(int category); + void configureKeyboard(); + void configureMouse(); + void layoutChanged(); + void layoutMapChanged(); + bool setLayout(QAction* action); + +public Q_SLOTS: + Q_SCRIPTABLE bool setLayout(const QString& layout); + Q_SCRIPTABLE QString getCurrentLayout(); + Q_SCRIPTABLE QStringList getLayoutsList(); + +Q_SIGNALS: + Q_SCRIPTABLE void currentLayoutChanged(QString layout); + Q_SCRIPTABLE void layoutListChanged(); + +public: + KeyboardDaemon(QObject *parent, const QList&); + virtual ~KeyboardDaemon(); +}; + +#endif /* KEYBOARD_DAEMON_H_ */ diff --git a/kcms/keyboard/keyboard_dbus.h b/kcms/keyboard/keyboard_dbus.h new file mode 100644 index 0000000000..4ea39668d7 --- /dev/null +++ b/kcms/keyboard/keyboard_dbus.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARD_DBUS_H_ +#define KEYBOARD_DBUS_H_ + +#define KEYBOARD_DBUS_SERVICE_NAME "org.kde.keyboard" +#define KEYBOARD_DBUS_OBJECT_PATH "/Layouts" +#define KEYBOARD_DBUS_CONFIG_RELOAD_MESSAGE "reloadConfig" + +#endif /* KEYBOARD_DBUS_H_ */ diff --git a/kcms/keyboard/keyboard_hardware.cpp b/kcms/keyboard/keyboard_hardware.cpp new file mode 100644 index 0000000000..dca49b6740 --- /dev/null +++ b/kcms/keyboard/keyboard_hardware.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include +#include // WTF? - otherwise compiler complains + +#include + +#include + +#include "x11_helper.h" +#include "kcmmisc.h" + +// from numlockx.c +extern "C" void numlockx_change_numlock_state(Display* dpy, int state); + +#include +#include + + +// This code is taken from xset utility from XFree 4.3 (http://www.xfree86.org/) + +static +void set_repeatrate(int delay, double rate) +{ + if( !X11Helper::xkbSupported(NULL) ) { + kError() << "Failed to set keyboard repeat rate: xkb is not supported"; + return; + } + + XkbDescPtr xkb = XkbAllocKeyboard(); + if (xkb) { + Display* dpy = QX11Info::display(); + int res = XkbGetControls(dpy, XkbRepeatKeysMask, xkb); + xkb->ctrls->repeat_delay = delay; + xkb->ctrls->repeat_interval = (int)floor(1000/rate + 0.5); + res = XkbSetControls(dpy, XkbRepeatKeysMask, xkb); + XkbFreeKeyboard(xkb, 0, true); + return; + } +} + +static +int set_volume(int clickVolumePercent, TriState keyboardRepeatMode) +{ + XKeyboardState kbd; + XKeyboardControl kbdc; + + XGetKeyboardControl(QX11Info::display(), &kbd); + + int flags = 0; + if( clickVolumePercent != -1 ) { + flags |= KBKeyClickPercent; + kbdc.key_click_percent = clickVolumePercent; + } + if( keyboardRepeatMode != STATE_UNCHANGED ) { + flags |= KBAutoRepeatMode; + kbdc.auto_repeat_mode = (keyboardRepeatMode==STATE_ON ? AutoRepeatModeOn : AutoRepeatModeOff); + } + + return XChangeKeyboardControl(QX11Info::display(), flags, &kbdc); +} + +void init_keyboard_hardware() +{ + KConfigGroup config(KSharedConfig::openConfig( "kcminputrc" ), "Keyboard"); + + QString keyRepeatStr = config.readEntry("KeyboardRepeating", TriStateHelper::getString(STATE_ON)); + TriState keyRepeat = STATE_UNCHANGED; + if( keyRepeatStr == "true" || keyRepeatStr == TriStateHelper::getString(STATE_ON) ) { + keyRepeat = STATE_ON; + } + else if( keyRepeatStr == "false" || keyRepeatStr == TriStateHelper::getString(STATE_OFF) ) { + keyRepeat = STATE_OFF; + } + + int clickVolumePercent = config.readEntry("ClickVolume", -1); + if( clickVolumePercent != -1 && keyRepeat != STATE_UNCHANGED ) { + set_volume(clickVolumePercent, keyRepeat); + } + + if( keyRepeat == STATE_ON ) { + int delay_ = config.readEntry("RepeatDelay", 250); + double rate_ = config.readEntry("RepeatRate", 30.); + set_repeatrate(delay_, rate_); + } + + + TriState numlockState = TriStateHelper::getTriState( config.readEntry( "NumLock", TriStateHelper::getInt(STATE_UNCHANGED) ) ); + if( numlockState != STATE_UNCHANGED ) { + numlockx_change_numlock_state(QX11Info::display(), numlockState == STATE_ON ); + } +} diff --git a/kcms/keyboard/keyboard_hardware.h b/kcms/keyboard/keyboard_hardware.h new file mode 100644 index 0000000000..e3daa80b91 --- /dev/null +++ b/kcms/keyboard/keyboard_hardware.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARD_HARDWARE_H_ +#define KEYBOARD_HARDWARE_H_ + +void init_keyboard_hardware(); + +#endif /* KEYBOARD_HARDWARE_H_ */ diff --git a/kcms/keyboard/layout_memory.cpp b/kcms/keyboard/layout_memory.cpp new file mode 100644 index 0000000000..9e723612b7 --- /dev/null +++ b/kcms/keyboard/layout_memory.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "layout_memory.h" + +#include +#include + +#include +#include + +#include "x11_helper.h" +#include "xkb_helper.h" + + +LayoutMemory::LayoutMemory(const KeyboardConfig& keyboardConfig_): + prevLayoutList(X11Helper::getLayoutsList()), + keyboardConfig(keyboardConfig_) +{ + registerListeners(); +} + +LayoutMemory::~LayoutMemory() +{ + unregisterListeners(); +} + +void LayoutMemory::configChanged() +{ +// this->layoutMap.clear(); // if needed this will be done on layoutMapChanged event + unregisterListeners(); + registerListeners(); +} + +void LayoutMemory::registerListeners() +{ + if( keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_WINDOW + || keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_APPLICATION ) { + connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(windowChanged(WId))); +// connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); + } + if( keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_DESKTOP ) { + connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(desktopChanged(int))); + } +} + +void LayoutMemory::unregisterListeners() +{ + disconnect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(windowChanged(WId))); + disconnect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(desktopChanged(int))); +// disconnect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); +} + +QString LayoutMemory::getCurrentMapKey() { + switch(keyboardConfig.switchingPolicy) { + case KeyboardConfig::SWITCH_POLICY_WINDOW: { + WId wid = KWindowSystem::self()->activeWindow(); + KWindowInfo winInfo(wid, NET::WMWindowType); + NET::WindowType windowType = winInfo.windowType( NET::NormalMask | NET::DesktopMask | NET::DialogMask ); + kDebug() << "window type" << windowType; + + // we ignore desktop type so that our keybaord layout applet on desktop could change layout properly + if( windowType == NET::Desktop ) + return previousLayoutMapKey; + if( windowType != NET::Unknown && windowType != NET::Normal && windowType != NET::Dialog ) + return QString(); + + return QString::number(wid); + } + case KeyboardConfig::SWITCH_POLICY_APPLICATION: { + WId wid = KWindowSystem::self()->activeWindow(); + KWindowInfo winInfo(wid, NET::WMWindowType, NET::WM2WindowClass); + NET::WindowType windowType = winInfo.windowType( NET::NormalMask | NET::DesktopMask | NET::DialogMask ); + kDebug() << "window type" << windowType; + + // we ignore desktop type so that our keybaord layout applet on desktop could change layout properly + if( windowType == NET::Desktop ) + return previousLayoutMapKey; + if( windowType != NET::Unknown && windowType != NET::Normal && windowType != NET::Dialog ) + return QString(); + + // shall we use pid or window class ??? - class seems better (see e.g. https://bugs.kde.org/show_bug.cgi?id=245507) + // for window class shall we use class.class or class.name? (seem class.class is a bit better - more app-oriented) + kDebug() << "New active window with class.class: " << winInfo.windowClassClass(); + return QString(winInfo.windowClassClass()); +// NETWinInfo winInfoForPid( QX11Info::display(), wid, QX11Info::appRootWindow(), NET::WMPid); +// return QString::number(winInfoForPid.pid()); + } + case KeyboardConfig::SWITCH_POLICY_DESKTOP: + return QString::number(KWindowSystem::self()->currentDesktop()); + default: + return QString(); + } +} + +static +bool isExtraSubset(const QList& allLayouts, const QList& newList) +{ + if( allLayouts.first() != newList.first() ) + return false; + foreach(const LayoutUnit& layoutUnit, newList) { + if( ! allLayouts.contains(layoutUnit) ) + return false; + } + return true; +} + +void LayoutMemory::layoutMapChanged() +{ + QList newLayoutList(X11Helper::getLayoutsList()); + + if( prevLayoutList == newLayoutList ) + return; + + kDebug() << "Layout map change: " << LayoutSet::toString(prevLayoutList) << "-->" << LayoutSet::toString(newLayoutList); + prevLayoutList = newLayoutList; + + //TODO: need more thinking here on how to support external map resetting + if( keyboardConfig.configureLayouts + && keyboardConfig.isSpareLayoutsEnabled() + && isExtraSubset(keyboardConfig.layouts, newLayoutList) ) { + kDebug() << "Layout map change for extra layout"; + layoutChanged(); // to remember new map for active "window" + } + else { +// if( newLayoutList != keyboardConfig.getDefaultLayouts() ) { + // layoutList = newLayoutList; + kDebug() << "Layout map change from external source: clearing layout memory"; + layoutMap.clear(); +// } + } +} + +void LayoutMemory::layoutChanged() +{ + QString layoutMapKey = getCurrentMapKey(); + if( layoutMapKey.isEmpty() ) + return; + + layoutMap[ layoutMapKey ] = X11Helper::getCurrentLayouts(); +} + +void LayoutMemory::setCurrentLayoutFromMap() +{ + QString layoutMapKey = getCurrentMapKey(); + if( layoutMapKey.isEmpty() ) + return; + + if( ! layoutMap.contains(layoutMapKey) ) { +// kDebug() << "new key for layout map" << layoutMapKey; + + if( ! X11Helper::isDefaultLayout() ) { +// kDebug() << "setting default layout for container key" << layoutMapKey; + if( keyboardConfig.configureLayouts && keyboardConfig.isSpareLayoutsEnabled() + && X11Helper::getLayoutsList() != keyboardConfig.getDefaultLayouts() ) { + XkbHelper::initializeKeyboardLayouts(keyboardConfig.getDefaultLayouts()); + } + X11Helper::setDefaultLayout(); + } + } + else { + LayoutSet layoutFromMap = layoutMap[layoutMapKey]; + kDebug() << "Setting layout map item" << layoutFromMap.currentLayout.toString() + << "for container key" << layoutMapKey; + + LayoutSet currentLayouts = X11Helper::getCurrentLayouts(); + if( layoutFromMap.layouts != currentLayouts.layouts ) { + if( keyboardConfig.configureLayouts && keyboardConfig.isSpareLayoutsEnabled() ) { + XkbHelper::initializeKeyboardLayouts(layoutFromMap.layouts); + } + X11Helper::setLayout( layoutFromMap.currentLayout ); + } + else if( layoutFromMap.currentLayout != currentLayouts.currentLayout ) { + X11Helper::setLayout( layoutFromMap.currentLayout ); + } + } + + previousLayoutMapKey = layoutMapKey; +} + +//#include +//#include + +void LayoutMemory::windowChanged(WId /*wId*/) +{ +// KPluginInfo::List plugins = Plasma::Containment::listContainments(); +// foreach(KPluginInfo info, plugins) { +// kDebug() << "applets" << info.name(); +// } + setCurrentLayoutFromMap(); +} + +void LayoutMemory::desktopChanged(int /*desktop*/) +{ + setCurrentLayoutFromMap(); +} diff --git a/kcms/keyboard/layout_memory.h b/kcms/keyboard/layout_memory.h new file mode 100644 index 0000000000..df8568c2bd --- /dev/null +++ b/kcms/keyboard/layout_memory.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef LAYOUT_MEMORY_H_ +#define LAYOUT_MEMORY_H_ + +#include +#include +#include //For WId + +#include "x11_helper.h" +#include "keyboard_config.h" + +class LayoutMemoryPersister; + +class LayoutMemory : public QObject +{ + Q_OBJECT + + // if there's some transient windows coming up we'll need to either ignore it + // or in case of layout switcher popup menu to apply new layout to previous key + QString previousLayoutMapKey; + QList prevLayoutList; + const KeyboardConfig& keyboardConfig; + + void registerListeners(); + void unregisterListeners(); + QString getCurrentMapKey(); + void setCurrentLayoutFromMap(); + +public Q_SLOTS: + void layoutMapChanged(); + void layoutChanged(); + void windowChanged(WId wId); + void desktopChanged(int desktop); + +public: + LayoutMemory(const KeyboardConfig& keyboardConfig); + virtual ~LayoutMemory(); + + void configChanged(); + +protected: + //QVariant does not support long for WId so we'll use QString for key instead + QMap layoutMap; + + friend class LayoutMemoryPersister; +}; + +#endif /* LAYOUT_MEMORY_H_ */ diff --git a/kcms/keyboard/layout_memory_persister.cpp b/kcms/keyboard/layout_memory_persister.cpp new file mode 100644 index 0000000000..8a6118aad9 --- /dev/null +++ b/kcms/keyboard/layout_memory_persister.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "layout_memory_persister.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "keyboard_config.h" +#include "layout_memory.h" + + +static const char* VERSION = "1.0"; +static const char* DOC_NAME = "LayoutMap"; +static const char* ROOT_NODE = "LayoutMap"; +static const char* VERSION_ATTRIBUTE = "version"; +static const char* SWITCH_MODE_ATTRIBUTE = "SwitchMode"; +static const char* ITEM_NODE = "item"; +static const QString CURRENT_LAYOUT_ATTRIBUTE("currentLayout"); +static const char* OWNER_KEY_ATTRIBUTE = "ownerKey"; +static const char* LAYOUTS_ATTRIBUTE = "layouts"; + +static const char* LIST_SEPARATOR_LM = ","; + +static const char* REL_SESSION_FILE_PATH = "/session/keyboard/layout_memory.xml"; + +QString LayoutMemoryPersister::getLayoutMapAsString() +{ + if( ! canPersist() ) + return ""; + + QDomDocument doc(DOC_NAME); + QDomElement root = doc.createElement(ROOT_NODE); + root.setAttribute(VERSION_ATTRIBUTE, VERSION); + root.setAttribute(SWITCH_MODE_ATTRIBUTE, KeyboardConfig::getSwitchingPolicyString(layoutMemory.keyboardConfig.switchingPolicy)); + doc.appendChild(root); + + if( layoutMemory.keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_GLOBAL ) { + if( ! globalLayout.isValid() ) + return ""; + + QDomElement item = doc.createElement(ITEM_NODE); + item.setAttribute(CURRENT_LAYOUT_ATTRIBUTE, globalLayout.toString()); + root.appendChild(item); + } + else { + foreach(const QString& key , layoutMemory.layoutMap.keys()) { + QDomElement item = doc.createElement(ITEM_NODE); + item.setAttribute(OWNER_KEY_ATTRIBUTE, key); + item.setAttribute(CURRENT_LAYOUT_ATTRIBUTE, layoutMemory.layoutMap[key].currentLayout.toString()); + + QString layoutSetString; + foreach(const LayoutUnit& layoutUnit, layoutMemory.layoutMap[key].layouts) { + if( ! layoutSetString.isEmpty() ) { + layoutSetString += LIST_SEPARATOR_LM; + } + layoutSetString += layoutUnit.toString(); + } + item.setAttribute(LAYOUTS_ATTRIBUTE, layoutSetString); + root.appendChild(item); + } + } + + return doc.toString(); +} + +static bool isRestoreSession() +{ + KConfigGroup c(KSharedConfig::openConfig("ksmserverrc", KConfig::NoGlobals), "General"); + kDebug() << "loginMode:" << c.readEntry("loginMode"); + QString loginMode = c.readEntry("loginMode"); + return loginMode != "default" && loginMode != "restoreSavedSession"; // we don't know how to restore saved session - only previous one +} + +bool LayoutMemoryPersister::save(const QString& moduleName) +{ + if( isRestoreSession() ) { + QString relPath = moduleName + REL_SESSION_FILE_PATH; + QFile file(KStandardDirs::locateLocal("data", relPath)); + return saveToFile(file); + } + return false; +} + +bool LayoutMemoryPersister::restore(const QString& moduleName) +{ + if( isRestoreSession() ) { + QString relPath = moduleName + REL_SESSION_FILE_PATH; + QFile file(KStandardDirs::locateLocal("data", relPath)); + return restoreFromFile(file); + } + return false; +} + + +bool LayoutMemoryPersister::saveToFile(const QFile& file_) +{ + QString xml = getLayoutMapAsString(); + if( xml.isEmpty() ) + return false; + + QFile file(file_.fileName()); // so we don't expose the file we open/close to the caller + if( ! file.open( QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) ) { + kWarning() << "Failed to open layout memory xml file for writing" << file.fileName(); + return false; + } + + QTextStream out(&file); + out << xml; + out.flush(); + + if( file.error() != QFile::NoError ) { + kWarning() << "Failed to store keyboard layout memory, error" << file.error(); + file.close(); + file.remove(); + return false; + } + else { + kDebug() << "Keyboard layout memory stored into" << file.fileName() << "written" << file.pos(); + return true; + } +} + + +class MapHandler : public QXmlDefaultHandler +{ +public: + MapHandler(const KeyboardConfig::SwitchingPolicy& switchingPolicy_): + verified(false), + switchingPolicy(switchingPolicy_) {} + + bool startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, + const QString &qName, const QXmlAttributes &attributes) { + + if( qName == ROOT_NODE ) { + if( attributes.value(VERSION_ATTRIBUTE) != VERSION ) + return false; + if( attributes.value(SWITCH_MODE_ATTRIBUTE) != KeyboardConfig::getSwitchingPolicyString(switchingPolicy) ) + return false; + + verified = true; + } + if( qName == ITEM_NODE ) { + if( ! verified ) + return false; + + if( switchingPolicy == KeyboardConfig::SWITCH_POLICY_GLOBAL ) { + globalLayout = LayoutUnit(attributes.value(CURRENT_LAYOUT_ATTRIBUTE)); + } + else { + QStringList layoutStrings = attributes.value(LAYOUTS_ATTRIBUTE).split(LIST_SEPARATOR_LM); + LayoutSet layoutSet; + foreach(const QString& layoutString, layoutStrings) { + layoutSet.layouts.append(LayoutUnit(layoutString)); + } + layoutSet.currentLayout = LayoutUnit(attributes.value(CURRENT_LAYOUT_ATTRIBUTE)); + QString ownerKey = attributes.value(OWNER_KEY_ATTRIBUTE); + + if( ownerKey.trimmed().isEmpty() || ! layoutSet.isValid() ) + return false; + + layoutMap[ownerKey] = layoutSet; + } + } + return verified; + } + + bool verified; + QMap layoutMap; + LayoutUnit globalLayout; + +private: + const KeyboardConfig::SwitchingPolicy& switchingPolicy; +}; + +template +static +bool containsAll(QList set1, QList set2) +{ + foreach(const T& t, set2) { + if( ! set1.contains(t) ) + return false; + } + return true; +} + +bool LayoutMemoryPersister::restoreFromFile(const QFile& file_) +{ + globalLayout = LayoutUnit(); + + if( ! canPersist() ) + return false; + + QFile file(file_.fileName()); // so we don't expose the file we open/close to the caller + if( ! file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { + kWarning() << "Failed to open layout memory xml file for reading" << file.fileName() << "error:" << file.error(); + return false; + } + + MapHandler mapHandler(layoutMemory.keyboardConfig.switchingPolicy); + + QXmlSimpleReader reader; + reader.setContentHandler(&mapHandler); + reader.setErrorHandler(&mapHandler); + + QXmlInputSource xmlInputSource(&file); + kDebug() << "Restoring keyboard layout map from" << file.fileName(); + + if( ! reader.parse(xmlInputSource) ) { + kWarning() << "Failed to parse the layout memory file" << file.fileName(); + return false; + } + + if( layoutMemory.keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_GLOBAL ) { + if( mapHandler.globalLayout.isValid() && layoutMemory.keyboardConfig.layouts.contains(mapHandler.globalLayout)) { + globalLayout = mapHandler.globalLayout; + kDebug() << "Restored global layout" << globalLayout.toString(); + } + } + else { + layoutMemory.layoutMap.clear(); + foreach(const QString& key, mapHandler.layoutMap.keys()) { + if( containsAll(layoutMemory.keyboardConfig.layouts, mapHandler.layoutMap[key].layouts) ) { + layoutMemory.layoutMap.insert(key, mapHandler.layoutMap[key]); + } + } + kDebug() << "Restored layouts for" << layoutMemory.layoutMap.size() << "containers"; + } + return true; +} + +bool LayoutMemoryPersister::canPersist() { + // we can't persist per window - as we're using window id which is not preserved between sessions + bool windowMode = layoutMemory.keyboardConfig.switchingPolicy == KeyboardConfig::SWITCH_POLICY_WINDOW; + if( windowMode ) { + kDebug() << "Not saving session for window mode"; + } + return !windowMode; +} diff --git a/kcms/keyboard/layout_memory_persister.h b/kcms/keyboard/layout_memory_persister.h new file mode 100644 index 0000000000..8c4b3c5f60 --- /dev/null +++ b/kcms/keyboard/layout_memory_persister.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LAYOUT_MEMORY_PERSISTER_H_ +#define LAYOUT_MEMORY_PERSISTER_H_ + +#include + +#include "x11_helper.h" + +class LayoutMemory; +class QFile; + +class LayoutMemoryPersister { +public: + LayoutMemoryPersister(LayoutMemory& layoutMemory_): + layoutMemory(layoutMemory_) {} + + bool saveToFile(const QFile& file); + bool restoreFromFile(const QFile& file); + + bool save(const QString& moduleName); + bool restore(const QString& moduleName); + + LayoutUnit getGlobalLayout() const { return globalLayout; } + void setGlobalLayout(const LayoutUnit& layout) { globalLayout = layout; } + +private: + LayoutMemory& layoutMemory; + LayoutUnit globalLayout; + + QString getLayoutMapAsString(); + + bool canPersist(); +}; + +#endif /* LAYOUT_MEMORY_PERSISTER_H_ */ diff --git a/kcms/keyboard/layout_tray_icon.cpp b/kcms/keyboard/layout_tray_icon.cpp new file mode 100644 index 0000000000..814c5606c9 --- /dev/null +++ b/kcms/keyboard/layout_tray_icon.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "layout_tray_icon.h" + +//#include +#include +#include +#include + +#include "xkb_rules.h" +#include "x11_helper.h" +#include "xkb_helper.h" +#include "keyboard_config.h" +#include "flags.h" +#include "layouts_menu.h" + + +// +// Layout Tray Icon +// +LayoutTrayIcon::LayoutTrayIcon(const Rules* rules_, const KeyboardConfig& keyboardConfig_): + keyboardConfig(keyboardConfig_), + rules(rules_), + flags(new Flags()), + layoutsMenu(new LayoutsMenu(keyboardConfig_, *rules, *flags)) +{ + m_notifierItem = new KStatusNotifierItem(this); + m_notifierItem->setCategory(KStatusNotifierItem::SystemServices); + m_notifierItem->setStatus(KStatusNotifierItem::Active); + m_notifierItem->setToolTipTitle(i18nc("tooltip title", "Keyboard Layout")); + m_notifierItem->setTitle(i18nc("tooltip title", "Keyboard Layout")); + + KMenu* menu = new KMenu(""); + m_notifierItem->setContextMenu(menu); + m_notifierItem->setStandardActionsEnabled(false); + + layoutMapChanged(); + + m_notifierItem->setStatus(KStatusNotifierItem::Active); + + init(); +} + +LayoutTrayIcon::~LayoutTrayIcon() +{ + destroy(); + delete flags; + delete layoutsMenu; +} + +void LayoutTrayIcon::init() +{ + connect(m_notifierItem, SIGNAL(activateRequested(bool,QPoint)), this, SLOT(toggleLayout())); + connect(m_notifierItem, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(scrollRequested(int,Qt::Orientation))); + connect(flags, SIGNAL(pixmapChanged()), this, SLOT(layoutChanged())); +} + +void LayoutTrayIcon::destroy() +{ + disconnect(flags, SIGNAL(pixmapChanged()), this, SLOT(layoutChanged())); + disconnect(m_notifierItem, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(scrollRequested(int,Qt::Orientation))); + disconnect(m_notifierItem, SIGNAL(activateRequested(bool,QPoint)), this, SLOT(toggleLayout())); +} + +void LayoutTrayIcon::layoutMapChanged() +{ + flags->clearCache(); + + QMenu* menu = m_notifierItem->contextMenu(); + menu->clear(); + QList actions = layoutsMenu->contextualActions(); + menu->addActions(actions); + + layoutChanged(); +} + +void LayoutTrayIcon::layoutChanged() +{ + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + if( layoutUnit.isEmpty() ) + return; + +// QString shortText = Flags::getShortText(layoutUnit, *keyboardConfig); +// kDebug() << "systray: LayoutChanged" << layoutUnit.toString() << shortText; + QString longText = Flags::getLongText(layoutUnit, rules); + + m_notifierItem->setToolTipSubTitle(longText); + + const QIcon icon(getFlag(layoutUnit.layout)); + m_notifierItem->setToolTipIconByPixmap(icon); + + QIcon textOrIcon = flags->getIconWithText(layoutUnit, keyboardConfig); + m_notifierItem->setIconByPixmap( textOrIcon ); +} + +void LayoutTrayIcon::toggleLayout() +{ + X11Helper::switchToNextLayout(); +} + +void LayoutTrayIcon::scrollRequested(int delta, Qt::Orientation /*orientation*/) +{ + X11Helper::scrollLayouts(delta > 0 ? 1 : -1); +} + +const QIcon LayoutTrayIcon::getFlag(const QString& layout) const +{ + return keyboardConfig.isFlagShown() ? flags->getIcon(layout) : QIcon(); +} diff --git a/kcms/keyboard/layout_tray_icon.h b/kcms/keyboard/layout_tray_icon.h new file mode 100644 index 0000000000..0287e81de8 --- /dev/null +++ b/kcms/keyboard/layout_tray_icon.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef LAYOUT_TRAY_ICON_H_ +#define LAYOUT_TRAY_ICON_H_ + +#include +#include + +#include "flags.h" +#include "x11_helper.h" + +class KeyboardConfig; +class LayoutsMenu; + +/** + * System tray icon to show layouts + */ +class KStatusNotifierItem; +class QActionGroup; +class Rules; +class Flags; +class LayoutTrayIcon : public QObject +{ + Q_OBJECT + +public: + LayoutTrayIcon(const Rules* rules, const KeyboardConfig& keyboardConfig); + ~LayoutTrayIcon(); + + void layoutMapChanged(); + +public Q_SLOTS: + void layoutChanged(); + +private Q_SLOTS: + void toggleLayout(); + void scrollRequested(int, Qt::Orientation); + +private: + void init(); + void destroy(); + const QIcon getFlag(const QString& layout) const; + + const KeyboardConfig& keyboardConfig; + const Rules* rules; + Flags* flags; + KStatusNotifierItem *m_notifierItem; + LayoutsMenu* layoutsMenu; +}; + + +#endif /* LAYOUT_WIDGET_H_ */ diff --git a/kcms/keyboard/layout_widget.cpp b/kcms/keyboard/layout_widget.cpp new file mode 100644 index 0000000000..231c39cc94 --- /dev/null +++ b/kcms/keyboard/layout_widget.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "layout_widget.h" + +//#include +#include + +#include + +#include "xkb_rules.h" +#include "x11_helper.h" +#include "xkb_helper.h" +#include "keyboard_config.h" +#include "flags.h" + + +K_PLUGIN_FACTORY(LayoutWidgetFactory, registerPlugin();) +K_EXPORT_PLUGIN(LayoutWidgetFactory("keyboard_layout_widget")) + + +LayoutWidget::LayoutWidget(QWidget* parent, const QList& /*args*/): + QWidget(parent), + xEventNotifier(nullptr), + keyboardConfig(new KeyboardConfig()), + flags(new Flags()) +{ + if( ! X11Helper::xkbSupported(NULL) ) { +// setFailedToLaunch(true, "XKB extension failed to initialize"); + hide(); + return; + } + + keyboardConfig->load(); + bool show = // keyboardConfig->showIndicator && + ( keyboardConfig->showSingle || X11Helper::getLayoutsList().size() > 1 ); + if( ! show ) { + hide(); + return; + } + + widget = new QPushButton(this); + widget->setFlat(true); + + layoutChanged(); + init(); +} + +LayoutWidget::~LayoutWidget() +{ + destroy(); +} + +void LayoutWidget::init() +{ +#warning XEventNotifier needs porting to QAbstractNativeEventFilter +#if 0 + connect(widget, SIGNAL(clicked(bool)), this, SLOT(toggleLayout())); + connect(xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); + connect(xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutChanged())); + xEventNotifier->start(); +#endif +} + +void LayoutWidget::destroy() +{ +#warning XEventNotifier needs porting to QAbstractNativeEventFilter +#if 0 + xEventNotifier->stop(); + disconnect(xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(layoutChanged())); + disconnect(xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); +#endif +} + +void LayoutWidget::toggleLayout() +{ + X11Helper::switchToNextLayout(); +} + +void LayoutWidget::layoutChanged() +{ + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + if( layoutUnit.isEmpty() ) + return; + + QIcon icon; + if( keyboardConfig->isFlagShown() ) { + icon = flags->getIcon(layoutUnit.layout); + } + + QString longText = Flags::getLongText(layoutUnit, NULL); + if( ! icon.isNull() ) { + widget->setIcon(icon); + widget->setText(""); + widget->setToolTip(longText); + } + else { + QString shortText = Flags::getShortText(layoutUnit, *keyboardConfig); + widget->setIcon(icon); + widget->setText(shortText); + widget->setToolTip(longText); +// widget->setShortcut(QKeySequence()); + } +} + +#include "layout_widget.moc" diff --git a/kcms/keyboard/layout_widget.h b/kcms/keyboard/layout_widget.h new file mode 100644 index 0000000000..1054cae86c --- /dev/null +++ b/kcms/keyboard/layout_widget.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef LAYOUT_WIDGET_H_ +#define LAYOUT_WIDGET_H_ + +#include +#include + +#include "flags.h" +#include "x11_helper.h" + +class QPushButton; +class KeyboardConfig; +class LayoutsMenu; +class XEventNotifier; + +/** + * Note: does not listen to configuration changes as currently we only use it in screen lock dialog + */ +class LayoutWidget : public QWidget +{ + Q_OBJECT + +public: + explicit LayoutWidget(QWidget* parent = 0, const QList& args = QList()); + virtual ~LayoutWidget(); + +private Q_SLOTS: + void toggleLayout(); + void layoutChanged(); + // void configChanged(); + +private: + void init(); + void destroy(); + + XEventNotifier* xEventNotifier; + QPushButton* widget; + KeyboardConfig* keyboardConfig; + Flags* flags; +}; + +#endif /* LAYOUT_WIDGET_H_ */ diff --git a/kcms/keyboard/layouts_menu.cpp b/kcms/keyboard/layouts_menu.cpp new file mode 100644 index 0000000000..af58e9bcef --- /dev/null +++ b/kcms/keyboard/layouts_menu.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "layouts_menu.h" + +#include +#include +#include +#include + +#include +#include + +#include "keyboard_config.h" +#include "x11_helper.h" +#include "xkb_helper.h" +#include "flags.h" + + +LayoutsMenu::LayoutsMenu(const KeyboardConfig& keyboardConfig_, const Rules& rules_, Flags& flags_): + keyboardConfig(keyboardConfig_), + rules(rules_), + flags(flags_), + actionGroup(NULL) +{ +} + +LayoutsMenu::~LayoutsMenu() +{ + delete actionGroup; +} + +const QIcon LayoutsMenu::getFlag(const QString& layout) const +{ + return keyboardConfig.isFlagShown() ? flags.getIcon(layout) : QIcon(); +} + +void LayoutsMenu::actionTriggered(QAction* action) +{ + QString data = action->data().toString(); + if( data == "config" ) { + QStringList args; + args << "--args=--tab=layouts"; + args << "kcm_keyboard"; + KToolInvocation::kdeinitExec("kcmshell4", args); + } + else { + LayoutUnit layoutUnit(LayoutUnit(action->data().toString())); + switchToLayout(layoutUnit, keyboardConfig); + } +} + +int LayoutsMenu::switchToLayout(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig) +{ + QList layouts = X11Helper::getCurrentLayouts().layouts; + + bool res; + if( layouts.contains(layoutUnit) ) { + res = X11Helper::setLayout(layoutUnit); + } + else if ( keyboardConfig.isSpareLayoutsEnabled() && keyboardConfig.layouts.contains(layoutUnit) ) { + QList layouts(keyboardConfig.getDefaultLayouts()); + layouts.removeLast(); + layouts.append(layoutUnit); + XkbHelper::initializeKeyboardLayouts(layouts); + res = X11Helper::setLayout(layoutUnit); + } + else { + kWarning() << "switchToLayout with unknown layout" << layoutUnit.toString(); + res = -1; + } + return res; +} + +QAction* LayoutsMenu::createAction(const LayoutUnit& layoutUnit) const +{ + QString menuText = Flags::getFullText(layoutUnit, keyboardConfig, &rules); + QAction* action = new QAction(getFlag(layoutUnit.layout), menuText, actionGroup); + action->setData(layoutUnit.toString()); + //FIXME: tooltips don't work on dbusmenus??? +// if( ! layoutUnit.getShortcut().isEmpty() ) { +// action->setToolTip(layoutUnit.getShortcut().toString()); +// } + return action; +} + +QList LayoutsMenu::contextualActions() +{ + if( actionGroup ) { + disconnect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); + delete actionGroup; + } + actionGroup = new QActionGroup(this); + + X11Helper::getLayoutsList(); //UGLY: seems to be more reliable with extra call + QList currentLayouts = X11Helper::getLayoutsList(); + foreach(const LayoutUnit& layoutUnit, currentLayouts) { + QAction* action = createAction(layoutUnit); + actionGroup->addAction(action); + } + + if( keyboardConfig.configureLayouts ) { + QList extraLayouts = keyboardConfig.layouts; + foreach(const LayoutUnit& layoutUnit, currentLayouts) { + extraLayouts.removeOne(layoutUnit); + } + if( extraLayouts.size() > 0 ) { + QAction* separator = new QAction(actionGroup); + separator->setSeparator(true); + actionGroup->addAction(separator); + + foreach(const LayoutUnit& layoutUnit, extraLayouts) { + QAction* action = createAction(layoutUnit); + actionGroup->addAction(action); + } + } + } + + QAction* separator = new QAction(actionGroup); + separator->setSeparator(true); + actionGroup->addAction(separator); + QAction* configAction = new QAction(i18n("Configure..."), actionGroup); + actionGroup->addAction(configAction); + configAction->setData("config"); + connect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); + return actionGroup->actions(); +} diff --git a/kcms/keyboard/layouts_menu.h b/kcms/keyboard/layouts_menu.h new file mode 100644 index 0000000000..db2f3ff584 --- /dev/null +++ b/kcms/keyboard/layouts_menu.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LAYOUTS_MENU_H_ +#define LAYOUTS_MENU_H_ + +#include +#include + +class QAction; +class KeyboardConfig; +class Flags; +class Rules; +class QActionGroup; +class LayoutUnit; + +class LayoutsMenu : public QObject +{ + Q_OBJECT + +public: + LayoutsMenu(const KeyboardConfig& keyboardConfig, const Rules& rules, Flags& flags); + virtual ~LayoutsMenu(); + + QList contextualActions(); + static int switchToLayout(const LayoutUnit& layoutUnit, const KeyboardConfig& keyboardConfig); + +private Q_SLOTS: + void actionTriggered(QAction* action); + +private: + const QIcon getFlag(const QString& layout) const; + QAction* createAction(const LayoutUnit& layoutUnit) const; + + const KeyboardConfig& keyboardConfig; + const Rules& rules; + Flags& flags; + QActionGroup* actionGroup; +}; + +#endif /* LAYOUTS_MENU_H_ */ diff --git a/kcms/keyboard/numlockx.c b/kcms/keyboard/numlockx.c new file mode 100644 index 0000000000..0ac7cfbd9c --- /dev/null +++ b/kcms/keyboard/numlockx.c @@ -0,0 +1,305 @@ +/**************************************************************************** + + NumLockX + + $Id: main.c,v 1.10 2001/04/30 20:55:20 seli Exp $ + + Copyright (C) 2000-2001 Lubos Lunak + Copyright (C) 2001 Oswald Buddenhagen + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include + +#include + +#include + +#ifdef HAVE_XTEST +#include +#endif + +#ifdef HAVE_XKB +#include +#endif + +#include + +static +Display* dpy; + +/* the XKB stuff is based on code created by Oswald Buddenhagen */ +#ifdef HAVE_XKB +static +int xkb_init() + { + int xkb_opcode, xkb_event, xkb_error; + int xkb_lmaj = XkbMajorVersion; + int xkb_lmin = XkbMinorVersion; + return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) + && XkbQueryExtension( dpy, &xkb_opcode, &xkb_event, &xkb_error, + &xkb_lmaj, &xkb_lmin ); + } + +static +unsigned int xkb_mask_modifier( XkbDescPtr xkb, const char *name ) + { + int i; + if( !xkb || !xkb->names ) + return 0; + for( i = 0; + i < XkbNumVirtualMods; + i++ ) + { + char* modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] ); + if( modStr != NULL && strcmp(name, modStr) == 0 ) + { + unsigned int mask; + XkbVirtualModsToReal( xkb, 1 << i, &mask ); + return mask; + } + } + return 0; + } + +static +unsigned int xkb_numlock_mask() + { + XkbDescPtr xkb; + if(( xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd )) != NULL ) + { + unsigned int mask = xkb_mask_modifier( xkb, "NumLock" ); + XkbFreeKeyboard( xkb, 0, True ); + return mask; + } + return 0; + } + +static +int xkb_set_on() + { + unsigned int mask; + if( !xkb_init()) + return 0; + mask = xkb_numlock_mask(); + if( mask == 0 ) + return 0; + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, mask); + return 1; + } + +static +int xkb_set_off() + { + unsigned int mask; + if( !xkb_init()) + return 0; + mask = xkb_numlock_mask(); + if( mask == 0 ) + return 0; + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, 0); + return 1; + } + +#ifdef NUMLOCKX_STANDALONE +static +int xkb_toggle() + { + unsigned int mask; + unsigned int numlockState; + XkbStateRec xkbState; + if( !xkb_init()) + return 0; + mask = xkb_numlock_mask(); + if( mask == 0 ) + return 0; + XkbGetState( dpy, XkbUseCoreKbd, &xkbState); + numlockState = xkbState.locked_mods & mask; + if (numlockState) + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, 0); + else + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, mask); + return 1; + } +#endif + +#endif + +#ifdef HAVE_XTEST +static +int xtest_get_numlock_state() + { + int i; + int numlock_mask = 0; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + unsigned int mask; + XModifierKeymap* map = XGetModifierMapping( dpy ); + KeyCode numlock_keycode = XKeysymToKeycode( dpy, XK_Num_Lock ); + if( numlock_keycode == NoSymbol ) + return 0; + for( i = 0; + i < 8; + ++i ) + { + if( map->modifiermap[ map->max_keypermod * i ] == numlock_keycode ) + numlock_mask = 1 << i; + } + XQueryPointer( dpy, DefaultRootWindow( dpy ), &dummy1, &dummy2, + &dummy3, &dummy4, &dummy5, &dummy6, &mask ); + XFreeModifiermap( map ); + return mask & numlock_mask; + } + +static +void xtest_change_numlock() + { + XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, XK_Num_Lock ), True, CurrentTime ); + XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, XK_Num_Lock ), False, CurrentTime ); + } + +static +void xtest_set_on() + { + if( !xtest_get_numlock_state()) + xtest_change_numlock(); + } + +static +void xtest_set_off() + { + if( xtest_get_numlock_state()) + xtest_change_numlock(); + } + +#ifdef NUMLOCKX_STANDALONE +static +void xtest_toggle() + { + xtest_change_numlock(); + } +#endif + +#endif + +static +void numlock_set_on() + { +#ifdef HAVE_XKB + if( xkb_set_on()) + return; +#endif +#ifdef HAVE_XTEST + xtest_set_on(); +#endif + } + +static +void numlock_set_off() + { +#ifdef HAVE_XKB + if( xkb_set_off()) + return; +#endif +#ifdef HAVE_XTEST + xtest_set_off(); +#endif + } + + +#ifndef NUMLOCKX_STANDALONE + +void numlockx_change_numlock_state(Display* dpy_, int state) +{ +#ifndef HAVE_XTEST +#ifdef __GNUC__ + #warning "XTEST extension not found - numlock setting may not work reliably" +#endif +#endif + + dpy = dpy_; + if( state ) { + numlock_set_on(); + } + else { + numlock_set_off(); + } +} + +#else + +static +void numlock_toggle() + { +#ifdef HAVE_XKB + if( xkb_toggle()) + return; +#endif +#ifdef HAVE_XTEST + xtest_toggle(); +#endif + } + +void usage( const char* argv0 ) + { + printf( "NumLockX " VERSION "\n" + "(C) 2000-2001 Lubos Lunak \n" + "(C) 2001 Oswald Buddenhagen \n\n" + "Usage : %s [on|off]\n" + "on - turns NumLock on in X ( default )\n" + "off - turns NumLock off in X\n" + "toggle - toggles the NumLock on and off in X\n" + "\n" + , argv0 ); + } + +int main( int argc, char* argv[] ) + { + if( argc > 2 ) + { + usage( argv[ 0 ] ); + return 1; + } + dpy = XOpenDisplay( NULL ); + if( dpy == NULL ) + { + fprintf( stderr, "Error opening display!\n" ); + return 1; + } + if( argc == 1 ) + numlock_set_on(); + else if( strcmp( argv[ 1 ], "on" ) == 0 ) + numlock_set_on(); + else if( strcmp( argv[ 1 ], "off" ) == 0 ) + numlock_set_off(); + else if( strcmp( argv[ 1 ], "toggle" ) == 0 ) + numlock_toggle(); + else + { + usage( argv[ 0 ] ); + XCloseDisplay( dpy ); + return 2; + } + XCloseDisplay( dpy ); + return 0; + } + +#endif diff --git a/kcms/keyboard/pics/CMakeLists.txt b/kcms/keyboard/pics/CMakeLists.txt new file mode 100644 index 0000000000..6d5b7cae94 --- /dev/null +++ b/kcms/keyboard/pics/CMakeLists.txt @@ -0,0 +1 @@ +install( FILES epo.png DESTINATION ${DATA_INSTALL_DIR}/kcmkeyboard/pics ) diff --git a/kcms/keyboard/pics/epo.png b/kcms/keyboard/pics/epo.png new file mode 100644 index 0000000000..a8ed294381 Binary files /dev/null and b/kcms/keyboard/pics/epo.png differ diff --git a/kcms/keyboard/plasma_applet_keyboard.desktop b/kcms/keyboard/plasma_applet_keyboard.desktop new file mode 100644 index 0000000000..37ca201dde --- /dev/null +++ b/kcms/keyboard/plasma_applet_keyboard.desktop @@ -0,0 +1,165 @@ +[Desktop Entry] +Name=Keyboard Layout +Name[af]=Sleutelbord Uitleg +Name[ar]=تخطيط لوحة المفاتيح +Name[ast]=Disposición del tecláu +Name[be]=Раскладка клавіятуры +Name[be@latin]=Vykład klavijatury +Name[bg]=Клавиатурни подредби +Name[bn]=কীবোর্ড বিন্যাস +Name[bn_IN]=কি-বোর্ড বিন্যাস +Name[br]=Reizhadur ar stokellaoueg +Name[bs]=Raspored tastera +Name[ca]=Disposició del teclat +Name[ca@valencia]=Disposició del teclat +Name[cs]=Rozvržení klávesnice +Name[csb]=Ùstôw klawiaturë +Name[cy]=Cynllun Bysellfwrdd +Name[da]=Tastaturlayout +Name[de]=Tastaturbelegung +Name[el]=Διάταξη πληκτρολογίου +Name[en_GB]=Keyboard Layout +Name[eo]=Klavararanĝo +Name[es]=Disposición del teclado +Name[et]=Klaviatuuri paigutus +Name[eu]=Teklatu-diseinua +Name[fa]=طرح‌بندی صفحه کلید +Name[fi]=Näppäimistön järjestys +Name[fr]=Disposition du clavier +Name[fy]=Toetseboerdyndieling +Name[ga]=Leagan Amach Méarchláir +Name[gl]=Disposición do teclado +Name[gu]=કીબોર્ડ દેખાવ +Name[he]=פריסת מקלדת +Name[hi]=कुंजीपट ख़ाका +Name[hne]=कुंजीपट खाका +Name[hr]=Raspored tipkovnice +Name[hsb]=Tastaturowy layout +Name[hu]=Billentyűzetkiosztás +Name[ia]=Disposition de claviero +Name[id]=Tata Letak Papan Ketik +Name[is]=Lyklaborð +Name[it]=Mappatura della tastiera +Name[ja]=キーボード配列 +Name[ka]=კლავიატურის განლაგება +Name[kk]=Перенетақта сәйкестігі +Name[km]=ប្លង់​ក្ដារចុច +Name[kn]=ಕೀಲಿಮಣೆ ವಿನ್ಯಾಸ +Name[ko]=키보드 레이아웃 +Name[ku]=Rengê Klavyeyê +Name[lt]=Klaviatūros išdėstymas +Name[lv]=Tastatūras izkārtojums +Name[mai]=कीबोर्ड लेआउट +Name[mk]=Распоред на тастатура +Name[ml]=കീബോര്‍ഡ് വിന്യാസം +Name[mr]=कळफलक रचना +Name[nb]=Tastaturoppsett +Name[nds]=Tasttoornen +Name[ne]=कुञ्जीपाटी सजावट +Name[nl]=Toetsenbordindeling +Name[nn]=Taste­oppsett +Name[oc]=Disposicion del clavièr +Name[or]=କିବୋର୍ଡ଼ ବିନ୍ୟାସ +Name[pa]=ਕੀ-ਬੋਰਡ ਲੇਆਉਟ +Name[pl]=Układ klawiatury +Name[pt]=Disposição do Teclado +Name[pt_BR]=Leiaute do teclado +Name[ro]=Aranjament de tastatură +Name[ru]=Раскладка клавиатуры +Name[se]=Boallobeavdehápmi +Name[si]=යතුරුපුවරු ආකෘතිය +Name[sk]=Rozloženie klávesnice +Name[sl]=Razporeditev tipk +Name[sr]=Распоред тастера +Name[sr@ijekavian]=Распоред тастера +Name[sr@ijekavianlatin]=Raspored tastera +Name[sr@latin]=Raspored tastera +Name[sv]=Tangentbordslayout +Name[ta]=விசைப்பலகை உருவரை +Name[te]=కీబోర్డ్ కూర్పు +Name[tg]=Тугмабандии клавиатура +Name[th]=ผังแป้นพิมพ์ +Name[tr]=Klavye Düzeni +Name[ug]=ھەرپتاختا ئورۇنلاشتۇرۇلۇشى +Name[uk]=Розкладка клавіатури +Name[uz]=Tugmalar tartibi +Name[uz@cyrillic]=Тугмалар тартиби +Name[vi]=Bố trí bàn phím +Name[wa]=Adjinçmint del taprece +Name[xh]=Ubeko Lwebhodi yezitshixo +Name[x-test]=xxKeyboard Layoutxx +Name[zh_CN]=键盘布局 +Name[zh_TW]=鍵盤配置 +Comment=View and switch between active keyboard layouts +Comment[ar]=إعرض وبدل بين مخططات لوحة المفاتيح +Comment[ast]=Ver y camudar ente les distribuciones de tecláu actives +Comment[bg]=Преглед и превключване на клавиатурната подредба +Comment[bn]=সক্রিয় কীবোর্ড বিন্যাসগুলি দেখুন এবং অদলবদল করুন +Comment[bs]=Prikaz i prebacivanje između aktivnih rasporeda tastature +Comment[ca]=Vista i commutació entre disposicions actives de teclat +Comment[ca@valencia]=Vista i commutació entre disposicions actives de teclat +Comment[cs]=Zobrazí a přepne mezi aktivními rozloženími klávesnice +Comment[da]=Vis og skift mellem aktive tastaturlayouts +Comment[de]=Aktive Tastaturbelegungen anzeigen und zwischen ihnen umschalten +Comment[el]=Εμφάνιση και εναλλαγή μεταξύ των ενεργών διατάξεων πληκτρολογίου +Comment[en_GB]=View and switch between active keyboard layouts +Comment[es]=Ver y cambiar entre las distribuciones de teclado activas +Comment[et]=Aktiivsete klaviatuuripaigutuste näitamine ja nende vahel lülitamine +Comment[eu]=Ikusi eta aldatu teklatu-diseinu aktibo batetik bestera +Comment[fi]=Katsele ja vaihda aktiivisten näppäinasetteluiden välillä +Comment[fr]=Affiche et change des dispositions actives de clavier +Comment[gl]=Mostra e troca a disposición de teclado activa +Comment[he]=משמש להצגת והחלפה בין פריסות מקלדת +Comment[hr]=Prikaži i prebaci između aktivnih tipkovničkih rasporeda +Comment[hu]=Aktív billentyűzetkiosztások megjelenítése és váltás köztük +Comment[ia]=Monstra e commuta inter dispositiones de claviero active +Comment[is]=Skoða og skipta á milli virkra lyklaborðsframsetninga +Comment[it]=Visualizza e cambia le mappature della tastiera attive +Comment[ja]=アクティブキーボードレイアウトを表示して切り替える +Comment[kk]=Белсенді пернетақталарды көру және біріне ауысу +Comment[km]=មើល ហើយ​ប្ដូរ​ប្លង់​ក្ដារចុច​សកម្ម +Comment[ko]=활성 키보드 레이아웃을 보고 바꾸기 +Comment[lt]=Peržiūrėti ir persijungti tarp aktyvių klaviatūros maketų +Comment[lv]=Skatīt un pārslēgties starp aktīvajiem tastatūras izkārtojumumiem +Comment[mr]=चालू कळफलक रचना बघा व बदला +Comment[nb]=Vis aktive tastaturutforminger og bytt mellom dem +Comment[nds]=Aktiv Tasttoornen ankieken un wesseln +Comment[nl]=Bekijk en schakel tussen actieve toetsenbordindelingen +Comment[nn]=Vis og byt mellom aktive tastaturoppsett +Comment[pa]=ਸਰਗਰਮ ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖੋ ਅਤੇ ਬਦਲੋ +Comment[pl]=Przeglądanie i przełączanie między aktywnymi układami klawiatury +Comment[pt]=Ver e mudar a disposição de teclado activa +Comment[pt_BR]=Visualiza e troca o leiaute de teclado ativo +Comment[ro]=Vizualizează și schimbă între aranjamentele de tastatură active +Comment[ru]=Просмотр и переключение между активными раскладками клавиатуры +Comment[sk]=Zobrazenie a prepínanie medzi aktívnymi rozloženiami klávesnice +Comment[sl]=Oglejte si in preklopite med dejavnimi razporeditvami tipk +Comment[sr]=Приказ и пребацивање између активних распореда тастатуре +Comment[sr@ijekavian]=Приказ и пребацивање између активних распореда тастатуре +Comment[sr@ijekavianlatin]=Prikaz i prebacivanje između aktivnih rasporeda tastature +Comment[sr@latin]=Prikaz i prebacivanje između aktivnih rasporeda tastature +Comment[sv]=Visa och byt mellan aktiva tangentbordslayouter +Comment[th]=ดูและสลับการจัดวางแป้นพิมพ์ที่ใช้งานอยู่ +Comment[tr]=Etkin klavye düzenlerini görüntüle ve geçiş yap +Comment[ug]=ئاكتىپ ھەرپتاختا جايلاشتۇرۇشى ئارىسىدا ئالماشتۇر ۋە كۆرسەت +Comment[uk]=Перегляд і перемикання між активними розкладками клавіатури +Comment[wa]=Vey eyet discandjî etur les adjinçmints del taprece ovrants +Comment[x-test]=xxView and switch between active keyboard layoutsxx +Comment[zh_CN]=查看和切换键盘布局 +Comment[zh_TW]=檢視並在可用的鍵盤佈局中切換 + +Icon=preferences-desktop-keyboard +Type=Service + +X-KDE-ServiceTypes=Plasma/Applet +X-KDE-Library=plasma_applet_keyboard +X-KDE-PluginInfo-Author=Andriy Rysin +X-KDE-PluginInfo-Email=rysin@kde.org +X-KDE-PluginInfo-Name=keyboard_applet +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category=System Information +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-Plasma-NotificationArea=true diff --git a/kcms/keyboard/preview/TODO b/kcms/keyboard/preview/TODO new file mode 100644 index 0000000000..784924fe82 --- /dev/null +++ b/kcms/keyboard/preview/TODO @@ -0,0 +1,10 @@ +Important: +* display tooltip for each key with key description from symbol file + +Good to have: +* read keyboard geometry and show it properly instead of default keyboard +* use some common parser to parse symbol and geometry descriptions +* replace symkey2ucs with something more reliable (e.g. XLookupString?) + +Cleanup: +* clean up the code (especially kbpreviewframe.cpp) diff --git a/kcms/keyboard/preview/kbpreviewframe.cpp b/kcms/keyboard/preview/kbpreviewframe.cpp new file mode 100644 index 0000000000..f9cc5a3e58 --- /dev/null +++ b/kcms/keyboard/preview/kbpreviewframe.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "kbpreviewframe.h" + +#include +#include + +#include +#include + + +static const QColor keyBorderColor("#d4d4d4"); +static const QColor lev12color("#d4d4d4"); +static const QColor lev34color("#FF3300"); +static const int sz=20, kszx=70, kszy=70; + +static const int xOffset[] = {15, 15, 40, 40 }; +static const int yOffset[] = {10, 40, 10, 40 }; +static const QColor color[] = { lev12color, lev12color, lev34color, lev34color }; + + +// TODO: parse geometry files and display keyboard according to current keyboard model + +KbPreviewFrame::KbPreviewFrame(QWidget *parent) : + QFrame(parent) +{ + setFrameStyle( QFrame::Box ); + setFrameShadow(QFrame::Sunken); +} + +void KbPreviewFrame::paintTLDE(QPainter &painter,int &x,int &y) +{ + painter.setPen(keyBorderColor); + painter.drawRect(x, y, kszx, kszy); + + const QList symbols = keyboardLayout.TLDE.symbols; + + for(int level=0; level symbols = keyboardLayout.AE[i].symbols; + + for(int level=0; level")); + x+=tabszx; + + + for(int i=0; i symbols = keyboardLayout.AD[i].symbols; + + painter.setPen(keyBorderColor); + painter.drawRect(x, y,kszx,kszy); + + for(int level=0; level symbols = keyboardLayout.BKSL.symbols; + + for(int level=0; level symbols = keyboardLayout.AC[i].symbols; + + for(int level=0; level symbols = keyboardLayout.AB[i].symbols; + + for(int level=0; level symstr = content.split("xkb_symbols "); + + if( layoutVariant.isEmpty() ) { + keyboardLayout.generateLayout(symstr.at(1), layout); + } + else { + for(int i=1;i +#include + +class KbPreviewFrame : public QFrame +{ + Q_OBJECT + +private: + void paintTLDE(QPainter &painter, int &x, int &y); + void paintAERow(QPainter &painter, int &x, int &y); + void paintADRow(QPainter &painter, int &x, int &y); + void paintACRow(QPainter &painter, int &x, int &y); + void paintABRow(QPainter &painter, int &x, int &y); + void paintBottomRow(QPainter &painter, int &x, int &y); + void paintFnKeys(QPainter &painter, int &x, int &y); + + KeySymHelper symbol; + Aliases alias; + KeyboardLayout keyboardLayout; + +public: + explicit KbPreviewFrame(QWidget *parent = 0); + + void paintEvent(QPaintEvent * event); + void generateKeyboardLayout(const QString &country, const QString &layoutVariant); + QString getLayoutName() const { + return keyboardLayout.getLayoutName(); + } +}; + +#endif // KBPREVIEWFRAME_H diff --git a/kcms/keyboard/preview/keyaliases.cpp b/kcms/keyboard/preview/keyaliases.cpp new file mode 100644 index 0000000000..71d6a00804 --- /dev/null +++ b/kcms/keyboard/preview/keyaliases.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keyaliases.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +Aliases::Aliases() +{ + QString filename=findaliasdir(); + QFile file(filename); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString content = file.readAll(); + file.close(); + QListals; + als=content.split("xkb_keycodes"); + for(int i=1;i"); + temp=temp.remove("<"); + temp=temp.remove(";"); + temp=temp.remove("}"); + temp=temp.remove("{"); + QListalskeys; + alskeys=temp.split("alias"); + if(temp.startsWith("qwerty")){ + for(int k=1;k= 3 ) { + // .../usr/lib/X11 -> /usr/share/X11/xkb vs .../usr/X11/lib -> /usr/X11/share/X11/xkb + QString delta = base.endsWith("X11") ? "/../../share/X11" : "/../share/X11"; + QDir baseDir(base + delta); + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + else { + QDir baseDir(base + "/X11"); // .../usr/X11/lib/X11/xkb (old XFree) + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + } + } + + if( xkbParentDir.isEmpty() ) { + xkbParentDir = "/usr/share/X11"; + } + aliasdir=QString("%1/xkb/keycodes/aliases").arg(xkbParentDir); + return(aliasdir); +} diff --git a/kcms/keyboard/preview/keyaliases.h b/kcms/keyboard/preview/keyaliases.h new file mode 100644 index 0000000000..3769e4eb29 --- /dev/null +++ b/kcms/keyboard/preview/keyaliases.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef ALIASES_H +#define ALIASES_H + +#include + +class Aliases +{ +private: + QMapqwerty; + QMapazerty; + QMapqwertz; + QString findaliasdir(); +public: + Aliases(); + QString getAlias(const QString &type, const QString &name); +}; + +#endif // ALIASES_H diff --git a/kcms/keyboard/preview/keyboardlayout.cpp b/kcms/keyboard/preview/keyboardlayout.cpp new file mode 100644 index 0000000000..9be0483cd5 --- /dev/null +++ b/kcms/keyboard/preview/keyboardlayout.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "keyboardlayout.h" +#include "keysymbols.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +//TODO: replace this with grammar parser (e.g. antlr) + + +KeyboardLayout::KeyboardLayout() +{ +} + +void KeyboardLayout::generateLayout(QString a,const QString& cname) +{ + includeSymbol(a,cname); + int i=a.indexOf("name[Group1]="); + i+=13; + + QString n=a.mid(i); + n=n.simplified(); + i=n.indexOf("\"",1); + layoutName=n.left(i); + layoutName.remove("\""); + layoutName.simplified(); + i=n.indexOf("key"); + n=n.mid(i); + + QList st; + st=n.split("key"); + + KeySymbols dum; + QString r,y; + + for(int k=0;ktobeinclude; + tobeinclude=a.split("include"); + + QString r; + for(int o=1;o incfile; + incfile=incsym.split("("); + for(int i=0;i symstrlist; + + symstrlist=content.split("xkb_symbols "); + for(int u=1;u= 3 ) { + // .../usr/lib/X11 -> /usr/share/X11/xkb vs .../usr/X11/lib -> /usr/X11/share/X11/xkb + QString delta = base.endsWith("X11") ? "/../../share/X11" : "/../share/X11"; + QDir baseDir(base + delta); + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + else { + QDir baseDir(base + "/X11"); // .../usr/X11/lib/X11/xkb (old XFree) + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + } + } + + if( xkbParentDir.isEmpty() ) { + xkbParentDir = "/usr/share/X11"; + } + + return QString("%1/xkb/symbols/").arg(xkbParentDir); +} diff --git a/kcms/keyboard/preview/keyboardlayout.h b/kcms/keyboard/preview/keyboardlayout.h new file mode 100644 index 0000000000..2d0c0883ca --- /dev/null +++ b/kcms/keyboard/preview/keyboardlayout.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KEYBOARDLAYOUT_H +#define KEYBOARDLAYOUT_H + +#include "keysymbols.h" +#include "keyaliases.h" + +#include + +class KeyboardLayout +{ + QString layoutName; + Aliases alias; + +public: + KeyboardLayout(); + + KeySymbols TLDE; + KeySymbols BKSL; + KeySymbols AE[12]; + KeySymbols AD[12]; + KeySymbols AC[11]; + KeySymbols AB[11]; + + void generateLayout(QString a, const QString &cname); + QString findSymbolBaseDir(); + void includeSymbol(QString a, const QString &cname); + QString getLayoutName() const { + return layoutName; + } +}; + +#endif // KEYBOARDLAYOUT_H diff --git a/kcms/keyboard/preview/keyboardpainter.cpp b/kcms/keyboard/preview/keyboardpainter.cpp new file mode 100644 index 0000000000..268fcb0d0e --- /dev/null +++ b/kcms/keyboard/preview/keyboardpainter.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keyboardpainter.h" + +#include +#include +#include + +#include + + +KeyboardPainter::KeyboardPainter(): + kbframe(new KbPreviewFrame(this)), + exitButton(new QPushButton(i18n("Close"),this)) +{ + kbframe->setFixedSize( 1030, 490 ); + exitButton->setFixedSize(120, 30); + + QVBoxLayout* vLayout = new QVBoxLayout( this ); + vLayout->addWidget(kbframe); + vLayout->addWidget(exitButton); + + connect(exitButton, SIGNAL(clicked()), this, SLOT(close())); + + setWindowTitle(kbframe->getLayoutName()); +} + +void KeyboardPainter::generateKeyboardLayout(const QString& layout, const QString& variant) +{ + kbframe->generateKeyboardLayout(layout, variant); +} + +KeyboardPainter::~KeyboardPainter() +{ + delete kbframe; + delete exitButton; +} diff --git a/kcms/keyboard/preview/keyboardpainter.h b/kcms/keyboard/preview/keyboardpainter.h new file mode 100644 index 0000000000..dde7b38099 --- /dev/null +++ b/kcms/keyboard/preview/keyboardpainter.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KEYBOARDPAINTER_H +#define KEYBOARDPAINTER_H + +#include "kbpreviewframe.h" + +#include + +class QPushButton; + +class KeyboardPainter : public QDialog +{ + Q_OBJECT + +public: + explicit KeyboardPainter(); + ~KeyboardPainter(); + + void generateKeyboardLayout(const QString& layout, const QString& variant); + +private: + KbPreviewFrame *kbframe; + QPushButton *exitButton; +}; + +#endif // KEYBOARDPAINTER_H diff --git a/kcms/keyboard/preview/keysym2ucs.cpp b/kcms/keyboard/preview/keysym2ucs.cpp new file mode 100644 index 0000000000..37afb082b6 --- /dev/null +++ b/kcms/keyboard/preview/keysym2ucs.cpp @@ -0,0 +1,899 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* $XFree86$ + * This module converts keysym values into the corresponding ISO 10646-1 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U+00000000 to + * U+00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U+10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x1000abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn , University of Cambridge, June 1999 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + */ + +#include + +struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ad, 0x0491 }, /* Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06bd, 0x0490 }, /* Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ RADICAL SYMBOL BOTTOM */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ LEFT SQUARE BRACKET UPPER CORNER */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ LEFT SQUARE BRACKET LOWER CORNER */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ RIGHT SQUARE BRACKET UPPER CORNER */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ RIGHT SQUARE BRACKET LOWER CORNER */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ LEFT PARENTHESIS UPPER HOOK */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ LEFT PARENTHESIS LOWER HOOK */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ RIGHT PARENTHESIS UPPER HOOK */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ RIGHT PARENTHESIS LOWER HOOK */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ LEFT CURLY BRACKET MIDDLE PIECE */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ RIGHT CURLY BRACKET MIDDLE PIECE */ +/* 0x08b1 topleftsummation ? ??? */ +/* 0x08b2 botleftsummation ? ??? */ +/* 0x08b3 topvertsummationconnector ? ??? */ +/* 0x08b4 botvertsummationconnector ? ??? */ +/* 0x08b5 toprightsummation ? ??? */ +/* 0x08b6 botrightsummation ? ??? */ +/* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ + { 0x09df, 0x2422 }, /* blank ␢ BLANK SYMBOL */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space   FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace   FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace   PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace   THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace   HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ + { 0x0aac, 0x2423 }, /* signifblank ␣ OPEN BOX */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ + { 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ +/* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ +/* 0x0ad5 permille ? ??? */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ +/* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ + { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ + { 0x0dde, 0x0e3e }, /* Thai_maihanakat_maitho ฾ ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ + { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ + + /* Combining symbols */ + { 0xfe50, 0x0300 }, /* dead_grave */ + { 0xfe51, 0x0301 }, /* dead_acute" */ + { 0xfe52, 0x0302 }, /* dead_circumflex" */ + { 0xfe53, 0x0303 }, /* dead_tilde" */ + { 0xfe54, 0x0304 }, /* dead_macron" */ + { 0xfe55, 0x0306 }, /* dead_breve" */ + { 0xfe56, 0x0307 }, /* dead_abovedot" */ + { 0xfe57, 0x0308 }, /* dead_diaeresis" */ + { 0xfe58, 0x030A }, /* dead_abovering" */ + { 0xfe59, 0x030B }, /* dead_doubleacute" */ + { 0xfe5a, 0x030C }, /* dead_caron" */ + { 0xfe5b, 0x0327 }, /* dead_cedilla" */ + { 0xfe5c, 0x0328 }, /* dead_ogonek" */ + { 0xfe60, 0x0323 }, /* dead_belowdot */ + + /* Special function keys. */ + + { 0xff08, 0x0008 }, /* XK_BackSpace */ + { 0xff09, 0x0009 }, /* XK_Tab */ + { 0xff0a, 0x000a }, /* XK_Linefeed */ + { 0xff0d, 0x000d }, /* XK_Return */ + { 0xff13, 0x0013 }, /* XK_Pause */ + { 0xff1b, 0x001b }, /* XK_Escape */ + { 0xff50, 0x0001 }, /* XK_Home */ + { 0xff51, 0x001c }, /* XK_Left */ + { 0xff52, 0x001e }, /* XK_Up */ + { 0xff53, 0x001d }, /* XK_Right */ + { 0xff54, 0x001f }, /* XK_Down */ + { 0xff55, 0x000b }, /* XK_Prior */ + { 0xff56, 0x000c }, /* XK_Next */ + { 0xff57, 0x0004 }, /* XK_End */ + { 0xff6a, 0x0005 }, /* XK_Help */ + { 0xffff, 0x007f }, /* XK_Delete */ +}; + +long keysym2ucs(KeySym keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + /* binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else { + /* found it */ + return keysymtab[mid].ucs; + } + } + + /* no matching Unicode value found */ + return -1; +} diff --git a/kcms/keyboard/preview/keysym2ucs.h b/kcms/keyboard/preview/keysym2ucs.h new file mode 100644 index 0000000000..6e50c76452 --- /dev/null +++ b/kcms/keyboard/preview/keysym2ucs.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Andriy Rysin (arysin@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KEYSYM2UCS_H +#define KEYSYM2UCS_H + +#include +#include + +extern long keysym2ucs(KeySym keysym); + +#endif diff --git a/kcms/keyboard/preview/keysymbols.cpp b/kcms/keyboard/preview/keysymbols.cpp new file mode 100644 index 0000000000..cea16aeb33 --- /dev/null +++ b/kcms/keyboard/preview/keysymbols.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "keysymbols.h" + +#include +#include + + +static const int MAX_GROUPS_SUPPORTED = 4; + +KeySymbols::KeySymbols() +{ +} + +void KeySymbols::setKey(const QString& a) +{ + int i=a.indexOf("<"); + i++; + keyname=a.mid(i,4); + keyname.remove(" "); + i=a.indexOf("["); + i++; + + QString str=a.mid(i); + i=str.indexOf("]"); + + QString st=str.left(i); + st=st.remove(" "); + //QStringList klst; + symbols=st.split(","); + + if( symbols.size() > MAX_GROUPS_SUPPORTED ) { + symbols = symbols.mid(0, MAX_GROUPS_SUPPORTED); + } + + for(int k=0;k +#include + +class KeySymbols +{ +public: + KeySymbols(); + + QString keyname; + QList symbols; + + void setKey(const QString& opton); +}; + +#endif // KEYSYMBOLS_H diff --git a/kcms/keyboard/preview/keysymhelper.cpp b/kcms/keyboard/preview/keysymhelper.cpp new file mode 100644 index 0000000000..67beb80db5 --- /dev/null +++ b/kcms/keyboard/preview/keysymhelper.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "keysymhelper.h" +#include "keysym2ucs.h" + +#include +#include + +#include + +#include + + +KeySymHelper::KeySymHelper() +{ + nill = 0; +} + +QString KeySymHelper::getKeySymbol(const QString& opton) +{ + if( keySymbolMap.contains(opton) ) + return keySymbolMap[opton]; + + const char* str = opton.toAscii().data(); + +#if 0 + //TODO: figure out how to use this so we don't need our own symkey2ucs mapping + int res = Xutf8LookupString(XIC ic, XKeyPressedEvent *event, char *buffer_return, int bytes_buffer, KeySym *keysym_return, Status *status_return); + +#else + + KeySym keysym = XStringToKeysym(str); + long ucs = keysym2ucs(keysym); + +// if( ucs == -1 && (keysym >= 0xFE50 && keysym <= 0xFE5F) ) { +// ucs = 0x0300 + (keysym & 0x000F); +// kWarning() << "Got dead symbol" << QString("0x%1").arg(keysym, 0, 16) << "named" << opton << "will use" << QString("0x%1").arg(ucs, 0, 16) << "as UCS"; +// } + + if( ucs == -1 ) { + nill++; + kWarning() << "No mapping from keysym:" << QString("0x%1").arg(keysym, 0, 16) << "named:" << opton << "to UCS"; + } + + QString ucsStr = QString(QChar((int)ucs)); + + // Combining Diacritical Marks + if( ucs >= 0x0300 && ucs <= 0x036F ) { + ucsStr = " " + ucsStr + " "; + } + +// kWarning() << "--" << opton << "keysym: " << keysym << QString("0x%1").arg(keysym, 0, 16) << "keysym2string" << XKeysymToString(keysym) +// << "---" << QString("0x%1").arg(ucs, 0, 16) << ucsStr; + + keySymbolMap[opton] = ucsStr; + + return ucsStr; + +#endif + +} + diff --git a/kcms/keyboard/preview/keysymhelper.h b/kcms/keyboard/preview/keysymhelper.h new file mode 100644 index 0000000000..763e872ebc --- /dev/null +++ b/kcms/keyboard/preview/keysymhelper.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KEYSYMHELPER_H +#define KEYSYMHELPER_H + +#include +#include + +class KeySymHelper +{ +public: + KeySymHelper(); + + QString getKeySymbol(const QString &opton); + bool isFailed() const { + return nill >= 120; + } + +private: + QMap keySymbolMap; + int nill; +}; + +#endif // KEYSYMHELPER_H diff --git a/kcms/keyboard/tests/CMakeLists.txt b/kcms/keyboard/tests/CMakeLists.txt new file mode 100644 index 0000000000..7f05ed7a6e --- /dev/null +++ b/kcms/keyboard/tests/CMakeLists.txt @@ -0,0 +1,55 @@ +macro(KEYBOARD_DAEMON_UNIT_TESTS _testname) + kde4_add_unit_test(${_testname}_test TESTNAME kcm-keyboard-${_testname} NOGUI ${_testname}_test.cpp ../${_testname}.cpp) + target_link_libraries(${_testname}_test + Qt5::Concurrent + Qt5::Widgets + Qt5::X11Extras + Qt5::Xml + Qt5::Test + KF5::I18n + KF5::KDE4Support + ${X11_Xkbfile_LIB} + ${X11_LIBRARIES} +) +endmacro(KEYBOARD_DAEMON_UNIT_TESTS) + +keyboard_daemon_unit_tests(xkb_rules) +keyboard_daemon_unit_tests(iso_codes) + +kde4_add_unit_test(flags_test TESTNAME kcm-keyboard-flags_test NOGUI flags_test.cpp ../flags.cpp ../x11_helper.cpp ../keyboard_config.cpp ../xkb_rules.cpp) +target_link_libraries(flags_test + Qt5::Concurrent + Qt5::X11Extras + Qt5::Xml + Qt5::Test + KF5::I18n + KF5::Plasma + KF5::KDE4Support + ${X11_Xkbfile_LIB} + ${X11_LIBRARIES} +) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config/base.1.1.xml ${CMAKE_CURRENT_BINARY_DIR}/config/base.1.1.xml COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config/base.bad.xml ${CMAKE_CURRENT_BINARY_DIR}/config/base.bad.xml COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config/base.xml ${CMAKE_CURRENT_BINARY_DIR}/config/base.xml COPYONLY) + +if( X11_XTest_FOUND ) + kde4_add_unit_test(keyboard_daemon_test TESTNAME kcm-keyboard-keyboard_daemon_test NOGUI keyboard_daemon_test.cpp ../keyboard_daemon.cpp ../layout_memory.cpp ../layout_memory_persister.cpp ../flags.cpp ../x11_helper.cpp ../xkb_helper.cpp ../xinput_helper.cpp ../layout_tray_icon.cpp ../keyboard_config.cpp ../xkb_rules.cpp ../keyboard_hardware.cpp ../bindings.cpp ../layouts_menu.cpp ../numlockx.c) + target_link_libraries(keyboard_daemon_test + Qt5::Concurrent + Qt5::X11Extras + Qt5::Xml + Qt5::Test + KF5::ConfigCore + KF5::CoreAddons + KF5::DBusAddons + KF5::Plasma + KF5::KDE4Support + ${X11_Xkbfile_LIB} + ${X11_XTest_LIB} + ${X11_LIBRARIES} +) + if(XiGetDevicePresence_FOUND) + target_link_libraries(keyboard_daemon_test ${X11_Xinput_LIB}) + endif() +endif() diff --git a/kcms/keyboard/tests/config/base.1.1.xml b/kcms/keyboard/tests/config/base.1.1.xml new file mode 100644 index 0000000000..97206a0b7b --- /dev/null +++ b/kcms/keyboard/tests/config/base.1.1.xml @@ -0,0 +1,68 @@ + + + + + + + pc101 + Generic 101-key PC + Generic + + + + + + + us + USA + USA + eng + + + + + chr + Cherokee + chr + + + chr + Cherokee2 + + + + bad + Bad Variant Missing configItem tag + chr + + + + + + tw + + zh + Taiwanese + + fox + + + + + + + + + grp + Key(s) to change layout + + + + + + \ No newline at end of file diff --git a/kcms/keyboard/tests/config/base.bad.xml b/kcms/keyboard/tests/config/base.bad.xml new file mode 100644 index 0000000000..75bacdd2d4 --- /dev/null +++ b/kcms/keyboard/tests/config/base.bad.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/kcms/keyboard/tests/config/base.xml b/kcms/keyboard/tests/config/base.xml new file mode 100644 index 0000000000..ddab0561c1 --- /dev/null +++ b/kcms/keyboard/tests/config/base.xml @@ -0,0 +1,48 @@ + + + + + + + pc101 + Generic 101-key PC + Generic + + + + + + + us + USA + USA + eng + + + + + chr + Cherokee + chr + + + + + + + + + + grp + Key(s) to change layout + + + + + + \ No newline at end of file diff --git a/kcms/keyboard/tests/flags_test.cpp b/kcms/keyboard/tests/flags_test.cpp new file mode 100644 index 0000000000..59f73716dc --- /dev/null +++ b/kcms/keyboard/tests/flags_test.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "../flags.h" +#include "../xkb_rules.h" +#include "../keyboard_config.h" + + +static QImage image(const QIcon& icon) { + return icon.pixmap(QSize(16,16), QIcon::Normal, QIcon::On).toImage(); +} + +class FlagsTest : public QObject +{ + Q_OBJECT + + Flags* flags; + const Rules* rules; + +private Q_SLOTS: + void initTestCase() { + flags = new Flags(); + rules = NULL; + } + + void cleanupTestCase() { + delete flags; + delete rules; + } + + void testRules() { + QVERIFY( flags != NULL ); + + QVERIFY( ! flags->getTransparentPixmap().isNull() ); + + const QIcon iconUs(flags->getIcon("us")); + QVERIFY( ! iconUs.isNull() ); + QVERIFY( flags->getIcon("--").isNull() ); + + KeyboardConfig keyboardConfig; + LayoutUnit layoutUnit("us"); + LayoutUnit layoutUnit1("us", "intl"); + layoutUnit1.setDisplayName("usi"); + LayoutUnit layoutUnit2("us", "other"); + + keyboardConfig.indicatorType = KeyboardConfig::SHOW_FLAG; + const QIcon iconUsFlag = flags->getIconWithText(layoutUnit, keyboardConfig); + QVERIFY( ! iconUsFlag.isNull() ); + QCOMPARE( image(iconUsFlag), image(iconUs) ); + + keyboardConfig.indicatorType = KeyboardConfig::SHOW_LABEL; + const QIcon iconUsText = flags->getIconWithText(layoutUnit, keyboardConfig); + QVERIFY( ! iconUsText.isNull() ); + QVERIFY( image(iconUsText) != image(iconUs) ); + + keyboardConfig.layouts.append(layoutUnit1); + QCOMPARE( flags->getShortText(layoutUnit, keyboardConfig), QString("us") ); + QCOMPARE( flags->getShortText(layoutUnit1, keyboardConfig), QString("usi") ); + QCOMPARE( flags->getShortText(layoutUnit2, keyboardConfig), QString("us") ); + + const Rules* rules = Rules::readRules(Rules::NO_EXTRAS); + QCOMPARE( flags->getLongText(layoutUnit, rules), QString("English (US)") ); + QVERIFY( flags->getLongText(layoutUnit1, rules).startsWith("English (US, international with dead keys)") ); + QCOMPARE( flags->getLongText(layoutUnit2, rules), QString("other") ); + + rules = NULL; // when no rules found + QCOMPARE( flags->getLongText(layoutUnit1, rules), QString("us - intl") ); + + flags->clearCache(); + } + +// void loadRulesBenchmark() { +// QBENCHMARK { +// Flags* flags = new Flags(); +// delete flags; +// } +// } + +}; + +// need GUI for xkb protocol in xkb_rules.cpp +QTEST_KDEMAIN( FlagsTest, GUI ) + +#include "flags_test.moc" diff --git a/kcms/keyboard/tests/iso_codes_test.cpp b/kcms/keyboard/tests/iso_codes_test.cpp new file mode 100644 index 0000000000..886f5662b0 --- /dev/null +++ b/kcms/keyboard/tests/iso_codes_test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "../iso_codes.h" + + +class IsoCodesTest : public QObject +{ + Q_OBJECT + + IsoCodes* isoCodes; + +private Q_SLOTS: + void initTestCase() { +// isoCodes = new IsoCodes(IsoCodes::iso_639); + isoCodes = new IsoCodes(IsoCodes::iso_639_3); + } + + void cleanupTestCase() { + delete isoCodes; + } + + void testIsoCodes() { + QVERIFY( isoCodes != NULL ); + QVERIFY( ! isoCodes->getEntryList().isEmpty() ); +// const IsoCodeEntry* isoEntry = isoCodes->getEntry(IsoCodes::attr_iso_639_2T_code, "eng"); + const IsoCodeEntry* isoEntry = isoCodes->getEntry(IsoCodes::attr_iso_639_3_id, "eng"); + QVERIFY( isoEntry != NULL ); + QVERIFY( ! isoEntry->empty() ); +// QCOMPARE( isoEntry->value(IsoCodes::attr_iso_639_2T_code), QString("eng") ); +// QCOMPARE( isoEntry->value(IsoCodes::attr_iso_639_2B_code), QString("eng") ); +// QCOMPARE( isoEntry->value(IsoCodes::attr_iso_639_1_code), QString("en") ); + QCOMPARE( isoEntry->value("name"), QString("English") ); +// QCOMPARE( isoEntry->value("status"), QString("Active") ); + } + + void testIso639_3_Codes() { + QVERIFY( isoCodes != NULL ); + QVERIFY( ! isoCodes->getEntryList().isEmpty() ); + const IsoCodeEntry* isoEntry = isoCodes->getEntry(IsoCodes::attr_iso_639_3_id, "ant"); + QVERIFY( isoEntry != NULL ); + QVERIFY( ! isoEntry->empty() ); + QVERIFY( isoEntry->value("name") != QString("ant") ); + QCOMPARE( isoEntry->value("name"), QString("Antakarinya") ); + } + + void loadIsoCodesBenchmark() { + QBENCHMARK { + IsoCodes* isoCodes = new IsoCodes(IsoCodes::iso_639_3); + delete isoCodes; + } + } + +}; + +//TODO: something lighter than KDEMAIN ? +QTEST_KDEMAIN( IsoCodesTest, NoGUI ) + +#include "iso_codes_test.moc" diff --git a/kcms/keyboard/tests/keyboard_daemon_test.cpp b/kcms/keyboard/tests/keyboard_daemon_test.cpp new file mode 100644 index 0000000000..8879daf9a9 --- /dev/null +++ b/kcms/keyboard/tests/keyboard_daemon_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +//#include + +#include +#include + +#include "../flags.h" +#include "../xkb_rules.h" +#include "../keyboard_config.h" +#include "../keyboard_daemon.h" + + +class KeyboardDaemonTest : public QObject +{ + Q_OBJECT + + KeyboardDaemon* keyboardDaemon; +// KApplication* kapplication; + +private Q_SLOTS: + void initTestCase() { +// kapplication = new KApplication(); +// const KAboutData* kAboutData = new KAboutData(i18n("a").toAscii(), i18n("a").toAscii(), KLocalizedString(), i18n("a").toAscii()); +// KCmdLineArgs::init(kAboutData); + keyboardDaemon = new KeyboardDaemon(this, QList()); + } + + void cleanupTestCase() { + delete keyboardDaemon; +// delete kapplication; + } + + void testDaemon() { + QVERIFY( keyboardDaemon != NULL ); + +// QVERIFY( ! flags->getTransparentPixmap().isNull() ); +// +// const QIcon iconUs(flags->getIcon("us")); +// QVERIFY( ! iconUs.isNull() ); +// QVERIFY( flags->getIcon("--").isNull() ); +// +// KeyboardConfig keyboardConfig; +// LayoutUnit layoutUnit("us"); +// LayoutUnit layoutUnit1("us", "intl"); +// layoutUnit1.setDisplayName("usi"); +// LayoutUnit layoutUnit2("us", "other"); +// +// keyboardConfig.showFlag = true; +// const QIcon iconUsFlag = flags->getIconWithText(layoutUnit, keyboardConfig); +// QVERIFY( ! iconUsFlag.isNull() ); +// QCOMPARE( image(iconUsFlag), image(iconUs) ); +// +// keyboardConfig.showFlag = false; +// const QIcon iconUsText = flags->getIconWithText(layoutUnit, keyboardConfig); +// QVERIFY( ! iconUsText.isNull() ); +// QVERIFY( image(iconUsText) != image(iconUs) ); +// +// keyboardConfig.layouts.append(layoutUnit1); +// QCOMPARE( flags->getShortText(layoutUnit, keyboardConfig), QString("us") ); +// QCOMPARE( flags->getShortText(layoutUnit1, keyboardConfig), QString("usi") ); +// QCOMPARE( flags->getShortText(layoutUnit2, keyboardConfig), QString("us") ); +// +// const Rules* rules = Rules::readRules(); +// QCOMPARE( flags->getLongText(layoutUnit, rules), QString("USA") ); +// QVERIFY( flags->getLongText(layoutUnit1, rules).startsWith("USA - International") ); +// QCOMPARE( flags->getLongText(layoutUnit2, rules), QString("USA - other") ); +// +// flags->clearCache(); + } + +// void loadRulesBenchmark() { +// QBENCHMARK { +// Flags* flags = new Flags(); +// delete flags; +// } +// } + +}; + +// need GUI for xkb protocol in xkb_rules.cpp +QTEST_KDEMAIN( KeyboardDaemonTest, GUI ) + +#include "keyboard_daemon_test.moc" diff --git a/kcms/keyboard/tests/layout_memory_persister_test.cpp b/kcms/keyboard/tests/layout_memory_persister_test.cpp new file mode 100644 index 0000000000..0dd0c74c8b --- /dev/null +++ b/kcms/keyboard/tests/layout_memory_persister_test.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "../layout_memory_persister.h" +#include "../layout_memory.h" +#include "../keyboard_config.h" + + +class TestLayoutMemory: public LayoutMemory { +public: + TestLayoutMemory(const KeyboardConfig& keyboardConfig): + LayoutMemory(keyboardConfig) {} + QMap& getLayoutMap() { return layoutMap; } +}; + +class LayoutMemoryPersisterTest : public QObject +{ + Q_OBJECT + + QString path; + KeyboardConfig keyboardConfig; + TestLayoutMemory* layoutMemory; + LayoutMemoryPersister* layoutMemoryPersister; + + const LayoutUnit layoutUnit1; + const LayoutUnit layoutUnit2; + const LayoutUnit layoutUnit3; + + +public: + LayoutMemoryPersisterTest(): + layoutUnit1("xx"), + layoutUnit2("yy", "var1"), + layoutUnit3("zz", "var2") + {} + +private Q_SLOTS: + void initTestCase() { + path = "keyboard_memory_test.xml"; + QFile(path).remove(); + + layoutMemory = new TestLayoutMemory(keyboardConfig); + layoutMemoryPersister = new LayoutMemoryPersister(*layoutMemory); + +// QFile(path).remove(); + } + + void cleanupTestCase() { + delete layoutMemoryPersister; + delete layoutMemory; + } + + void testSaveNA() { + QFile file(path); + + keyboardConfig.switchingPolicy = KeyboardConfig::SWITCH_POLICY_WINDOW; + QVERIFY( ! layoutMemoryPersister->saveToFile(file) ); + QVERIFY(QFile(path).size() == 0); + + QVERIFY( ! layoutMemoryPersister->restoreFromFile(file) ); + } + + void testSaveGlobal() { + QFile file(path); + + keyboardConfig.switchingPolicy = KeyboardConfig::SWITCH_POLICY_GLOBAL; + layoutMemoryPersister->setGlobalLayout(layoutUnit1); + QVERIFY( layoutMemoryPersister->saveToFile(file) ); + QVERIFY(QFile(path).size() > 0); +// file.close(); + + keyboardConfig.layouts.clear(); + + QVERIFY( layoutMemoryPersister->restoreFromFile(file) ); + QVERIFY( !layoutMemoryPersister->getGlobalLayout().isValid() ); + +// file.close(); + keyboardConfig.layouts << layoutUnit1; + QVERIFY( layoutMemoryPersister->restoreFromFile(file) ); + QVERIFY( layoutUnit1.isValid() ); + QVERIFY( layoutMemoryPersister->getGlobalLayout().isValid() ); + QCOMPARE( layoutMemoryPersister->getGlobalLayout(), layoutUnit1); + } + + void testSaveByApp() { + QFile file(path); + + keyboardConfig.switchingPolicy = KeyboardConfig::SWITCH_POLICY_APPLICATION; + + layoutMemoryPersister->setGlobalLayout(LayoutUnit()); + layoutMemory->getLayoutMap().clear(); + + keyboardConfig.layouts.clear(); + keyboardConfig.layouts << layoutUnit1 << layoutUnit2; + + LayoutSet layoutSet1; + layoutSet1.layouts << layoutUnit1 << layoutUnit2; + layoutSet1.currentLayout = layoutUnit1; + layoutMemory->getLayoutMap().insert(QString("app1"), layoutSet1); + + LayoutSet layoutSet2; + layoutSet2.layouts << layoutUnit2 << layoutUnit1 << layoutUnit3; + layoutSet2.currentLayout = layoutUnit2; + layoutMemory->getLayoutMap().insert(QString("app2"), layoutSet2); + + QVERIFY( layoutMemoryPersister->saveToFile(file) ); + QVERIFY(QFile(path).size() > 0); + file.close(); + + layoutMemory->getLayoutMap().clear(); + QVERIFY( ! layoutMemory->getLayoutMap().value("app1").isValid() ); + QVERIFY( ! layoutMemory->getLayoutMap().value("app2").isValid() ); + + QVERIFY( layoutMemoryPersister->restoreFromFile(file) ); + QVERIFY( ! layoutMemoryPersister->getGlobalLayout().isValid() ); + QCOMPARE( layoutMemory->getLayoutMap().value("app1"), layoutSet1 ); + QVERIFY( ! layoutMemory->getLayoutMap().value("app2").isValid() ); + + keyboardConfig.layouts << layoutUnit3; + + file.close(); + QVERIFY( layoutMemoryPersister->restoreFromFile(file) ); + QVERIFY( ! layoutMemoryPersister->getGlobalLayout().isValid() ); + QCOMPARE( layoutMemory->getLayoutMap().value("app1"), layoutSet1 ); + QCOMPARE( layoutMemory->getLayoutMap().value("app2"), layoutSet2 ); + } + +// void layoutMemroyPersisterBenchmark() { +// QBENCHMARK { +// //TODO: generate big map +// layoutMemoryPersister->save(); +// layoutMemoryPersister->restore(); +// } +// } + +}; + +// need GUI for xkb protocol +QTEST_KDEMAIN( LayoutMemoryPersisterTest, GUI ) + +#include "layout_memory_persister_test.moc" diff --git a/kcms/keyboard/tests/xkb_rules_test.cpp b/kcms/keyboard/tests/xkb_rules_test.cpp new file mode 100644 index 0000000000..daef508c76 --- /dev/null +++ b/kcms/keyboard/tests/xkb_rules_test.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2011 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "../xkb_rules.h" + +#include +#include + +static const Rules::ExtrasFlag readExtras = Rules::NO_EXTRAS; + +class RulesTest : public QObject +{ + Q_OBJECT + + Rules* rules; + +private Q_SLOTS: + void initTestCase() { + rules = Rules::readRules(readExtras); + } + + void cleanupTestCase() { + delete rules; + } + + void testRules() { + QVERIFY( rules != NULL ); + QVERIFY( rules->modelInfos.size() > 0 ); + QVERIFY( rules->layoutInfos.size() > 0 ); + QVERIFY( rules->optionGroupInfos.size() > 0 ); + } + + void testModel() { + foreach(const ModelInfo* modelInfo, rules->modelInfos) { + QVERIFY( modelInfo != NULL); + QVERIFY( modelInfo->name.length() > 0 ); + QVERIFY( modelInfo->description.length() > 0 ); +// QVERIFY( ! modelInfo->vendor.isEmpty() ); + } + } + + void testLayouts() { + foreach(const LayoutInfo* layoutInfo, rules->layoutInfos) { + QVERIFY( layoutInfo != NULL); + QVERIFY( ! layoutInfo->name.isEmpty() ); +// const char* desc = layoutInfo->name.toUtf8() ; +// qDebug() << layoutInfo->name; + QVERIFY( ! layoutInfo->description.isEmpty() ); + + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + QVERIFY( variantInfo != NULL ); + QVERIFY( ! variantInfo->name.isEmpty() ); + QVERIFY( ! variantInfo->description.isEmpty() ); + } + foreach(const QString& language, layoutInfo->languages) { + QVERIFY( ! language.isEmpty() ); + } + } + } + + void testOptionGroups() { + foreach(const OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { + QVERIFY( optionGroupInfo != NULL); + QVERIFY( ! optionGroupInfo->name.isEmpty() ); + QVERIFY( ! optionGroupInfo->description.isEmpty() ); + // optionGroupInfo->exclusive + + foreach(const OptionInfo* optionInfo, optionGroupInfo->optionInfos) { + QVERIFY( optionInfo != NULL ); + QVERIFY( ! optionInfo->name.isEmpty() ); + QVERIFY( ! optionInfo->description.isEmpty() ); + } + } + } + + void testExtras() { + Rules* rulesWithExtras = Rules::readRules(Rules::READ_EXTRAS); + QVERIFY2(rulesWithExtras->layoutInfos.size() > rules->layoutInfos.size(), "Rules with extras should have more layouts"); + + foreach(const LayoutInfo* layoutInfo, rules->layoutInfos) { + QVERIFY( ! layoutInfo->fromExtras ); + } + + bool foundFromExtras = false, foundNonExtras = false; + foreach(const LayoutInfo* layoutInfo, rulesWithExtras->layoutInfos) { + if( layoutInfo->fromExtras ) foundFromExtras = true; + if( ! layoutInfo->fromExtras ) foundNonExtras = true; + layoutInfo->languages.size(); // make sure we can access all merged objects + layoutInfo->variantInfos.size(); // make sure we can access all merged objects + } + QVERIFY( foundNonExtras ); + QVERIFY( foundFromExtras ); + } + + void testWriteNewXml() { + QDomDocument doc("xkbConfigRegistry"); + QDomElement root = doc.createElement("xkbConfigRegistry"); + root.setAttribute("version", "2.0"); + doc.appendChild(root); + + QDomElement modelList = doc.createElement("modelList"); + root.appendChild(modelList); + foreach(const ModelInfo* modelInfo, rules->modelInfos) { + QDomElement model = doc.createElement("model"); + model.setAttribute("name", modelInfo->name); + model.setAttribute("description", modelInfo->description); + model.setAttribute("vendor", modelInfo->vendor); + modelList.appendChild(model); + } + + QDomElement layoutList = doc.createElement("layoutList"); + foreach(const LayoutInfo* layoutInfo, rules->layoutInfos) { + QDomElement layout = doc.createElement("layout"); + layout.setAttribute("name", layoutInfo->name); + layout.setAttribute("description", layoutInfo->description); + + QDomElement langList = doc.createElement("languageList"); + foreach(const QString& lang, layoutInfo->languages) { + QDomElement langNode = doc.createElement("lang"); + langNode.setAttribute("iso639Id", lang); + langList.appendChild(langNode); + } + if( langList.hasChildNodes() ) { + layout.appendChild(langList); + } + + QDomElement variantList = doc.createElement("variantList"); + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + QDomElement variant = doc.createElement("variant"); + variant.setAttribute("name", variantInfo->name); + variant.setAttribute("description", variantInfo->description); + + QDomElement langList = doc.createElement("languageList"); + foreach(const QString& lang, variantInfo->languages) { + QDomElement langNode = doc.createElement("lang"); + langNode.setAttribute("iso639Id", lang); + langList.appendChild(langNode); + } + if( langList.hasChildNodes() ) { + variant.appendChild(langList); + } + + variantList.appendChild(variant); + } + if( variantList.hasChildNodes() ) { + layout.appendChild(variantList); + } + + layoutList.appendChild(layout); + } + root.appendChild(layoutList); + + QDomElement optionGroupList = doc.createElement("optionList"); + foreach(const OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { + QDomElement optionGroup = doc.createElement("optionGroup"); + optionGroup.setAttribute("name", optionGroupInfo->name); + optionGroup.setAttribute("description", optionGroupInfo->description); + optionGroup.setAttribute("exclusive", optionGroupInfo->exclusive); + + foreach(const OptionInfo* optionGroupInfo, optionGroupInfo->optionInfos) { + QDomElement option = doc.createElement("option"); + option.setAttribute("name", optionGroupInfo->name); + option.setAttribute("description", optionGroupInfo->description); + optionGroup.appendChild(option); + } + + optionGroupList.appendChild(optionGroup); + } + root.appendChild(optionGroupList); + + QFile file("base2.xml"); + if( ! file.open( QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) ) { + kWarning() << "Failed to open layout memory xml file for writing" << file.fileName(); + QFAIL("failed"); + } + + QTextStream out(&file); + out << doc.toString(); + } + + void testRulesVersion() { + QVERIFY(!rules->version.isEmpty()); + + Rules* rules10 = new Rules(); + Rules::readRules(rules10, QString("config/base.xml"), false); + QCOMPARE(rules10->version, QString("1.0")); + delete rules10; + + Rules* rules11 = new Rules(); + Rules::readRules(rules11, QString("config/base.1.1.xml"), false); + QCOMPARE(rules11->version, QString("1.1")); + + foreach(const LayoutInfo* layoutInfo, rules11->layoutInfos) { + QVERIFY( layoutInfo != NULL); + QVERIFY( ! layoutInfo->name.isEmpty() ); + QVERIFY( ! layoutInfo->description.isEmpty() ); + + foreach(const VariantInfo* variantInfo, layoutInfo->variantInfos) { + QVERIFY( variantInfo != NULL ); + QVERIFY( ! variantInfo->name.isEmpty() ); + QVERIFY( ! variantInfo->description.isEmpty() ); + } + } + + delete rules11; + } + + void loadRulesBenchmark() { + QBENCHMARK { + Rules* rules = Rules::readRules(readExtras); + delete rules; + } + } + +}; + +// need kde libs for config-workspace.h used in xkb_rules.cpp +// need GUI for xkb protocol +QTEST_KDEMAIN( RulesTest, GUI ) + +#include "xkb_rules_test.moc" diff --git a/kcms/keyboard/x11_helper.cpp b/kcms/keyboard/x11_helper.cpp new file mode 100644 index 0000000000..84f5592e5d --- /dev/null +++ b/kcms/keyboard/x11_helper.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "x11_helper.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + + +// more information about the limit https://bugs.freedesktop.org/show_bug.cgi?id=19501 +int X11Helper::MAX_GROUP_COUNT = 4; +int X11Helper::ARTIFICIAL_GROUP_LIMIT_COUNT = 8; + +const char* X11Helper::LEFT_VARIANT_STR = "("; +const char* X11Helper::RIGHT_VARIANT_STR = ")"; + +bool X11Helper::xkbSupported(int* xkbOpcode) +{ + // Verify the Xlib has matching XKB extension. + + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + + if (!XkbLibraryVersion(&major, &minor)) + { + kWarning() << "Xlib XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion; + return false; + } + + // Verify the X server has matching XKB extension. + + int opcode_rtrn; + int error_rtrn; + int xkb_opcode; + if( ! XkbQueryExtension(QX11Info::display(), &opcode_rtrn, &xkb_opcode, &error_rtrn, &major, &minor)) { + kWarning() << "X server XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion; + return false; + } + + if( xkbOpcode != NULL ) { + *xkbOpcode = xkb_opcode; + } + + return true; +} + +void X11Helper::switchToNextLayout() +{ + int size = getLayoutsList().size(); //TODO: could optimize a bit as we don't need the layouts - just count + int group = (X11Helper::getGroup() + 1) % size; + X11Helper::setGroup(group); +} + +void X11Helper::scrollLayouts(int delta) +{ + int size = getLayoutsList().size(); //TODO: could optimize a bit as we don't need the layouts - just count + int group = X11Helper::getGroup() + delta; + group = group < 0 ? size - ((-group) % size) : group % size; + + X11Helper::setGroup(group); +} + +QStringList X11Helper::getLayoutsListAsString(const QList& layoutsList) +{ + QStringList stringList; + foreach(const LayoutUnit& layoutUnit, layoutsList) { + stringList << layoutUnit.toString(); + } + return stringList; +} + +bool X11Helper::setLayout(const LayoutUnit& layout) +{ + QList currentLayouts = getLayoutsList(); + int idx = currentLayouts.indexOf(layout); + if( idx == -1 || idx >= X11Helper::MAX_GROUP_COUNT ) { + kWarning() << "Layout" << layout.toString() << "is not found in current layout list" + << getLayoutsListAsString(currentLayouts); + return false; + } + + return X11Helper::setGroup((unsigned int)idx); +} + +bool X11Helper::setDefaultLayout() { + return X11Helper::setGroup(0); +} + +bool X11Helper::isDefaultLayout() { + return X11Helper::getGroup() == 0; +} + +LayoutUnit X11Helper::getCurrentLayout() +{ + QList currentLayouts = getLayoutsList(); + unsigned int group = X11Helper::getGroup(); + if( group < (unsigned int)currentLayouts.size() ) + return currentLayouts[group]; + + kWarning() << "Current group number" << group << "is outside of current layout list" << + getLayoutsListAsString(currentLayouts); + return LayoutUnit(); +} + +LayoutSet X11Helper::getCurrentLayouts() +{ + LayoutSet layoutSet; + + QList currentLayouts = getLayoutsList(); + layoutSet.layouts = currentLayouts; + + unsigned int group = X11Helper::getGroup(); + if( group < (unsigned int)currentLayouts.size() ) { + layoutSet.currentLayout = currentLayouts[group]; + } + else { + kWarning() << "Current group number" << group << "is outside of current layout list" << getLayoutsListAsString(currentLayouts); + layoutSet.currentLayout = LayoutUnit(); + } + + return layoutSet; +} + + +//static QString addNum(const QString& str, int n) +//{ +// QString format("%1%2"); +// if( str.length() >= 3 ) return format.arg(str.left(2)).arg(n); +// return format.arg(str).arg(n); +//} + +QList X11Helper::getLayoutsList() +{ + XkbConfig xkbConfig; + QList layouts; + if( X11Helper::getGroupNames(QX11Info::display(), &xkbConfig, X11Helper::LAYOUTS_ONLY) ) { + for(int i=0; i 0) || (real_prop_type != XA_STRING) || (fmt != 8)) { + if (prop_data) + XFree(prop_data); + kWarning() << "Failed to fetch layouts from server:" << "Wrong property format"; + return false; + } + +// qDebug() << "prop_data:" << nitems << prop_data; + QStringList names; + for(char* p=prop_data; p-prop_data < (long)nitems && p != NULL; p += strlen(p)+1) { + names.append( p ); +// kDebug() << " " << p; + } + + if( names.count() < 4 ) { //{ rules, model, layouts, variants, options } + XFree(prop_data); + return false; + } + + if( fetchType == ALL || fetchType == LAYOUTS_ONLY ) { + QStringList layouts = names[2].split(OPTIONS_SEPARATOR); + QStringList variants = names[3].split(OPTIONS_SEPARATOR); + + for(int ii=0; iilayouts << (layouts[ii] != NULL ? layouts[ii] : ""); + xkbConfig->variants << (ii < variants.count() && variants[ii] != NULL ? variants[ii] : ""); + } + kDebug() << "Fetched layout groups from X server:" + << "\tlayouts:" << xkbConfig->layouts + << "\tvariants:" << xkbConfig->variants; + } + + if( fetchType == ALL || fetchType == MODEL_ONLY ) { + xkbConfig->keyboardModel = (names[1] != NULL ? names[1] : ""); + kDebug() << "Fetched keyboard model from X server:" << xkbConfig->keyboardModel; + } + + if( fetchType == ALL ) { + if( names.count() >= 5 ) { + QString options = (names[4] != NULL ? names[4] : ""); + xkbConfig->options = options.split(OPTIONS_SEPARATOR); + kDebug() << "Fetched xkbOptions from X server:" << options; + } + } + + XFree(prop_data); + return true; +} + +#if 0 +XEventNotifier::XEventNotifier(QWidget* parent): + QWidget(parent), + xkbOpcode(-1) +{ + if( KApplication::kApplication() == NULL ) { + kWarning() << "Layout Widget won't work properly without KApplication instance"; + } +} + +void XEventNotifier::start() +{ + if( KApplication::kApplication() != NULL && X11Helper::xkbSupported(&xkbOpcode) ) { + registerForXkbEvents(QX11Info::display()); + + // start the event loop + KApplication::kApplication()->installX11EventFilter(this); + } +} + +void XEventNotifier::stop() +{ + if( KApplication::kApplication() != NULL ) { + //TODO: unregister + // XEventNotifier::unregisterForXkbEvents(QX11Info::display()); + + // stop the event loop + KApplication::kApplication()->removeX11EventFilter(this); + } +} + +bool XEventNotifier::isXkbEvent(XEvent* event) +{ + return event->type == xkbOpcode; +} + +bool XEventNotifier::processOtherEvents(XEvent* /*event*/) +{ + return true; +} + +bool XEventNotifier::processXkbEvents(XEvent* event) +{ + if( XEventNotifier::isGroupSwitchEvent(event) ) { + emit(layoutChanged()); + } + else if( XEventNotifier::isLayoutSwitchEvent(event) ) { + emit(layoutMapChanged()); + } + return true; +} + +bool XEventNotifier::x11Event(XEvent * event) +{ + // qApp->x11ProcessEvent ( event ); + if( isXkbEvent(event) ) { + processXkbEvents(event); + } + else { + processOtherEvents(event); + } + return QWidget::x11Event(event); +} + +bool XEventNotifier::isGroupSwitchEvent(XEvent* event) +{ + XkbEvent *xkbEvent = (XkbEvent*) event; +#define GROUP_CHANGE_MASK \ + ( XkbGroupStateMask | XkbGroupBaseMask | XkbGroupLatchMask | XkbGroupLockMask ) + + return xkbEvent->any.xkb_type == XkbStateNotify && (xkbEvent->state.changed & GROUP_CHANGE_MASK); +} + +bool XEventNotifier::isLayoutSwitchEvent(XEvent* event) +{ + XkbEvent *xkbEvent = (XkbEvent*) event; + + return //( (xkbEvent->any.xkb_type == XkbMapNotify) && (xkbEvent->map.changed & XkbKeySymsMask) ) || +/* || ( (xkbEvent->any.xkb_type == XkbNamesNotify) && (xkbEvent->names.changed & XkbGroupNamesMask) || )*/ + (xkbEvent->any.xkb_type == XkbNewKeyboardNotify); +} + +int XEventNotifier::registerForXkbEvents(Display* display) +{ + int eventMask = XkbNewKeyboardNotifyMask | XkbStateNotifyMask; + if( ! XkbSelectEvents(display, XkbUseCoreKbd, eventMask, eventMask) ) { + kWarning() << "Couldn't select desired XKB events"; + return false; + } + return true; +} +#endif + + +static const char* LAYOUT_VARIANT_SEPARATOR_PREFIX = "("; +static const char* LAYOUT_VARIANT_SEPARATOR_SUFFIX = ")"; + +static QString& stripVariantName(QString& variant) +{ + if( variant.endsWith(LAYOUT_VARIANT_SEPARATOR_SUFFIX) ) { + int suffixLen = strlen(LAYOUT_VARIANT_SEPARATOR_SUFFIX); + return variant.remove(variant.length()-suffixLen, suffixLen); + } + return variant; +} + +LayoutUnit::LayoutUnit(const QString& fullLayoutName) +{ + QStringList lv = fullLayoutName.split(LAYOUT_VARIANT_SEPARATOR_PREFIX); + layout = lv[0]; + variant = lv.size() > 1 ? stripVariantName(lv[1]) : ""; +} + +QString LayoutUnit::toString() const +{ + if( variant.isEmpty() ) + return layout; + + return layout + LAYOUT_VARIANT_SEPARATOR_PREFIX+variant+LAYOUT_VARIANT_SEPARATOR_SUFFIX; +} + +const int LayoutUnit::MAX_LABEL_LENGTH = 3; diff --git a/kcms/keyboard/x11_helper.h b/kcms/keyboard/x11_helper.h new file mode 100644 index 0000000000..b393e5eff1 --- /dev/null +++ b/kcms/keyboard/x11_helper.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef X11_HELPER_H_ +#define X11_HELPER_H_ + +#include +#include +#include +#include +#include + +#warning XEventNotifier needs porting to QAbstractNativeEventFilter +#if 0 +class XEventNotifier : public QWidget { + Q_OBJECT + +Q_SIGNALS: + void layoutChanged(); + void layoutMapChanged(); + +public: + XEventNotifier(QWidget* parent=NULL); + virtual ~XEventNotifier() {} + + virtual void start(); + virtual void stop(); + +protected: + bool x11Event(XEvent * e); + virtual bool processOtherEvents(XEvent* e); + virtual bool processXkbEvents(XEvent* e); + +private: + int registerForXkbEvents(Display* display); + bool isXkbEvent(XEvent* event); + bool isGroupSwitchEvent(XEvent* event); + bool isLayoutSwitchEvent(XEvent* event); + + int xkbOpcode; +}; +#endif + +struct XkbConfig { + QString keyboardModel; + QStringList layouts; + QStringList variants; + QStringList options; + + bool isValid() { return ! layouts.empty(); } +}; + + +struct LayoutUnit { + static const int MAX_LABEL_LENGTH; + + //TODO: move these to private? + QString layout; + QString variant; + + LayoutUnit() {} + explicit LayoutUnit(const QString& fullLayoutName); + LayoutUnit(const QString& layout_, const QString& variant_) { + layout = layout_; + variant = variant_; + } + /*explicit*/ LayoutUnit(const LayoutUnit& layoutUnit) { + layout = layoutUnit.layout; + variant = layoutUnit.variant; + displayName = layoutUnit.displayName; + shortcut = layoutUnit.shortcut; + } + + QString getRawDisplayName() const { return displayName; } + QString getDisplayName() const { return !displayName.isEmpty() ? displayName : layout; } + void setDisplayName(const QString& name) { displayName = name; } + + void setShortcut(const QKeySequence& shortcut) { this->shortcut = shortcut; } + QKeySequence getShortcut() const { return shortcut; } + + bool isEmpty() const { return layout.isEmpty(); } + bool isValid() const { return ! isEmpty(); } + bool operator==(const LayoutUnit& layoutItem) const { + return layout==layoutItem.layout && variant==layoutItem.variant; + } + bool operator!=(const LayoutUnit& layoutItem) const { + return ! (*this == layoutItem); + } + QString toString() const; + +private: + QString displayName; + QKeySequence shortcut; +}; + +struct LayoutSet { + QList layouts; + LayoutUnit currentLayout; + + LayoutSet() {} + + LayoutSet(const LayoutSet& currentLayouts) { + this->layouts = currentLayouts.layouts; + this->currentLayout = currentLayouts.currentLayout; + } + + bool isValid() const { + return currentLayout.isValid() && layouts.contains(currentLayout); + } + + bool operator == (const LayoutSet& currentLayouts) const { + return this->layouts == currentLayouts.layouts + && this->currentLayout == currentLayouts.currentLayout; + } + + LayoutSet& operator = (const LayoutSet& currentLayouts) { + this->layouts = currentLayouts.layouts; + this->currentLayout = currentLayouts.currentLayout; + return *this; + } + + QString toString() const { + QString str(currentLayout.toString()); + str += ": "; + foreach(const LayoutUnit& layoutUnit, layouts) { + str += layoutUnit.toString() + " "; + } + return str; + } + + static QString toString(const QList& layoutUnits) { + QString str; + foreach(const LayoutUnit& layoutUnit, layoutUnits) { + str += layoutUnit.toString() + ","; + } + return str; + } +}; + +class X11Helper +{ +public: + static int MAX_GROUP_COUNT; + static int ARTIFICIAL_GROUP_LIMIT_COUNT; + + static const char* LEFT_VARIANT_STR; + static const char* RIGHT_VARIANT_STR; + + static bool xkbSupported(int* xkbOpcode); + + static void switchToNextLayout(); + static void scrollLayouts(int delta); + static bool isDefaultLayout(); + static bool setDefaultLayout(); + static bool setLayout(const LayoutUnit& layout); + static LayoutUnit getCurrentLayout(); + static LayoutSet getCurrentLayouts(); + static QList getLayoutsList(); + static QStringList getLayoutsListAsString(const QList& layoutsList); + + enum FetchType { ALL, LAYOUTS_ONLY, MODEL_ONLY }; + static bool getGroupNames(Display* dpy, XkbConfig* xkbConfig, FetchType fetchType); + +private: + static unsigned int getGroup(); + static bool setGroup(unsigned int group); +}; + +#endif /* X11_HELPER_H_ */ diff --git a/kcms/keyboard/xinput_helper.cpp b/kcms/keyboard/xinput_helper.cpp new file mode 100644 index 0000000000..b138855aa0 --- /dev/null +++ b/kcms/keyboard/xinput_helper.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "xinput_helper.h" + +#include +#include + +#include + +#include +#include +#include + +#ifdef HAVE_XINPUT_AND_DEVICE_NOTIFY +#include +#endif + +#include + +#include "x11_helper.h" + +#include + +static int DEVICE_NONE = 0; +static int DEVICE_KEYBOARD = 1; +static int DEVICE_POINTER = 2; + +XInputEventNotifier::XInputEventNotifier(QWidget* parent): + XEventNotifier(parent), + xinputEventType(-1) +{ +} + +void XInputEventNotifier::start() +{ + if( KApplication::kApplication() != NULL ) { + registerForNewDeviceEvent(QX11Info::display()); + } + + XEventNotifier::start(); +} + +void XInputEventNotifier::stop() +{ + XEventNotifier::stop(); + + if( KApplication::kApplication() != NULL ) { + // XEventNotifier::unregisterForNewDeviceEvent(QX11Info::display()); + } +} + +bool XInputEventNotifier::processOtherEvents(XEvent* event) +{ + int newDeviceType = getNewDeviceEventType(event); + if( newDeviceType == DEVICE_KEYBOARD ) { + emit(newKeyboardDevice()); + } + else if( newDeviceType == DEVICE_POINTER ) { + emit(newPointerDevice()); + emit(newKeyboardDevice()); // arghhh, looks like X resets xkb map even when only pointer device is connected + } + return true; +} + + +#ifdef HAVE_XINPUT_AND_DEVICE_NOTIFY + +extern "C" { + extern int _XiGetDevicePresenceNotifyEvent(Display *); +} + +// This is ugly but allows to skip multiple execution of setxkbmap +// for all keyboard devices that don't care about layouts +static bool isRealKeyboard(const char* deviceName) +{ + return strstr(deviceName, "Video Bus") == NULL + && strstr(deviceName, "Sleep Button") == NULL + && strstr(deviceName, "Power Button") == NULL + && strstr(deviceName, "WMI hotkeys") == NULL; +} + +int XInputEventNotifier::getNewDeviceEventType(XEvent* event) +{ + int newDeviceType = DEVICE_NONE; + + if( xinputEventType != -1 && event->type == xinputEventType ) { + XDevicePresenceNotifyEvent *xdpne = (XDevicePresenceNotifyEvent*) event; + if( xdpne->devchange == DeviceEnabled ) { + int ndevices; + XDeviceInfo *devices = XListInputDevices(xdpne->display, &ndevices); + if( devices != NULL ) { +// kDebug() << "New device id:" << xdpne->deviceid; + for(int i=0; ideviceid ) { + if( devices[i].use == IsXKeyboard || devices[i].use == IsXExtensionKeyboard ) { + if( isRealKeyboard(devices[i].name) ) { + newDeviceType = DEVICE_KEYBOARD; + kDebug() << "new keyboard device, id:" << devices[i].id << "name:" << devices[i].name << "used as:" << devices[i].use; + break; + } + } + if( devices[i].use == IsXPointer || devices[i].use == IsXExtensionPointer ) { + newDeviceType = DEVICE_POINTER; + kDebug() << "new pointer device, id:" << devices[i].id << "name:" << devices[i].name << "used as:" << devices[i].use; + break; + } + } + } + XFreeDeviceList(devices); + } + } + } + return newDeviceType; +} + +int XInputEventNotifier::registerForNewDeviceEvent(Display* display) +{ + int xitype; + XEventClass xiclass; + + DevicePresence(display, xitype, xiclass); + XSelectExtensionEvent(display, DefaultRootWindow(display), &xiclass, 1); + kDebug() << "Registered for new device events from XInput, class" << xitype; + xinputEventType = xitype; + return xitype; +} + +#else + +#ifdef __GNUC__ +#warning "Keyboard daemon is compiled without XInput, keyboard settings will be reset when new keyboard device is plugged in!" +#endif + +int XInputEventNotifier::registerForNewDeviceEvent(Display* /*display*/) +{ + kWarning() << "Keyboard kded daemon is compiled without XInput, xkb configuration will be reset when new keyboard device is plugged in!"; + return -1; +} + +int XInputEventNotifier::getNewDeviceEventType(XEvent* /*event*/) +{ + return DEVICE_NONE; +} + +#endif diff --git a/kcms/keyboard/xinput_helper.h b/kcms/keyboard/xinput_helper.h new file mode 100644 index 0000000000..92f1eb65b2 --- /dev/null +++ b/kcms/keyboard/xinput_helper.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef XINPUT_HELPER_H_ +#define XINPUT_HELPER_H_ + +#include "x11_helper.h" + + +class XInputEventNotifier: public XEventNotifier { + Q_OBJECT + +public: + XInputEventNotifier(QWidget* parent=NULL); + + void start(); + void stop(); + + int registerForNewDeviceEvent(Display* dpy); + +Q_SIGNALS: + void newKeyboardDevice(); + void newPointerDevice(); + +protected: + bool processOtherEvents(XEvent* event); + +private: + int getNewDeviceEventType(XEvent* event); + + int xinputEventType; +}; + +#endif /* XINPUT_HELPER_H_ */ diff --git a/kcms/keyboard/xkb_helper.cpp b/kcms/keyboard/xkb_helper.cpp new file mode 100644 index 0000000000..10969878a6 --- /dev/null +++ b/kcms/keyboard/xkb_helper.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "xkb_helper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "keyboard_config.h" + + +static const char* SETXKBMAP_EXEC = "setxkbmap"; +static const char* XMODMAP_EXEC = "xmodmap"; + +static bool setxkbmapNotFound = false; +static QString setxkbmapExe; + +static bool xmodmapNotFound = false; +static QString xmodmapExe; + +static const QString COMMAND_OPTIONS_SEPARATOR(","); + +static +QString getSetxkbmapExe() +{ + if( setxkbmapNotFound ) + return ""; + + if( setxkbmapExe.isEmpty() ) { + setxkbmapExe = KGlobal::dirs()->findExe(SETXKBMAP_EXEC); + if( setxkbmapExe.isEmpty() ) { + setxkbmapNotFound = true; + kError() << "Can't find" << SETXKBMAP_EXEC << "- keyboard layouts won't be configured"; + return ""; + } + } + return setxkbmapExe; +} + +static +void executeXmodmap(const QString& configFileName) +{ + if( xmodmapNotFound ) + return; + + if( QFile(configFileName).exists() ) { + if( xmodmapExe.isEmpty() ) { + xmodmapExe = KGlobal::dirs()->findExe(XMODMAP_EXEC); + if( xmodmapExe.isEmpty() ) { + xmodmapNotFound = true; + kError() << "Can't find" << XMODMAP_EXEC << "- xmodmap files won't be run"; + return; + } + } + + KProcess xmodmapProcess; + xmodmapProcess << xmodmapExe; + xmodmapProcess << configFileName; + kDebug() << "Executing" << xmodmapProcess.program().join(" "); + if( xmodmapProcess.execute() != 0 ) { + kError() << "Failed to execute " << xmodmapProcess.program(); + } + } +} + +static +void restoreXmodmap() +{ + // TODO: is just home .Xmodmap enough or should system be involved too? + // QString configFileName = QDir("/etc/X11/xinit").filePath(".Xmodmap"); + // executeXmodmap(configFileName); + QString configFileName = QDir::home().filePath(".Xmodmap"); + executeXmodmap(configFileName); +} + +//TODO: make private +bool XkbHelper::runConfigLayoutCommand(const QStringList& setxkbmapCommandArguments) +{ + QTime timer; + timer.start(); + + KProcess setxkbmapProcess; + setxkbmapProcess << getSetxkbmapExe() << setxkbmapCommandArguments; + int res = setxkbmapProcess.execute(); + + if( res == 0 ) { // restore Xmodmap mapping reset by setxkbmap + kDebug() << "Executed successfully in " << timer.elapsed() << "ms" << setxkbmapProcess.program().join(" "); + restoreXmodmap(); + kDebug() << "\t and with xmodmap" << timer.elapsed() << "ms"; + return true; + } + else { + kError() << "Failed to run" << setxkbmapProcess.program().join(" ") << "return code:" << res; + } + return false; +} + +bool XkbHelper::initializeKeyboardLayouts(const QList& layoutUnits) +{ + QStringList layouts; + QStringList variants; + foreach (const LayoutUnit& layoutUnit, layoutUnits) { + layouts.append(layoutUnit.layout); + variants.append(layoutUnit.variant); + } + + QStringList setxkbmapCommandArguments; + setxkbmapCommandArguments.append("-layout"); + setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); + if( ! variants.join("").isEmpty() ) { + setxkbmapCommandArguments.append("-variant"); + setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); + } + + return runConfigLayoutCommand(setxkbmapCommandArguments); +} + +bool XkbHelper::initializeKeyboardLayouts(KeyboardConfig& config) +{ + QStringList setxkbmapCommandArguments; + if( ! config.keyboardModel.isEmpty() ) { + XkbConfig xkbConfig; + X11Helper::getGroupNames(QX11Info::display(), &xkbConfig, X11Helper::MODEL_ONLY); + if( xkbConfig.keyboardModel != config.keyboardModel ) { + setxkbmapCommandArguments.append("-model"); + setxkbmapCommandArguments.append(config.keyboardModel); + } + } + if( config.configureLayouts ) { + QStringList layouts; + QStringList variants; + QList defaultLayouts = config.getDefaultLayouts(); + foreach (const LayoutUnit& layoutUnit, defaultLayouts) { + layouts.append(layoutUnit.layout); + variants.append(layoutUnit.variant); + } + + setxkbmapCommandArguments.append("-layout"); + setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); + if( ! variants.join("").isEmpty() ) { + setxkbmapCommandArguments.append("-variant"); + setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); + } + } + if( config.resetOldXkbOptions ) { + setxkbmapCommandArguments.append("-option"); + } + if( ! config.xkbOptions.isEmpty() ) { + setxkbmapCommandArguments.append("-option"); + setxkbmapCommandArguments.append(config.xkbOptions.join(COMMAND_OPTIONS_SEPARATOR)); + } + + if( ! setxkbmapCommandArguments.isEmpty() ) { + return runConfigLayoutCommand(setxkbmapCommandArguments); + if( config.configureLayouts ) { + X11Helper::setDefaultLayout(); + } + } + return false; +} diff --git a/kcms/keyboard/xkb_helper.h b/kcms/keyboard/xkb_helper.h new file mode 100644 index 0000000000..5e4f94d2c3 --- /dev/null +++ b/kcms/keyboard/xkb_helper.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef XKB_HELPER_H_ +#define XKB_HELPER_H_ + +template class QList; +class LayoutUnit; +class QStringList; +class KeyboardConfig; + +class XkbHelper { +public: + static bool initializeKeyboardLayouts(KeyboardConfig& config); + static bool initializeKeyboardLayouts(const QList& layouts); + static bool runConfigLayoutCommand(const QStringList& setxkbmapCommandArguments); +}; + +#endif /* XKB_HELPER_H_ */ diff --git a/kcms/keyboard/xkb_rules.cpp b/kcms/keyboard/xkb_rules.cpp new file mode 100644 index 0000000000..a1840f898f --- /dev/null +++ b/kcms/keyboard/xkb_rules.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "xkb_rules.h" +#include "config-workspace.h" + +#include +#include +#include + +#include +#include +#include // for Qt::escape +#include +#include + +//#include +//#include + +#include "x11_helper.h" + +// for findXkbRuleFile +#include +#include +#include +#include +#include +#include +#include + + +class RulesHandler : public QXmlDefaultHandler +{ +public: + RulesHandler(Rules* rules_, bool fromExtras_): + rules(rules_), + fromExtras(fromExtras_){} + + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &attributes); + bool endElement(const QString &namespaceURI, const QString &localName, + const QString &qName); + bool characters(const QString &str); +// bool fatalError(const QXmlParseException &exception); +// QString errorString() const; + +private: +// QString getString(const QString& text); + + QStringList path; + Rules* rules; + const bool fromExtras; +}; + +static QString translate_xml_item(const QString& itemText) +{ + return i18n(Qt::escape(itemText).toUtf8()); + // return QString::fromUtf8(dgettext("xkeyboard-config", itemText.toAscii())); +} + +static QString translate_description(ConfigItem* item) +{ + return item->description.isEmpty() + ? item->name : translate_xml_item(item->description); +} + +static bool notEmpty(const ConfigItem* item) +{ + return ! item->name.isEmpty(); +} + +template +void removeEmptyItems(QList& list) +{ +#ifdef __GNUC__ +#if __GNUC__ == 4 && (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 3) || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ < 4) +#warning Compiling with a workaround for GCC < 4.8.3 || GCC < 4.7.4 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 + Q_FOREACH(T* x, list) { + ConfigItem *y = static_cast(x); + if (y->name.isEmpty()) { + list.removeAll(x); + } + } +#else + QtConcurrent::blockingFilter(list, notEmpty); +#endif +#endif +} + +static +void postProcess(Rules* rules) +{ + //TODO remove elements with empty names to safeguard us + removeEmptyItems(rules->layoutInfos); + removeEmptyItems(rules->modelInfos); + removeEmptyItems(rules->optionGroupInfos); + +// setlocale(LC_ALL, ""); +// bindtextdomain("xkeyboard-config", LOCALE_DIR); + foreach(ModelInfo* modelInfo, rules->modelInfos) { + modelInfo->vendor = translate_xml_item(modelInfo->vendor); + modelInfo->description = translate_description(modelInfo); + } + + foreach(LayoutInfo* layoutInfo, rules->layoutInfos) { + layoutInfo->description = translate_description(layoutInfo); + + removeEmptyItems(layoutInfo->variantInfos); + foreach(VariantInfo* variantInfo, layoutInfo->variantInfos) { + variantInfo->description = translate_description(variantInfo); + } + } + foreach(OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { + optionGroupInfo->description = translate_description(optionGroupInfo); + + removeEmptyItems(optionGroupInfo->optionInfos); + foreach(OptionInfo* optionInfo, optionGroupInfo->optionInfos) { + optionInfo->description = translate_description(optionInfo); + } + } +} + + +Rules::Rules(): + version("1.0") +{ +} + +QString Rules::getRulesName() +{ + XkbRF_VarDefsRec vd; + char *tmp = NULL; + + if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != NULL ) { + // qDebug() << "namesprop" << tmp ; + const QString name(tmp); + XFree(tmp); + return name; + } + + return QString::null; +} + +static QString findXkbRulesFile() +{ + QString rulesFile; + QString rulesName = Rules::getRulesName(); + + if ( ! rulesName.isNull() ) { + QString xkbParentDir; + + QString base(XLIBDIR); + if( base.count('/') >= 3 ) { + // .../usr/lib/X11 -> /usr/share/X11/xkb vs .../usr/X11/lib -> /usr/X11/share/X11/xkb + QString delta = base.endsWith("X11") ? "/../../share/X11" : "/../share/X11"; + QDir baseDir(base + delta); + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + else { + QDir baseDir(base + "/X11"); // .../usr/X11/lib/X11/xkb (old XFree) + if( baseDir.exists() ) { + xkbParentDir = baseDir.absolutePath(); + } + } + } + + if( xkbParentDir.isEmpty() ) { + xkbParentDir = "/usr/share/X11"; + } + + rulesFile = QString("%1/xkb/rules/%2.xml").arg(xkbParentDir, rulesName); + } + + return rulesFile; +} + +static +void mergeRules(Rules* rules, Rules* extraRules) +{ + rules->modelInfos.append( extraRules->modelInfos ); + rules->optionGroupInfos.append( extraRules->optionGroupInfos ); // need to iterate and merge? + + QList layoutsToAdd; + foreach(LayoutInfo* extraLayoutInfo, extraRules->layoutInfos) { + LayoutInfo* layoutInfo = findByName(rules->layoutInfos, extraLayoutInfo->name); + if( layoutInfo != NULL ) { + layoutInfo->variantInfos.append( extraLayoutInfo->variantInfos ); + layoutInfo->languages.append( extraLayoutInfo->languages ); + } + else { + layoutsToAdd.append(extraLayoutInfo); + } + } + rules->layoutInfos.append(layoutsToAdd); + kDebug() << "Merged from extra rules:" << extraRules->layoutInfos.size() << "layouts," << extraRules->modelInfos.size() << "models," << extraRules->optionGroupInfos.size() << "option groups"; + + // base rules now own the objects - remove them from extra rules so that it does not try to delete them + extraRules->layoutInfos.clear(); + extraRules->modelInfos.clear(); + extraRules->optionGroupInfos.clear(); +} + + +const char Rules::XKB_OPTION_GROUP_SEPARATOR = ':'; + +Rules* Rules::readRules(ExtrasFlag extrasFlag) +{ + Rules* rules = new Rules(); + QString rulesFile = findXkbRulesFile(); + if( ! readRules(rules, rulesFile, false) ) { + delete rules; + return NULL; + } + if( extrasFlag == Rules::READ_EXTRAS ) { + QRegExp regex("\\.xml$"); + Rules* rulesExtra = new Rules(); + QString extraRulesFile = rulesFile.replace(regex, ".extras.xml"); + if( readRules(rulesExtra, extraRulesFile, true) ) { // not fatal if it fails + mergeRules(rules, rulesExtra); + } + delete rulesExtra; + } + return rules; +} + + +Rules* Rules::readRules(Rules* rules, const QString& filename, bool fromExtras) +{ + QFile file(filename); + if( !file.open(QFile::ReadOnly | QFile::Text) ) { + kError() << "Cannot open the rules file" << file.fileName(); + return NULL; + } + + RulesHandler rulesHandler(rules, fromExtras); + + QXmlSimpleReader reader; + reader.setContentHandler(&rulesHandler); + reader.setErrorHandler(&rulesHandler); + + QXmlInputSource xmlInputSource(&file); + + kDebug() << "Parsing xkb rules from" << file.fileName(); + + if( ! reader.parse(xmlInputSource) ) { + kError() << "Failed to parse the rules file" << file.fileName(); + delete rules; + return NULL; + } + + postProcess(rules); + + return rules; +} + +bool RulesHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, + const QString &qName, const QXmlAttributes &attributes) +{ + path << QString(qName); + + QString strPath = path.join("/"); + if( strPath.endsWith("layoutList/layout/configItem") ) { + rules->layoutInfos << new LayoutInfo(fromExtras); + } + else if( strPath.endsWith("layoutList/layout/variantList/variant") ) { + rules->layoutInfos.last()->variantInfos << new VariantInfo(fromExtras); + } + else if( strPath.endsWith("modelList/model") ) { + rules->modelInfos << new ModelInfo(); + } + else if( strPath.endsWith("optionList/group") ) { + rules->optionGroupInfos << new OptionGroupInfo(); + rules->optionGroupInfos.last()->exclusive = (attributes.value("allowMultipleSelection") != "true"); + } + else if( strPath.endsWith("optionList/group/option") ) { + rules->optionGroupInfos.last()->optionInfos << new OptionInfo(); + } + else if( strPath == ("xkbConfigRegistry") && ! attributes.value("version").isEmpty() ) { + rules->version = attributes.value("version"); + kDebug() << "xkbConfigRegistry version" << rules->version; + } + return true; +} + +bool RulesHandler::endElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &/*qName*/) +{ + path.removeLast(); + return true; +} + +bool RulesHandler::characters(const QString &str) +{ + if( !str.trimmed().isEmpty() ) { + QString strPath = path.join("/"); + if( strPath.endsWith("layoutList/layout/configItem/name") ) { + if( rules->layoutInfos.last() != NULL ) { + rules->layoutInfos.last()->name = str.trimmed(); +// qDebug() << "name:" << str; + } + // skipping invalid entry + } + else if( strPath.endsWith("layoutList/layout/configItem/description") ) { + rules->layoutInfos.last()->description = str.trimmed(); +// qDebug() << "descr:" << str; + } + else if( strPath.endsWith("layoutList/layout/configItem/languageList/iso639Id") ) { + rules->layoutInfos.last()->languages << str.trimmed(); +// qDebug() << "\tlang:" << str; + } + else if( strPath.endsWith("layoutList/layout/variantList/variant/configItem/name") ) { + rules->layoutInfos.last()->variantInfos.last()->name = str.trimmed(); +// qDebug() << "\tvariant name:" << str; + } + else if( strPath.endsWith("layoutList/layout/variantList/variant/configItem/description") ) { + rules->layoutInfos.last()->variantInfos.last()->description = str.trimmed(); +// qDebug() << "\tvariant descr:" << str; + } + else if( strPath.endsWith("layoutList/layout/variantList/variant/configItem/languageList/iso639Id") ) { + rules->layoutInfos.last()->variantInfos.last()->languages << str.trimmed(); +// qDebug() << "\tvlang:" << str; + } + else if( strPath.endsWith("modelList/model/configItem/name") ) { + rules->modelInfos.last()->name = str.trimmed(); +// qDebug() << "name:" << str; + } + else if( strPath.endsWith("modelList/model/configItem/description") ) { + rules->modelInfos.last()->description = str.trimmed(); +// qDebug() << "\tdescr:" << str; + } + else if( strPath.endsWith("modelList/model/configItem/vendor") ) { + rules->modelInfos.last()->vendor = str.trimmed(); +// qDebug() << "\tvendor:" << str; + } + else if( strPath.endsWith("optionList/group/configItem/name") ) { + rules->optionGroupInfos.last()->name = str.trimmed(); +// qDebug() << "name:" << str; + } + else if( strPath.endsWith("optionList/group/configItem/description") ) { + rules->optionGroupInfos.last()->description = str.trimmed(); +// qDebug() << "\tdescr:" << str; + } + else if( strPath.endsWith("optionList/group/option/configItem/name") ) { + rules->optionGroupInfos.last()->optionInfos.last()->name = str.trimmed(); +// qDebug() << "name:" << str; + } + else if( strPath.endsWith("optionList/group/option/configItem/description") ) { + rules->optionGroupInfos.last()->optionInfos.last()->description = str.trimmed(); +// qDebug() << "\tdescr:" << str; + } + } + return true; +} + +bool LayoutInfo::isLanguageSupportedByLayout(const QString& lang) const +{ + if( languages.contains(lang) || isLanguageSupportedByVariants(lang) ) + return true; + +// // return yes if no languages found in layout or its variants +// if( languages.empty() ) { +// foreach(const VariantInfo* info, variantInfos) { +// if( ! info->languages.empty() ) +// return false; +// } +// return true; +// } + + return false; +} + +bool LayoutInfo::isLanguageSupportedByVariants(const QString& lang) const +{ + foreach(const VariantInfo* info, variantInfos) { + if( info->languages.contains(lang) ) + return true; + } + return false; +} + +bool LayoutInfo::isLanguageSupportedByDefaultVariant(const QString& lang) const +{ + if( languages.contains(lang) ) + return true; + + if( languages.empty() && isLanguageSupportedByVariants(lang) ) + return true; + + return false; +} + +bool LayoutInfo::isLanguageSupportedByVariant(const VariantInfo* variantInfo, const QString& lang) const +{ + if( variantInfo->languages.contains(lang) ) + return true; + + // if variant has no languages try to "inherit" them from layout + if( variantInfo->languages.empty() && languages.contains(lang) ) + return true; + + return false; +} diff --git a/kcms/keyboard/xkb_rules.h b/kcms/keyboard/xkb_rules.h new file mode 100644 index 0000000000..2be856246c --- /dev/null +++ b/kcms/keyboard/xkb_rules.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef XKB_RULES_H_ +#define XKB_RULES_H_ + +#include +#include +#include + + +struct ConfigItem { + QString name; + QString description; +}; + +template +inline T* findByName(QList list, QString name) { + foreach(T* info, list) { + if( info->name == name ) + return info; + } + return NULL; +} + +struct VariantInfo: public ConfigItem { + QList languages; + const bool fromExtras; + + VariantInfo(bool fromExtras_): + fromExtras(fromExtras_) {} +}; + +struct LayoutInfo: public ConfigItem { + QList variantInfos; + QList languages; + const bool fromExtras; + +// LayoutInfo() {} + LayoutInfo(bool fromExtras_): + fromExtras(fromExtras_) {} + ~LayoutInfo() { foreach(VariantInfo* variantInfo, variantInfos) delete variantInfo; } + + VariantInfo* getVariantInfo(const QString& variantName) const { + return findByName(variantInfos, variantName); + } + + bool isLanguageSupportedByLayout(const QString& lang) const; + bool isLanguageSupportedByDefaultVariant(const QString& lang) const; + bool isLanguageSupportedByVariants(const QString& lang) const; + bool isLanguageSupportedByVariant(const VariantInfo* variantInfo, const QString& lang) const; +}; + +struct ModelInfo: public ConfigItem { + QString vendor; +}; + +struct OptionInfo: public ConfigItem { +}; + +struct OptionGroupInfo: public ConfigItem { + QList optionInfos; + bool exclusive; + + ~OptionGroupInfo() { foreach(OptionInfo* opt, optionInfos) delete opt; } + + const OptionInfo* getOptionInfo(const QString& optionName) const { + return findByName(optionInfos, optionName); + } +}; + +struct Rules { + enum ExtrasFlag { NO_EXTRAS, READ_EXTRAS }; + + static const char XKB_OPTION_GROUP_SEPARATOR; + + QList layoutInfos; + QList modelInfos; + QList optionGroupInfos; + QString version; + + Rules(); + + ~Rules() { + foreach(LayoutInfo* layoutInfo, layoutInfos) delete layoutInfo; + foreach(ModelInfo* modelInfo, modelInfos) delete modelInfo; + foreach(OptionGroupInfo* optionGroupInfo, optionGroupInfos) delete optionGroupInfo; + } + + const LayoutInfo* getLayoutInfo(const QString& layoutName) const { + return findByName(layoutInfos, layoutName); + } + + const OptionGroupInfo* getOptionGroupInfo(const QString& optionGroupName) const { + return findByName(optionGroupInfos, optionGroupName); + } + + static Rules* readRules(ExtrasFlag extrasFlag); + static Rules* readRules(Rules* rules, const QString& filename, bool fromExtras); + static QString getRulesName(); +}; + +#endif /* XKB_RULES_H_ */ diff --git a/kcms/keys/CMakeLists.txt b/kcms/keys/CMakeLists.txt new file mode 100644 index 0000000000..f9c4d6dead --- /dev/null +++ b/kcms/keys/CMakeLists.txt @@ -0,0 +1,53 @@ +########### next target ############### + +set(kcm_keys_PART_SRCS + kglobalshortcutseditor.cpp + globalshortcuts.cpp + select_scheme_dialog.cpp + kglobalaccel_interface.cpp + kglobalaccel_component_interface.cpp + export_scheme_dialog.cpp + ) + +qt5_wrap_ui( kcm_keys_PART_SRCS + export_scheme_dialog.ui + kglobalshortcutseditor.ui + select_scheme_dialog.ui ) + +set(kglobalaccel_xml ${KGLOBALACCEL_DBUS_INTERFACES_DIR}/kf5_org.kde.KGlobalAccel.xml) +set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo.h") +qt5_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_xml} kglobalaccel_interface ) + +set(kglobalaccel_component_xml ${KGLOBALACCEL_DBUS_INTERFACES_DIR}/kf5_org.kde.kglobalaccel.Component.xml) +set_source_files_properties(${kglobalaccel_component_xml} PROPERTIES INCLUDE "kglobalshortcutinfo.h") +qt5_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_component_xml} kglobalaccel_component_interface ) + +add_library(kcm_keys MODULE ${kcm_keys_PART_SRCS}) +target_link_libraries(kcm_keys + Qt5::DBus + KF5::KCMUtils + KF5::GlobalAccel + KF5::I18n + KF5::IconThemes + KF5::KIOWidgets + KF5::XmlGui + KF5::KDE4Support +) + +install(TARGETS kcm_keys DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES keys.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES + schemes/kde3.kksrc + schemes/kde4.kksrc + schemes/mac4.kksrc + schemes/unix3.kksrc + schemes/win3.kksrc + schemes/win4.kksrc + schemes/wm3.kksrc + DESTINATION ${DATA_INSTALL_DIR}/kcmkeys ) + + diff --git a/kcms/keys/ChangeLog b/kcms/keys/ChangeLog new file mode 100644 index 0000000000..67c21160db --- /dev/null +++ b/kcms/keys/ChangeLog @@ -0,0 +1,29 @@ +2005-09-30 Benjamin Meyer + * Removed Command Shortcuts, it is already in kmenuedit + +1999-08-19 Duncan Haldane + * removed left-over commented out code from change + decribed below, and adjusted help doc names to + index-1.html + +1999-02-28 Duncan Haldane + * commented out those unnecessary debug calls. + in keyconfig.cpp + +1998-12-19 Duncan Haldane + * Converted global.cpp, global.h to keyconfig.cpp, + keyconfig.h, that can now be used to configure both + the standard keys and the global keys + in the same sophisticated manner as + global.cpp did for just the global keys. + * converted main.cpp to use keyconfig.cpp rather than + global.cpp and standard.cpp for standard and globall + key configuration. KGlobalConfig and KStdConfig disappear. + (KGlobalConfig is renamed to KKeyConfig) + * appropriate changes to Makefile.am. + * standard key binding are now #include'd from + stdbindings.cpp + * standard.cpp, standard.h are left here for now. + The entries that used to use them in main.cpp and + Makefile.am are just commented out for now. + diff --git a/kcms/keys/Messages.sh b/kcms/keys/Messages.sh new file mode 100644 index 0000000000..78a9c48f26 --- /dev/null +++ b/kcms/keys/Messages.sh @@ -0,0 +1,7 @@ +#! /usr/bin/env bash +# customkeys=`grep "^.include .\.\." keyconfig.cpp | sed -e "s#.*\"\(.*\)\"#\1#"` +# $XGETTEXT *.cpp $customkeys -o $podir/kcmkeys.pot +$EXTRACTRC *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcmkeys.pot +rm -f rc.cpp + diff --git a/kcms/keys/README b/kcms/keys/README new file mode 100644 index 0000000000..14e09f73ea --- /dev/null +++ b/kcms/keys/README @@ -0,0 +1,7 @@ +CHANGES V0.2 +- Global keys stored by default in ~/.kde/share/config/kdeglobals + [Global Keys] group +- KKeyDialog checks new key choices against exising bindings for the widget +and against entries in .kderc [Global Keys] +- kcmkeys now has two standard kcontrol pages - one for standard desktop +accelerators and one for global keybindings. diff --git a/kcms/keys/export_scheme_dialog.cpp b/kcms/keys/export_scheme_dialog.cpp new file mode 100644 index 0000000000..b43fbce65b --- /dev/null +++ b/kcms/keys/export_scheme_dialog.cpp @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2009 Michael Jansen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "export_scheme_dialog.h" + +#include +#include + + +ExportSchemeDialog::ExportSchemeDialog(QStringList components, QWidget *parent) + : KDialog(parent), + ui(), + mComponents(components) + { + QWidget *w = new QWidget(this); + ui.setupUi(w); + setMainWidget(w); + + // We allow to check more than one button + mButtons.setExclusive(false); + + // A grid layout for the buttons + QGridLayout *vb = new QGridLayout(this); + + int item=0; + Q_FOREACH(QString component, mComponents) + { + QCheckBox *cb = new QCheckBox(component); + vb->addWidget(cb, item / 2, item % 2); + mButtons.addButton(cb, item); + ++item; + } + + ui.components->setLayout(vb); + } + + +ExportSchemeDialog::~ExportSchemeDialog() + {} + + +QStringList ExportSchemeDialog::selectedComponents() const + { + QStringList rc; + Q_FOREACH(QAbstractButton const *button, mButtons.buttons()) + { + if (button->isChecked()) + { + // Remove the '&' added by KAcceleratorManager magically + rc.append(KLocalizedString::removeAcceleratorMarker(button->text())); + } + } + return rc; + } + + +#include "moc_export_scheme_dialog.cpp" diff --git a/kcms/keys/export_scheme_dialog.h b/kcms/keys/export_scheme_dialog.h new file mode 100644 index 0000000000..8cc8c66342 --- /dev/null +++ b/kcms/keys/export_scheme_dialog.h @@ -0,0 +1,59 @@ +#ifndef EXPORT_SCHEME_DIALOG_H +#define EXPORT_SCHEME_DIALOG_H +/** + * Copyright (C) 2009 Michael Jansen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_export_scheme_dialog.h" + +#include +#include + +/** + * @author Michael Jansen + */ +class ExportSchemeDialog : public KDialog + { + Q_OBJECT + +public: + + ExportSchemeDialog (QStringList components, QWidget *parent=NULL); + + virtual ~ExportSchemeDialog(); + + // Get the list of currently selected components + QStringList selectedComponents() const; + +private: + + Ui::ExportSchemeDialog ui; + + // list of components to choose from + QStringList mComponents; + + // list of buttons for the components + QButtonGroup mButtons; + + }; // ExportSchemeDialog + + + + +#endif /* EXPORT_SCHEME_DIALOG_H */ + diff --git a/kcms/keys/export_scheme_dialog.ui b/kcms/keys/export_scheme_dialog.ui new file mode 100644 index 0000000000..4e8651ee60 --- /dev/null +++ b/kcms/keys/export_scheme_dialog.ui @@ -0,0 +1,35 @@ + + + ExportSchemeDialog + + + + 0 + 0 + 400 + 300 + + + + + + + Select the Components to Export + + + Qt::AlignCenter + + + + + + + Components + + + + + + + + diff --git a/kcms/keys/globalshortcuts.cpp b/kcms/keys/globalshortcuts.cpp new file mode 100644 index 0000000000..89e671848d --- /dev/null +++ b/kcms/keys/globalshortcuts.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2007 Andreas Pakulat + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "globalshortcuts.h" + +#include "kglobalshortcutseditor.h" + +#include +#include +#include + + +#include + + +K_PLUGIN_FACTORY(GlobalShortcutsModuleFactory, registerPlugin();) +K_EXPORT_PLUGIN(GlobalShortcutsModuleFactory("kcmkeys")) + + +GlobalShortcutsModule::GlobalShortcutsModule(QWidget *parent, const QVariantList &args) + : KCModule(parent, args), + editor(0) +{ + KCModule::setButtons(KCModule::Buttons(KCModule::Default | KCModule::Apply | KCModule::Help)); + + + // Create the kglobaleditor + editor = new KGlobalShortcutsEditor(this, KShortcutsEditor::GlobalAction); + connect(editor, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); + + // Layout the hole bunch + QVBoxLayout *global = new QVBoxLayout; + global->addWidget(editor); + setLayout(global); +} + +GlobalShortcutsModule::~GlobalShortcutsModule() +{} + + +void GlobalShortcutsModule::load() +{ + editor->load(); +} + + +void GlobalShortcutsModule::defaults() +{ + switch (KMessageBox::questionYesNoCancel( + this, + i18n("You are about to reset all shortcuts to their default values."), + i18n("Reset to defaults"), + KGuiItem(i18n("Current Component")), + KGuiItem(i18n("All Components")))) + { + case KMessageBox::Yes: + editor->defaults(KGlobalShortcutsEditor::CurrentComponent); + break; + + case KMessageBox::No: + editor->defaults(KGlobalShortcutsEditor::AllComponents); + break; + + default: + return; + } +} + + +void GlobalShortcutsModule::save() +{ + editor->save(); +} + + +#include "globalshortcuts.moc" diff --git a/kcms/keys/globalshortcuts.h b/kcms/keys/globalshortcuts.h new file mode 100644 index 0000000000..428c0335d3 --- /dev/null +++ b/kcms/keys/globalshortcuts.h @@ -0,0 +1,44 @@ +/* + * Copyright 2007 Andreas Pakulat + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SHORTCUTS_MODULE_H +#define SHORTCUTS_MODULE_H + +#include +#include + +class KGlobalShortcutsEditor; + +class GlobalShortcutsModule : public KCModule +{ + Q_OBJECT +public: + GlobalShortcutsModule(QWidget *parent, const QVariantList &args); + ~GlobalShortcutsModule(); + + virtual void save(); + virtual void load(); + virtual void defaults(); + +private: + KGlobalShortcutsEditor *editor; +}; + + +#endif diff --git a/kcms/keys/keys.desktop b/kcms/keys/keys.desktop new file mode 100644 index 0000000000..76d1b622f4 --- /dev/null +++ b/kcms/keys/keys.desktop @@ -0,0 +1,229 @@ +[Desktop Entry] +Exec=kcmshell5 keys +Icon=preferences-desktop-keyboard-shortcut +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/keys/index.html + +X-KDE-Library=kcm_keys +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=shortcuts-and-gestures +X-KDE-Weight=160 + +Name=Global Keyboard Shortcuts +Name[ar]=اختصارات لوحة المفاتيح العمومية +Name[ast]=Accesos rápidos de tecláu globales +Name[be@latin]=Paŭsiudnyja klavijaturnyja skaroty +Name[bg]=Общи бързи клавиши +Name[bn]=গ্লোবাল কীবোর্ড শর্টকাট +Name[bn_IN]=কি-বোর্ডের সার্বজনীন শর্ট-কাট +Name[bs]=Globalne prečice sa tastature +Name[ca]=Dreceres de teclat globals +Name[ca@valencia]=Dreceres de teclat globals +Name[cs]=Globální klávesové zkratky +Name[csb]=Globalné klawiszowé skrodzënë +Name[da]=Globale tastaturgenveje +Name[de]=Globale Kurzbefehle +Name[el]=Καθολικές συντομεύσεις πληκτρολογίου +Name[en_GB]=Global Keyboard Shortcuts +Name[eo]=Ĝeneralaj Klavkombinoj +Name[es]=Accesos rápidos de teclado globales +Name[et]=Globaalsed kiirklahvid +Name[eu]=Laster-tekla orokorrak +Name[fa]=میان‌برهای صفحه‌ کلید سراسری +Name[fi]=Työpöydänlaajuiset pikanäppäimet +Name[fr]=Raccourcis globaux de clavier +Name[fy]=Globale fluchkeppelings +Name[ga]=Aicearraí Comhchoiteanna Méarchláir +Name[gl]=Atallos de teclado globais +Name[gu]=વૈશ્વીક કીબોર્ડ ટૂંકાણો +Name[he]=קיצורי מקשים גלובליים +Name[hi]=वैश्विक कुंजीपट शॉर्टकट +Name[hne]=वैस्विक कुंजीपट सार्टकट +Name[hr]=Globalni prečaci tipkovnice +Name[hsb]=Globalne tastaturowe skrótšenki +Name[hu]=Globális billentyűparancsok +Name[ia]=Vias breve global de claviero +Name[id]=Jalan Pintas Papan Ketik Global +Name[is]=Víðværir flýtilyklar +Name[it]=Scorciatoie globali della tastiera +Name[ja]=グローバルキーボードショートカット +Name[kk]=Жалпы жүйелік перне тіркесімдер +Name[km]=ផ្លូវកាត់​ក្ដារចុច​​សកល +Name[kn]=ಸಾರ್ವತ್ರಿಕ ಕೀಲಿಮಣೆ ಶೀಘ್ರಮಾರ್ಗಗಳು (ಶಾರ್ಟ್ ಕಟ್) +Name[ko]=전역 키보드 단축키 +Name[ku]=Kurteriyên Klavyeyê yên Giştî +Name[lt]=Globalieji spartieji klavišai +Name[lv]=Globālie tastatūras īsceļi +Name[mai]=वैश्विक कुंजीपटल शार्टकट +Name[mk]=Глобални кратенки на тастатурата +Name[ml]=ആഗോളമായ കീബോര്‍ഡിലെ കുറക്കുവഴികള്‍ +Name[mr]=जागतिक कळफलक शॉर्टकट +Name[nb]=Globale hurtigtaster +Name[nds]=Globaal Tastkombinatschonen +Name[nl]=Globale sneltoetsen +Name[nn]=Globale snøgg­tastar +Name[or]=ଜାଗତିକ କିବୋର୍ଡ଼ ସଂକ୍ଷିପ୍ତ ପଥ +Name[pa]=ਗਲੋਬਲ ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ +Name[pl]=Globalne skróty klawiszowe +Name[pt]=Atalhos Globais do Teclado +Name[pt_BR]=Atalhos de teclado globais +Name[ro]=Acceleratori de tastatură globali +Name[ru]=Глобальные комбинации клавиш +Name[si]=ගෝලීය යතුරුපුවරු කෙටිමං +Name[sk]=Globálne klávesové skratky +Name[sl]=Splošne tipkovne bližnjice +Name[sr]=Глобалне пречице са тастатуре +Name[sr@ijekavian]=Глобалне пречице са тастатуре +Name[sr@ijekavianlatin]=Globalne prečice sa tastature +Name[sr@latin]=Globalne prečice sa tastature +Name[sv]=Globala snabbtangenter +Name[ta]=பொது விசைப்பலகை குறுக்குவழிகள் +Name[te]=గ్లౌబల్ కీబోర్‍డ్ లఘువులు +Name[tg]=Глобальные комбинации клавиш +Name[th]=ปุ่มพิมพ์ลัดใช้งานส่วนรวม +Name[tr]=Genel Klavye Kısayolları +Name[ug]=ئومۇمىيەت ھەرپتاختا تېزلەتمىسى +Name[uk]=Загальні клавіатурні скорочення +Name[vi]=Phím tắt toàn hệ thống +Name[wa]=Rascourtis del taprece po ttavå +Name[x-test]=xxGlobal Keyboard Shortcutsxx +Name[zh_CN]=全局键盘快捷键 +Name[zh_TW]=全域鍵盤捷徑 + +Comment=Configuration of keybindings +Comment[af]=Opstelling van sleutel bindinge +Comment[ar]=إعداد ارتباطات المفاتيح +Comment[ast]=Configuración de les asociaciones de les tecles +Comment[be]=Настаўленні клавішных скаротаў +Comment[be@latin]=Nałady dla poviaziaŭ klavišaŭ z roznymi aperacyjami +Comment[bg]=Настройки на бързи клавиши +Comment[bn]=কী-বাইন্ডিং কনফিগারেশন +Comment[bn_IN]=কি-বাইন্ডিং কনফিগারেশন +Comment[br]=Kefluniañ ereoù stokell +Comment[bs]=Postava prečica sa tastature +Comment[ca]=Configuració dels lligams de tecles +Comment[ca@valencia]=Configuració dels lligams de tecles +Comment[cs]=Nastavení klávesových zkratek +Comment[csb]=Nastôw klawiszowëch skrodzënów +Comment[cy]=Furfweddiad o rwymiadau bysell +Comment[da]=Indstilling af tastebindinger +Comment[de]=Einrichtung von Tastenzuordnungen +Comment[el]=Διαμόρφωση συνδυασμών πλήκτρων +Comment[en_GB]=Configuration of keybindings +Comment[eo]=Agordo de klavkombinoj +Comment[es]=Configuración de las asociaciones de las teclas +Comment[et]=Kiirklahvide seadistamine +Comment[eu]=Laster-teklen konfigurazioa +Comment[fa]=پیکربندی مقیدسازیهای کلید +Comment[fi]=Näppäinsidosten asetukset +Comment[fr]=Configuration des combinaisons de touches +Comment[fy]=Ynstellings foar fluchtoetsen +Comment[ga]=Cumraíocht ceangal eochracha +Comment[gl]=Configuración dos atallos de teclado +Comment[gu]=કીજોડાણોનું રૂપરેખાંકન +Comment[he]=הגדרות מיפוי מקשים +Comment[hi]=की-बाइंडिंग का कॉन्फ़िगरेशन +Comment[hne]=की-बाइंडिंग के कान्फिगरेसन +Comment[hr]=Konfiguriranje prečaca tipkovnice +Comment[hsb]=Připrawjenje přirjadowanjow tastow +Comment[hu]=A billentyűparancsok beállításai +Comment[ia]=Configuration de keybindings (ligamines de claves) +Comment[id]=Konfigurasi pengikat tombol +Comment[is]=Stillingar altækra flýtihnappa +Comment[it]=Configurazione delle associazioni di tasti +Comment[ja]=キーバインディングの設定 +Comment[ka]=კლავიშთა წყვილების კონფიგურაცია +Comment[kk]=Перне тіркесімдерді баптау +Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​ការ​ចង​គ្រាប់ចុច +Comment[kn]=ಕೀಲಿ ನಿಬಂಧಗಳ (ಬೈಂಡಿಂಗ್ಸ್) ಸಂರಚನೆ +Comment[ko]=키 바인딩 설정 +Comment[ku]=Veavakirina bicîkirina bişkojkan +Comment[lt]=Klavišų siečių derinimas +Comment[lv]=Taustiņsasaistes konfigurēšana +Comment[mai]=की-बाइंडिंग क' कान्फिगरेशन +Comment[mk]=Конфигурација на врски меѓу копчиња и акции +Comment[ml]=കീബൈന്‍ഡിങ്സിനുള്ള ക്രമീകരണം +Comment[mr]=कीबाइंडिंगची संयोजना +Comment[ms]=Konfigurasi untuk pengikatan kekunci +Comment[nb]=Oppsett av hurtigtaster +Comment[nds]=Instellen vun Tastkombinatschonen +Comment[ne]=कुञ्जी बाइन्डिङहरूको कन्फिगरेसन +Comment[nl]=Instellingen voor sneltoetsen +Comment[nn]=Oppsett av snøggtastar +Comment[or]=କିବନ୍ଧନଗୁଡ଼ିକର ସଂରଚନା +Comment[pa]=ਕੀ-ਬਾਇਡਿੰਗ ਦੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja powiązań klawiszowych +Comment[pt]=Configuração das teclas +Comment[pt_BR]=Configuração dos atalhos de teclado +Comment[ro]=Configurează asocierile de taste +Comment[ru]=Настройка глобальных комбинаций клавиш +Comment[se]=Jođánisboaluid heiveheapmi +Comment[si]=යතුරු බැඳුම් වල සැකසුම +Comment[sk]=Nastavenie globálnych klávesových skratiek +Comment[sl]=Nastavitve tipkovnih bližnjic +Comment[sr]=Постава пречица са тастатуре +Comment[sr@ijekavian]=Постава пречица са тастатуре +Comment[sr@ijekavianlatin]=Postava prečica sa tastature +Comment[sr@latin]=Postava prečica sa tastature +Comment[sv]=Anpassa kortkommandon +Comment[ta]=விசைசேர்வுகளுக்கான வடிவமைப்பு +Comment[te]=కీ భందనముల యొక్క ఆకృతీకరణ +Comment[tg]=Танзимоти тугмаҳои якҷояшуда +Comment[th]=ปรับแต่งปุ่มพิมพ์ลัดต่าง ๆ +Comment[tr]=Tuş bağıntılarının yapılandırılması +Comment[ug]=كۇنۇپكا باغلاش سەپلىمە +Comment[uk]=Налаштування прив'язок клавіш +Comment[uz]=Tugmalar birikmasini moslash +Comment[uz@cyrillic]=Тугмалар бирикмасини мослаш +Comment[vi]=Cấu hình các tổ hợp phím +Comment[wa]=Apontiaedje des rascourtis di tapes +Comment[xh]=Uqwalaselo lwezibopheleo zesitshixo +Comment[x-test]=xxConfiguration of keybindingsxx +Comment[zh_CN]=键绑定配置 +Comment[zh_TW]=按鍵關聯設定 + +X-KDE-Keywords=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts +X-KDE-Keywords[bs]=Tipke, Globalni vezovi tipki, Šema tipki, Veze tipki, Prečice, Prečice za aplikacije, globalne Prečice +X-KDE-Keywords[ca]=Tecles,Assignació de tecles globals,Esquema de tecles,Assignació de tecles,dreceres,dreceres d'aplicació,dreceres globals +X-KDE-Keywords[ca@valencia]=Tecles,Assignació de tecles globals,Esquema de tecles,Assignació de tecles,dreceres,dreceres d'aplicació,dreceres globals +X-KDE-Keywords[da]=Taster,globale tastebindinger,tasteskema,tastebindinger,genveje,programgenveje,globale genveje +X-KDE-Keywords[de]=Tasten,Tastenzuordnung,Tastenkürzel,Kurzbefehle,Tastenschema,Tastaturlayout +X-KDE-Keywords[el]=Πλήκτρα,δεσμεύσεις καθολικών πλήκτρων, σχήμα πλήκτρων, δεσμεύσεις πλήκτρων,συντομεύσεις,συντομεύσεις εφαρμογών,καθολικές συντομεύσεις +X-KDE-Keywords[es]=Teclas,Asociaciones globales de teclas,Esquema de teclas,Asociaciones de teclas,accesos rápidos,accesos rápidos de aplicaciones,accesos rápidos globales +X-KDE-Keywords[et]=klahvid,globaalsed kiirklahvid,klaviatuuriskeem,klahviseosed,kiirklahvid,rakenduse kiirklahvid,globaalsed kiirklahvid +X-KDE-Keywords[eu]=tekla,laster-tekla orokor,tekla-eskema,laster-tekla,lasterbide,aplikazioen laster-teklak +X-KDE-Keywords[fi]=näppäimet,globaalit näppäinsidokset,yleiset näppäinsidokset,työpöydänlaajuiset näppäinsidokset,globaalit pikanäppäimet,työpöydänlaajuiset pikanäppäimet,globaalit näppäinyhdistelmät,työpöydänlaajuiset näppäinyhdistelmät,näppäimistön yleispikavalinnat,näppäimistön globaalit pikavalinnat,näppäinsidokset,pikanäppäimet,näppäimistön pikavalinnat, näppäinyhdistelmät,näppäinoikotiet,sovellusten pikanäppäimet,sovellusten näppäinyhdistelmät,sovellusten näppäinoikotiet +X-KDE-Keywords[fr]=Touches, Association globale des touches, Schéma des touches, Association de touches, Raccourci des applications, Raccourcis globaux +X-KDE-Keywords[ga]=Eochracha,Ceangail chomhchoiteanna eochracha,Scéim eochracha,Ceangail eochracha,aicearraí,aicearraí feidhmchláir,aicearraí comhchoiteanna +X-KDE-Keywords[gl]=tecla, atallo, asociación, esquema de teclas, atallo de teclado, asociación de teclas, atallo a programa, atallos globais +X-KDE-Keywords[hu]=Billentyűk,Globális gyorsbillentyűk,Billentyűséma,Gyorsbillentyűk,alkalmazás gyorsbillentyűk,globális gyorsbillentyűk +X-KDE-Keywords[ia]=Claves,ligamines de clave global,schema de clave,ligamines de clave,vias breve,vias breve de application,vias breve global +X-KDE-Keywords[it]=tasti,scorciatoie globali di tastiera,schema di tasti,scorciatoie,scorciatoie delle applicazioni,scorciatoie globali +X-KDE-Keywords[kk]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts +X-KDE-Keywords[km]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts +X-KDE-Keywords[ko]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,키,전역 키 바인딩,키 바인딩,단축키,프로그램 단축키 +X-KDE-Keywords[mr]=कीज, ग्लोबल की बाईंडिंगज, की स्कीम, की बाईंडिंगज, शॉर्टकटस, अप्लिकेशन शॉर्टकटस, ग्लोबल शॉर्टकटस +X-KDE-Keywords[nb]=Taster,Globale tastebindingeer,Tastaturutforming,Tastebindinger,snarveier,programsnarveier,globale snarveier +X-KDE-Keywords[nds]=Tasten,Globaal Tasttowiesen,Tasttowiesen,Tastkombinatschonen,Programm-Tastkombinatschonen,Globaal Tastkombinatschonen +X-KDE-Keywords[nl]=toetsen,globale toetsbindingen,toetsenschema,toetsbindingen,sneltoetsen,sneltoetsen van toepassing,globale sneltoetsen +X-KDE-Keywords[pl]=Klawisze,Globalne powiązania klawiszy,Motyw klawiszy,Powiązania klawiszy,skróty,skróty programów,globalne skróty +X-KDE-Keywords[pt]=Teclas,combinações de teclas,global,esquema de teclas,atalhos de teclado,atalhos,atalhos das aplicações +X-KDE-Keywords[pt_BR]=Teclas,combinações de teclas global,esquema de teclas,atalhos de teclado,atalhos,atalhos dos aplicativos, atalhos globais +X-KDE-Keywords[ru]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,клавиши,глобальные привязки клавиш,схема,привязки клавиш,комбинации клавиш,комбинации клавиш в приложениях,глобальные привязки клавиш +X-KDE-Keywords[sk]=Klávesy,Globálne klávesové skratky,Klávesová schéma,Klávesové skratky,skratky,skratky aplikácie,globálne skratky +X-KDE-Keywords[sl]=tipke,splošne tipkovne bližnjice,tipkovna shema,shema tipk,tipkovne bližnjice,vezave tipk,bližnjice,programske bližnjice,splošne bližnjice +X-KDE-Keywords[sr]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,тастери,глобалне свезе тастера,шема тастера,свезе тастера,пречице,пречице програма,глобалне пречице +X-KDE-Keywords[sr@ijekavian]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,тастери,глобалне свезе тастера,шема тастера,свезе тастера,пречице,пречице програма,глобалне пречице +X-KDE-Keywords[sr@ijekavianlatin]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,tasteri,globalne sveze tastera,šema tastera,sveze tastera,prečice,prečice programa,globalne prečice +X-KDE-Keywords[sr@latin]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,tasteri,globalne sveze tastera,šema tastera,sveze tastera,prečice,prečice programa,globalne prečice +X-KDE-Keywords[sv]=Tangenter,Globala tangentbindingar,Tangentschema,Tangentbindingar,genvägar,programgenvägar,globala genvägar +X-KDE-Keywords[tr]=Tuşlar,Genel kısayollar,Klavye şeması,Klavye Kısayolları,kısayollar,uygulama kısayolları,genel kısayollar, kısayol tuşları +X-KDE-Keywords[uk]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,клавіатура,скорочення,клавіатурні скорочення,схема,загальні клавіатурні скорочення,клавіатурні скорочення програми,загальні скорочення +X-KDE-Keywords[vi]=Phím,tổ hợp phím toàn cục,phối hợp phím,tổ hợp phím,gõ tắt,gõ tắt cho ứng dụng +X-KDE-Keywords[x-test]=xxKeys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcutsxx +X-KDE-Keywords[zh_CN]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts,按键,全局键绑定,按键方案,按键绑定,快捷键,应用程序,全局快捷键 +X-KDE-Keywords[zh_TW]=Keys,Global key bindings,Key scheme,Key bindings,shortcuts,application shortcuts,global shortcuts + +Categories=Qt;KDE;X-KDE-settings-accessibility; diff --git a/kcms/keys/kglobalshortcutseditor.cpp b/kcms/keys/kglobalshortcutseditor.cpp new file mode 100644 index 0000000000..d52396dceb --- /dev/null +++ b/kcms/keys/kglobalshortcutseditor.cpp @@ -0,0 +1,673 @@ +/* + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kglobalshortcutseditor.h" + +#include "ui_kglobalshortcutseditor.h" +#include "export_scheme_dialog.h" +#include "select_scheme_dialog.h" +#include "globalshortcuts.h" +#include "kglobalaccel_interface.h" +#include "kglobalaccel_component_interface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * README + * + * This class was created because the kshortcutseditor class has some shortcomings. That class uses + * QTreeWidget and therefore makes it impossible for an outsider to switch the models. But the + * global shortcuts editor did that. Each global component ( kded, krunner, kopete ... ) was + * destined to be separately edited. If you selected another component the kshortcutseditor was + * cleared and refilled. But the items take care of undoing. Therefore when switching the component + * you lost the undo history. + * + * To solve that problem this class keeps one kshortcuteditor for each component. That is easier + * than rewrite that dialog to a model/view framework. + * + * It perfectly covers a bug of KExtedableItemDelegate when clearing and refilling the associated + * model. + */ + +class ComponentData + { + +public: + + ComponentData( + const QString &uniqueName, + const QDBusObjectPath &path, + KShortcutsEditor *_editor); + + ~ComponentData(); + + QString uniqueName() const; + KShortcutsEditor *editor(); + QDBusObjectPath dbusPath(); + +private: + + QString _uniqueName; + QDBusObjectPath _path; + QPointer _editor; + }; + + +ComponentData::ComponentData( + const QString &uniqueName, + const QDBusObjectPath &path, + KShortcutsEditor *editor) + : _uniqueName(uniqueName), + _path(path), + _editor(editor) + {} + + +ComponentData::~ComponentData() + { + delete _editor; _editor = 0; + } + + +QString ComponentData::uniqueName() const + { + return _uniqueName; + } + + +QDBusObjectPath ComponentData::dbusPath() + { + return _path; + } + + +KShortcutsEditor *ComponentData::editor() + { + return _editor; + } + + +class KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate +{ +public: + + KGlobalShortcutsEditorPrivate(KGlobalShortcutsEditor *q) + : q(q), + stack(0), + bus(QDBusConnection::sessionBus()) + {} + + //! Setup the gui + void initGUI(); + + //! Load the component at @a componentPath + bool loadComponent(const QDBusObjectPath &componentPath); + + //! Return the componentPath for component + QDBusObjectPath componentPath(const QString &componentUnique); + + //! Remove the component + void removeComponent(const QString &componentUnique); + + KGlobalShortcutsEditor *q; + Ui::KGlobalShortcutsEditor ui; + QStackedWidget *stack; + KShortcutsEditor::ActionTypes actionTypes; + QHash components; + QDBusConnection bus; +}; + + +void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::initGUI() +{ + ui.setupUi(q); + // Create a stacked widget. + stack = new QStackedWidget(q); + q->layout()->addWidget(stack); + + // Connect our components + connect(ui.components, SIGNAL(activated(QString)), + q, SLOT(activateComponent(QString))); + + // Build the menu + QMenu *menu = new QMenu(q); + menu->addAction( QIcon::fromTheme("document-import"), i18n("Import Scheme..."), q, SLOT(importScheme())); + menu->addAction( QIcon::fromTheme("document-export"), i18n("Export Scheme..."), q, SLOT(exportScheme())); + menu->addAction( i18n("Set All Shortcuts to None"), q, SLOT(clearConfiguration())); + QAction *action = menu->addAction( QIcon::fromTheme("edit-delete"), i18n("Remove Component")); + connect(action, &QAction::triggered, [this]() { + QString name = ui.components->currentText(); + QString componentUnique = components.value(name)->uniqueName(); + + // The confirmation text is different when the component is active + if (KGlobalAccel::isComponentActive(componentUnique)) { + if (KMessageBox::questionYesNo( + q, + i18n("Component '%1' is currently active. Only global shortcuts currently not active will be removed from the list.\n" + "All global shortcuts will reregister themselves with their defaults when they are next started.", componentUnique), + i18n("Remove component")) != KMessageBox::Yes) { + return; + } + } else { + if (KMessageBox::questionYesNo( + q, + i18n("Are you sure you want to remove the registered shortcuts for component '%1'? " + "The component and shortcuts will reregister themselves with their default settings" + " when they are next started.", + componentUnique), + i18n("Remove component")) != KMessageBox::Yes) { + return; + } + } + + // Initiate the removing of the component. + if (KGlobalAccel::cleanComponent(componentUnique)) { + + // Get the objectPath BEFORE we delete the source of it + QDBusObjectPath oPath = components.value(name)->dbusPath(); + // Remove the component from the gui + removeComponent(componentUnique); + + // Load it again + // ############# + if (loadComponent(oPath)) { + // Active it + q->activateComponent(name); + } + } + }); + + ui.menu_button->setMenu(menu); + + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(q); + proxyModel->setSourceModel(new QStandardItemModel(0, 1, proxyModel)); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + ui.components->setModel(proxyModel); +} + + +KGlobalShortcutsEditor::KGlobalShortcutsEditor(QWidget *parent, KShortcutsEditor::ActionTypes actionTypes) + : QWidget(parent), + d(new KGlobalShortcutsEditorPrivate(this)) +{ + d->actionTypes = actionTypes; + // Setup the ui + d->initGUI(); +} + + +KGlobalShortcutsEditor::~KGlobalShortcutsEditor() +{ + // Before closing the door, undo all changes + undo(); + qDeleteAll(d->components); + delete d; +} + + +void KGlobalShortcutsEditor::activateComponent(const QString &component) +{ + QHash::Iterator iter = d->components.find(component); + if (iter == d->components.end()) { + Q_ASSERT(iter != d->components.end()); + return; + } else { + int index = d->ui.components->findText(component); + Q_ASSERT(index != -1); + if (index > -1) { + // Known component. Get it. + d->ui.components->setCurrentIndex(index); + d->stack->setCurrentWidget((*iter)->editor()); + } + } +} + + +void KGlobalShortcutsEditor::addCollection( + KActionCollection *collection, + const QDBusObjectPath &objectPath, + const QString &id, + const QString &friendlyName) +{ + KShortcutsEditor *editor; + // Check if this component is known + QHash::Iterator iter = d->components.find(friendlyName); + if (iter == d->components.end()) { + // Unknown component. Create an editor. + editor = new KShortcutsEditor(this, d->actionTypes); + d->stack->addWidget(editor); + + // try to find one appropriate icon ( allowing NULL pixmap to be returned) + QPixmap pixmap = KIconLoader::global()->loadIcon(id, KIconLoader::Small, 0, + KIconLoader::DefaultState, QStringList(), 0, true); + // if NULL pixmap is returned, use the F.D.O "system-run" icon + if (pixmap.isNull()) { + pixmap = KIconLoader::global()->loadIcon("system-run", KIconLoader::Small); + } + + // Add to the component combobox + d->ui.components->addItem(pixmap, friendlyName); + d->ui.components->model()->sort(0); + + // Add to our component registry + ComponentData *cd = new ComponentData(id, objectPath, editor); + d->components.insert(friendlyName, cd); + + connect(editor, SIGNAL(keyChange()), this, SLOT(_k_key_changed())); + } else { + // Known component. + editor = (*iter)->editor(); + } + + // Add the collection to the editor of the component + editor->addCollection(collection, friendlyName); + + if (d->ui.components->count() > -1) { + d->ui.components->setCurrentIndex(0); + activateComponent(d->ui.components->itemText(0)); + } +} + + +void KGlobalShortcutsEditor::clearConfiguration() +{ + QString name = d->ui.components->currentText(); + d->components[name]->editor()->clearConfiguration(); +} + + +void KGlobalShortcutsEditor::defaults(ComponentScope scope) +{ + switch (scope) + { + case AllComponents: + Q_FOREACH (ComponentData *cd, d->components) { + // The editors are responsible for the reset + cd->editor()->allDefault(); + } + break; + + case CurrentComponent: { + QString name = d->ui.components->currentText(); + // The editors are responsible for the reset + d->components[name]->editor()->allDefault(); + } + break; + + default: + Q_ASSERT(false); + }; +} + + +void KGlobalShortcutsEditor::clear() +{ + // Remove all components and their associated editors + qDeleteAll(d->components); + d->components.clear(); + d->ui.components->clear(); +} + + +static bool compare(const QString &a, const QString &b) + { + return a.toLower().localeAwareCompare(b.toLower()) < 0; + } + + +void KGlobalShortcutsEditor::exportScheme() +{ + QStringList keys = d->components.keys(); + qSort(keys.begin(), keys.end(), compare); + ExportSchemeDialog dia(keys); + + if (dia.exec() != KMessageBox::Ok) { + return; + } + + QUrl url = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), QStringLiteral("*.kksrc")); + if (!url.isEmpty()) { + KConfig config(url.path(), KConfig::SimpleConfig); + // TODO: Bug ossi to provide a method for this + Q_FOREACH(const QString &group, config.groupList()) + { + // do not overwrite the Settings group. That makes it possible to + // update the standard scheme kksrc file with the editor. + if (group == "Settings") continue; + config.deleteGroup(group); + } + exportConfiguration(dia.selectedComponents(), &config); + } +} + + +void KGlobalShortcutsEditor::importScheme() +{ + // Check for unsaved modifications + if (isModified()) { + int choice = KMessageBox::warningContinueCancel( + this, + i18n("Your current changes will be lost if you load another scheme before saving this one"), + i18n("Load Shortcut Scheme"), + KGuiItem(i18n("Load"))); + if (choice != KMessageBox::Continue) { + return; + } + } + + SelectSchemeDialog dialog(this); + if (dialog.exec() != KDialog::Accepted) { + return; + } + + KUrl url = dialog.selectedScheme(); + if (!url.isLocalFile()) { + KMessageBox::sorry(this, i18n("This file (%1) does not exist. You can only select local files.", + url.url())); + return; + } + KConfig config(url.path(), KConfig::SimpleConfig); + importConfiguration(&config); +} + + +void KGlobalShortcutsEditor::load() +{ + // Connect to kglobalaccel. If that fails there is no need to continue. + qRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType(); + + org::kde::KGlobalAccel kglobalaccel( + "org.kde.kglobalaccel", + "/kglobalaccel", + d->bus); + + if (!kglobalaccel.isValid()) { + QString errorString; + QDBusError error = kglobalaccel.lastError(); + // The global shortcuts DBus service manages all global shortcuts and we + // can't do anything useful without it. + if (error.isValid()) { + errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); + } + + KMessageBox::sorry( + this, + i18n("Failed to contact the KDE global shortcuts daemon\n") + + errorString ); + return; + } + + // Undo all changes not yet applied + undo(); + clear(); + + QDBusReply< QList > componentsRc = kglobalaccel.allComponents(); + if (!componentsRc.isValid()) + { + // Sometimes error pop up only after the first real call. + QString errorString; + QDBusError error = componentsRc.error(); + // The global shortcuts DBus service manages all global shortcuts and we + // can't do anything useful without it. + if (error.isValid()) { + errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); + } + + KMessageBox::sorry( + this, + i18n("Failed to contact the KDE global shortcuts daemon\n") + + errorString ); + return; + } + QList components = componentsRc; + + Q_FOREACH(const QDBusObjectPath &componentPath, components) { + d->loadComponent(componentPath); + } // Q_FOREACH(component) +} + + +void KGlobalShortcutsEditor::save() +{ + // The editors are responsible for the saving + kDebug() << "Save the changes"; + Q_FOREACH (ComponentData *cd, d->components) { + cd->editor()->commit(); + } +} + + +void KGlobalShortcutsEditor::importConfiguration(KConfigBase *config) +{ + kDebug() << config->groupList(); + + // In a first step clean out the current configurations. We do this + // because we want to minimize the chance of conflicts. + Q_FOREACH (ComponentData *cd, d->components) { + KConfigGroup group(config, cd->uniqueName()); + kDebug() << cd->uniqueName() << group.name(); + if (group.exists()) { + kDebug() << "Removing" << cd->uniqueName(); + cd->editor()->clearConfiguration(); + } + } + + // Now import the new configurations. + Q_FOREACH (ComponentData *cd, d->components) { + KConfigGroup group(config, cd->uniqueName()); + if (group.exists()) { + kDebug() << "Importing" << cd->uniqueName(); + cd->editor()->importConfiguration(&group); + } + } +} + +void KGlobalShortcutsEditor::exportConfiguration(QStringList components, KConfig *config) const + { + Q_FOREACH (const QString &componentFriendly, components) + { + QHash::Iterator iter = d->components.find(componentFriendly); + if (iter == d->components.end()) + { + Q_ASSERT(iter != d->components.end()); + continue; + } + else + { + KConfigGroup group(config, (*iter)->uniqueName()); + (*iter)->editor()->exportConfiguration(&group); + } + } + } + + +void KGlobalShortcutsEditor::undo() +{ + // The editors are responsible for the undo + kDebug() << "Undo the changes"; + Q_FOREACH (ComponentData *cd, d->components) { + cd->editor()->undoChanges(); + } +} + + +bool KGlobalShortcutsEditor::isModified() const +{ + Q_FOREACH (ComponentData *cd, d->components) { + if (cd->editor()->isModified()) { + return true; + } + } + return false; +} + + +void KGlobalShortcutsEditor::_k_key_changed() +{ + emit changed(isModified()); +} + + +QDBusObjectPath KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::componentPath(const QString &componentUnique) +{ + return QDBusObjectPath(QLatin1String("/component/") + componentUnique); +} + + +bool KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::loadComponent(const QDBusObjectPath &componentPath) +{ + // Get the component + org::kde::kglobalaccel::Component component( + "org.kde.kglobalaccel", + componentPath.path(), + bus); + if (!component.isValid()) { + kDebug() << "Component " << componentPath.path() << "not valid! Skipping!"; + return false; + } + + // Get the shortcut contexts. + QDBusReply shortcutContextsRc = component.getShortcutContexts(); + if (!shortcutContextsRc.isValid()) { + kDebug() << "Failed to get contexts for component " + << componentPath.path() <<"! Skipping!"; + kDebug() << shortcutContextsRc.error(); + return false; + } + QStringList shortcutContexts = shortcutContextsRc; + + // We add the shortcuts for all shortcut contexts to the editor. This + // way the user keeps full control of it's shortcuts. + Q_FOREACH (const QString &shortcutContext, shortcutContexts) { + + QDBusReply< QList > shortcutsRc = + component.allShortcutInfos(shortcutContext); + if (!shortcutsRc.isValid()) + { + kDebug() << "allShortcutInfos() failed for " << componentPath.path() << shortcutContext; + continue; + } + QList shortcuts = shortcutsRc; + // Shouldn't happen. But you never know + if (shortcuts.isEmpty()) { + kDebug() << "Got shortcut context" << shortcutContext << "without shortcuts for" + << componentPath.path(); + continue; + } + + // It's safe now + const QString componentUnique = shortcuts[0].componentUniqueName(); + QString componentContextId = componentUnique; + // kglobalaccel knows that '|' is our separator between + // component and context + if (shortcutContext != "default") { + componentContextId += QString("|") + shortcutContext; + } + + // Create a action collection for our current component:context + KActionCollection* col = new KActionCollection( + q, + componentContextId); + + // Now add the shortcuts. + Q_FOREACH (const KGlobalShortcutInfo &shortcut, shortcuts) { + + const QString &objectName = shortcut.uniqueName(); + QAction *action = col->addAction(objectName); + action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction + action->setText(shortcut.friendlyName()); + + // Always call this to enable global shortcuts for the action. The editor widget + // checks it. + // Also actually loads the shortcut using the KAction::Autoloading mechanism. + // Avoid setting the default shortcut; it would just be written to the global + // configuration so we would not get the real one below. + KGlobalAccel::self()->setShortcut(action, QList()); + + // The default shortcut will never be loaded because it's pointless in a real + // application. There are no scarce resources [i.e. physical keys] to manage + // so applications can set them at will and there's no autoloading. + QList sc = shortcut.defaultKeys(); + if (sc.count()>0) { + KGlobalAccel::self()->setDefaultShortcut(action, sc); + } + } // Q_FOREACH(shortcut) + + QString componentFriendlyName = shortcuts[0].componentFriendlyName(); + + if (shortcuts[0].contextUniqueName() != "default") + { + componentFriendlyName += + QString('[') + shortcuts[0].contextFriendlyName() + QString(']'); + } + + q->addCollection(col, componentPath, componentContextId, componentFriendlyName ); + + } // Q_FOREACH(context) + + return true; +} + + +void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::removeComponent( + const QString &componentUnique ) + { + // TODO: Remove contexts too. + + Q_FOREACH (const QString &text, components.keys()) + { + if (components.value(text)->uniqueName() == componentUnique) + { + // Remove from QComboBox + int index = ui.components->findText(text); + Q_ASSERT(index != -1); + ui.components->removeItem(index); + + // Remove from QStackedWidget + stack->removeWidget(components[text]->editor()); + + // Remove the componentData + delete components.take(text); + } + } + } + + +#include "kglobalshortcutseditor.moc" diff --git a/kcms/keys/kglobalshortcutseditor.h b/kcms/keys/kglobalshortcutseditor.h new file mode 100644 index 0000000000..1889aa1650 --- /dev/null +++ b/kcms/keys/kglobalshortcutseditor.h @@ -0,0 +1,154 @@ +/* + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KGLOBALSHORTCUTSEDITOR_H +#define KGLOBALSHORTCUTSEDITOR_H + +#include + +#include "kshortcutseditor.h" + +class KActionCollection; +class KShortcut; +class KConfig; +class QDBusObjectPath; + +/** + * Combine a KShortcutsEditor with a KComboBox. + * + * @see KShortcutsEditor + * @author Michael Jansen + */ +class KGlobalShortcutsEditor : public QWidget +{ + Q_OBJECT + +public: + + /** + * Constructor + * + * @param parent parent widget + */ + KGlobalShortcutsEditor(QWidget *parent, + KShortcutsEditor::ActionTypes actionTypes = KShortcutsEditor::AllActions); + ~KGlobalShortcutsEditor(); + + /** + * Insert an action collection, i.e. add all it's actions to the ones already associated + * with the KShortcutsEditor object. + * + * @param collection the collection to add + * @param component title for the component + * @param title title for the subtree in the component + */ + void addCollection(KActionCollection *, const QDBusObjectPath &path, const QString &id, const QString &name); + + /** + * Clear all collections were currently hosting. Current changes are not + * undone? Do that before calling this method. + */ + void clear(); + + + /** + * Revert all changes made since the last save. + */ + void undo(); + + + /** + * Load the shortcuts from the configuration. + */ + void importConfiguration(KConfigBase *config); + + + /** + * Save the shortcuts to the configuration. + */ + void exportConfiguration(QStringList componentsFriendly, KConfig *config) const; + + + /** + * Are the unsaved changes? + */ + bool isModified() const; + + enum ComponentScope + { + AllComponents, + CurrentComponent + }; + +Q_SIGNALS: + + /** + * Indicate that state of the modules contents has changed. + * + * @param state changed or not + */ + void changed(bool); + + +public Q_SLOTS: + + /** + * Activate the component \a component. + * + * @param component the component + */ + void activateComponent(const QString &component); + + /** + * Set all shortcuts to none. + */ + void clearConfiguration(); + + /** + * Load/Reload the global shortcuts + */ + void load(); + + /** + * Make the changes persistent. + * + * That's function is not really saving. Global shortcuts are saved immediately. This + * prevent the undo on deleting the editor. + */ + void save(); + + /** + * Set shortcuts to their default value; + */ + void defaults(ComponentScope scope); + + virtual void importScheme(); + virtual void exportScheme(); + +private Q_SLOTS: + void _k_key_changed(); + +private: + + friend class KGlobalShortcutsEditorPrivate; + class KGlobalShortcutsEditorPrivate; + KGlobalShortcutsEditorPrivate *const d; + + Q_DISABLE_COPY(KGlobalShortcutsEditor) +}; // class KGlobalShortcutsEditor + +#endif // KGLOBALSHORTCUTSEDITOR_H diff --git a/kcms/keys/kglobalshortcutseditor.ui b/kcms/keys/kglobalshortcutseditor.ui new file mode 100644 index 0000000000..444103e2de --- /dev/null +++ b/kcms/keys/kglobalshortcutseditor.ui @@ -0,0 +1,57 @@ + + + KGlobalShortcutsEditor + + + + 0 + 0 + 612 + 516 + + + + + + + + + KDE component: + + + + + + + + 0 + 0 + + + + + + + + File + + + + + + + + + + KComboBox + QComboBox +
    kcombobox.h
    +
    +
    + + components + menu_button + + + +
    diff --git a/kcms/keys/schemes/kde3.kksrc b/kcms/keys/schemes/kde3.kksrc new file mode 100644 index 0000000000..336757d387 --- /dev/null +++ b/kcms/keys/schemes/kde3.kksrc @@ -0,0 +1,166 @@ +[Settings] +Name=KDE Default for 3 Modifier Keys +Name[af]=Standaard KDE vir 3 verandering sleutels +Name[ar]=مبدئي كدي لـ3 مفاتيح مغيرة +Name[ast]=Predetermináu de KDE de 3 modificadores de tecláu +Name[be]=Стандартны для KDE з трыма клавішамі мадыфікацыі +Name[be@latin]=Zmoŭčanaja schiema „KDE” dla 3 klavišnych madyfikataraŭ +Name[bg]=KDE (с 3 модификационни клавиша) +Name[ca]=Omissió del KDE per a 3 tecles modificadores +Name[ca@valencia]=Omissió del KDE per a 3 tecles modificadores +Name[cs]=Výchozí pro KDE (3 modifikátory) +Name[csb]=Domëslny nastôw KDE dlô 3 klawiszów zjinaczi +Name[cy]=Rhagosodion KDE i 3 Bysell Addasu +Name[da]=KDE-standard med 3 modifikatortaster +Name[de]=KDE-Standard für 3 Sondertasten +Name[el]=Προκαθορισμένο του KDE για 3 τροποποιητές +Name[en_GB]=KDE Default for 3 Modifier Keys +Name[eo]=KDE-defaŭlto por 3 modifoklavoj +Name[es]=Predeterminado de KDE de 3 modificadores de teclado +Name[et]=KDE vaikeskeem 3 muuteklahviga +Name[eu]=KDE lehenetsia 3 tekla modifikatzailekin +Name[fa]=پیش‌فرض KDE برای ۳ کلید تغییردهنده +Name[fi]=KDE oletus 3:lle muokkausnäppäimelle +Name[fr]=KDE par défaut pour 3 touches de modifications +Name[fy]=KDE-standert foar 3 modifikaasjetoetsen +Name[ga]=Réamhshocrú KDE le haghaidh trí eochair mhionathraithe +Name[gl]=Predeterminado de KDE para 3 teclas modificadoras +Name[gu]=૩ બદલેલ કળો માટે KDE મૂળભૂત +Name[he]=ברירת המחדל של KDE עבור 3 כפתורים משני מצב +Name[hi]=3 मॉडीफ़ायर कुंजियों के लिए केडीई डिफ़ॉल्ट +Name[hne]=3 माडीफायर कुंजी मन बर केडीई डिफाल्ट +Name[hr]=KDE zadano za 3 modifikatorske tipke +Name[hsb]=KDE-standard za tři modifikowace tasty +Name[hu]=KDE-s alapértelmezés (3 módosító billentyűvel) +Name[ia]=KDE predefinite pro 3 claves modificator +Name[id]=Standar KDE untuk 3 Tombol Pengubah +Name[is]=Sjálfgefið KDE fyrir 3 breytilykla +Name[it]=Predefinito di KDE per 3 tasti modificatori +Name[ja]=KDE 標準 (修飾キー 3 個) +Name[kk]=3 түрлендіру пернелі KDE-нің әдетті баптауы +Name[km]=លំនាំដើម​របស់ KDE សម្រាប់​សោ​កែប្រែ ៣ +Name[kn]=೩ ಪರಿವರ್ತಕ ಕೀಲಿಗಳಿಗೆ ಕೆಡಿಇ ಪೂರ್ವನಿಯೋಜನೆ +Name[ko]=세 개의 수정자 키를 사용하는 KDE 기본값 +Name[ku]=KDE Standard ji bo 3 Bişkojkên Guherker +Name[lt]=KDE numatytoji 3 lygio klavišams – modifikatoriams +Name[lv]=KDE noklusējums ar 3 modifikācijas taustiņiem +Name[mai]=3 माडीफायर कुंजीसभ क' लेल केडीई पूर्वनिर्धारित +Name[mk]=KDE-стандардно за 3 копчиња-модификатори +Name[ml]=3 മോഡിഫയര്‍ കീകള്‍ക്കു് കെഡിഇയുടെ സഹജമായതു് +Name[mr]=3 संपादन की करीता KDE मुलभूत +Name[ms]=Piawai KDE untuk 3 Kekunci Pengubah Suai +Name[nb]=KDE-standard for tre valgtaster +Name[nds]=KDE-Standard för 3 Sünnertasten +Name[ne]=३ परिमार्जक कुञ्जीका लागि पूर्वनिर्धारित केडीई +Name[nl]=KDE-standaard voor 3 modificatietoetsen +Name[nn]=KDE-standard med tre valtastar +Name[or]=3ଟି ପରିବର୍ତ୍ତକ କି ପାଇଁ KDE ପୂର୍ବନିର୍ଦ୍ଧାରିତ +Name[pa]=3 ਮੋਡੀਫਾਇਰ ਸਵਿੱਚਾਂ ਲਈ KDE ਡਿਫਾਲਟ +Name[pl]=Ustawienia domyślne KDE dla 3 klawiszy modyfikatorów +Name[pt]=Predefinição do KDE para 3 Teclas Modificadoras +Name[pt_BR]=Padrão do KDE para 3 teclas modificadoras +Name[ro]=Implicit KDE pentru 3 modificatori +Name[ru]=KDE с тремя клавишами-модификаторами +Name[se]=KDE-standárda mas leat golbma válljeboalu +Name[si]=3 වෙනස් යතුරු සඳහා KDE පෙරනිමියන් +Name[sk]=KDE štandardné (3 modifikačné klávesy) +Name[sl]=Privzeto za 3 spremenilne tipke +Name[sr]=КДЕ‑ово подразумевано за три модификаторска тастера +Name[sr@ijekavian]=КДЕ‑ово подразумијевано за три модификаторска тастера +Name[sr@ijekavianlatin]=KDE‑ovo podrazumijevano za tri modifikatorska tastera +Name[sr@latin]=KDE‑ovo podrazumevano za tri modifikatorska tastera +Name[sv]=KDE-förinställt med 3 väljartangenter +Name[ta]=KDE Default for 3 Modifier Keys +Name[te]=3 సవరింపు కీలకొరకు KDE అప్రమేయం +Name[tg]=KDE с тремя клавишами-модификаторами +Name[th]=ค่าปริยายของ KDE สำหรับปุ่มเปลี่ยนหน้าที่ 3 ปุ่ม +Name[tr]=3 Değiştirici Anahtar için KDE Varsayılan +Name[uk]=Типова для KDE з 3 клавішами-модифікаторами +Name[vi]=Mặc định KDE cho 3 Phím Bổ trợ +Name[wa]=Prémetou KDE (3 tapes modifieueses) +Name[xh]=KDE Engagqibekanga ye 3 Izitsixo Mofifier +Name[x-test]=xxKDE Default for 3 Modifier Keysxx +Name[zh_CN]=三个修饰键的 KDE 默认值 +Name[zh_TW]=KDE 預設 3 個變更鍵 +Uses Win Modifier=false + +[klipper][Global Shortcuts] +clipboard_action=Ctrl+Alt+X +repeat_action=Ctrl+Alt+R +show_klipper_popup=Ctrl+Alt+V + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=Ctrl+Alt+L +Log Out Without Confirmation=none +Log Out=Ctrl+Alt+Del +Reboot Without Confirmation=none +Run Command=Alt+F2 +Show System Activity=Ctrl+Esc + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Ctrl+Print +Kill Window=Ctrl+Alt+Esc +Mouse Emulation=Alt+F12 +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Ctrl+F10 +Switch to Desktop 11=Ctrl+F11 +Switch to Desktop 12=Ctrl+F12 +Switch to Desktop 13=Ctrl+Shift+F1 +Switch to Desktop 14=Ctrl+Shift+F2 +Switch to Desktop 15=Ctrl+Shift+F3 +Switch to Desktop 16=Ctrl+Shift+F4 +Switch to Desktop 1=Ctrl+F1 +Switch to Desktop 2=Ctrl+F2 +Switch to Desktop 3=Ctrl+F3 +Switch to Desktop 4=Ctrl+F4 +Switch to Desktop 5=Ctrl+F5 +Switch to Desktop 6=Ctrl+F6 +Switch to Desktop 7=Ctrl+F7 +Switch to Desktop 8=Ctrl+F8 +Switch to Desktop 9=Ctrl+F9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=Ctrl+Shift+Tab +Walk Through Desktop List=Ctrl+Tab +Walk Through Desktops (Reverse)=none +Walk Through Desktops=none +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+Tab +Window Close=Alt+F4 +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=none +Window Minimize=none +Window Move=none +Window Operations Menu=Alt+F3 +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Alt+Print +Window Shade=none +Window to Desktop 10=none +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=none +Window to Desktop 2=none +Window to Desktop 3=none +Window to Desktop 4=none +Window to Desktop 5=none +Window to Desktop 6=none +Window to Desktop 7=none +Window to Desktop 8=none +Window to Desktop 9=none +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/schemes/kde4.kksrc b/kcms/keys/schemes/kde4.kksrc new file mode 100644 index 0000000000..2ea1432b92 --- /dev/null +++ b/kcms/keys/schemes/kde4.kksrc @@ -0,0 +1,166 @@ +[Settings] +Name=KDE Default for 4 Modifier Keys +Name[af]=Standaard KDE vir 4 verandering sleutels +Name[ar]=مبدئي كدي لـ4 مفاتيح مغيرة +Name[ast]=Predetermináu de KDE de 4 modificadores de tecláu +Name[be]=Стандартны для KDE з чатырма клавішамі мадыфікацыі +Name[be@latin]=Zmoŭčanaja schiema „KDE” dla 4 klavišnych madyfikataraŭ +Name[bg]=KDE (с 4 модификационни клавиша) +Name[ca]=Omissió del KDE per a 4 tecles modificadores +Name[ca@valencia]=Omissió del KDE per a 4 tecles modificadores +Name[cs]=Výchozí pro KDE (4 modifikátory) +Name[csb]=Domëslny nastôw KDE dlô 4 klawiszów zjinaczi +Name[cy]=Rhagosodion KDE i 4 Bysell Addasu +Name[da]=KDE-standard med 4 modifikatortaster +Name[de]=KDE-Standard für 4 Sondertasten +Name[el]=Προκαθορισμένο του KDE για 4 τροποποιητές +Name[en_GB]=KDE Default for 4 Modifier Keys +Name[eo]=KDE-defaŭlto por 4 modifoklavoj +Name[es]=Predeterminado de KDE de 4 modificadores de teclado +Name[et]=KDE vaikeskeem 4 muuteklahviga +Name[eu]=KDE lehenetsia 4 tekla modifikatzailekin +Name[fa]=پیش‌فرض KDE برای ۴ کلید تغییردهنده +Name[fi]=KDE oletus 4:lle muokkausnäppäimelle +Name[fr]=KDE par défaut pour 4 touches de modifications +Name[fy]=KDE-standert foar 4 modifikaasjetoetsen +Name[ga]=Réamhshocrú KDE le haghaidh ceithre eochair mhionathraithe +Name[gl]=Predeterminado de KDE para 4 teclas modificadoras +Name[gu]=૪ બદલેલ કળો માટે KDE મૂળભૂત +Name[he]=ברירת המחדל של KDE עבור 4 כפתורים משני מצב +Name[hi]=4 मॉडीफ़ायर कुंजियों के लिए केडीई डिफ़ॉल्ट +Name[hne]=4 माडीफायर कुंजी मन बर केडीई डिफाल्ट +Name[hr]=KDE zadano za 4 modifikatorske tipke +Name[hsb]=KDE-standard za štyri modifikowace tasty +Name[hu]=KDE-s alapértelmezés (4 módosító billentyűvel) +Name[ia]=KDE predefinite pro 4 claves modificator +Name[id]=Standar KDE untuk 4 Tombol Pengubah +Name[is]=Sjálfgefið KDE fyrir 4 breytilykla +Name[it]=Predefinito di KDE per 4 tasti modificatori +Name[ja]=KDE 標準 (修飾キー 4 個) +Name[kk]=4 түрлендіру пернелі KDE-нің әдетті баптауы +Name[km]=លំនាំដើម​របស់ KDE សម្រាប់​សោ​កែប្រែ ៤ +Name[kn]=೪ ಪರಿವರ್ತಕ ಕೀಲಿಗಳಿಗೆ ಕೆಡಿಇ ಪೂರ್ವನಿಯೋಜನೆ +Name[ko]=네 개의 수정자 키를 사용하는 KDE 기본값 +Name[ku]=KDE Standard ji bo 4 Bişkojkên Guherker +Name[lt]=KDE numatytoji 4 lygio klavišams – modifikatoriams +Name[lv]=KDE noklusējums ar 4 modifikācijas taustiņiem +Name[mai]=4 माडीफायर कुंजियों क' लेल केडीई पूर्वनिर्धारित +Name[mk]=KDE-стандардно за 4 копчиња-модификатори +Name[ml]=4 മോഡിഫയര്‍ കീകള്‍ക്കു് കെഡിഇയുടെ സഹജമായതു് +Name[mr]=4 संपादन की करीता KDE मुलभूत +Name[ms]=Piawai KDE untuk 4 Kekunci Pengubah Suai +Name[nb]=KDE-standard for fire valgtaster +Name[nds]=KDE-Standard för 4 Sünnertasten +Name[ne]=४ परिमार्जक कुञ्जीका लागि पूर्वनिर्धारित केडीई +Name[nl]=KDE-standaard voor 4 modificatietoetsen +Name[nn]=KDE-standard med fire valtastar +Name[or]=4ଟି ପରିବର୍ତ୍ତକ କି ପାଇଁ KDE ପୂର୍ବନିର୍ଦ୍ଧାରିତ +Name[pa]=4 ਮੋਡੀਫਾਇਰ ਸਵਿੱਚਾਂ ਲਈ KDE ਡਿਫਾਲਟ +Name[pl]=Ustawienia domyślne KDE dla 4 klawiszy modyfikatorów +Name[pt]=Predefinição do KDE para 4 Teclas Modificadoras +Name[pt_BR]=Padrão do KDE para 4 teclas modificadoras +Name[ro]=Implicit KDE pentru 4 modificatori +Name[ru]=KDE с четырьмя клавишами-модификаторами +Name[se]=KDE-standárda mas leat njeallje válljenboalu +Name[si]=4 වෙනස් යතුරු සඳහා KDE පෙරනිමියන් +Name[sk]=KDE štandardné (4 modifikačné klávesy) +Name[sl]=Privzeto za 4 spremenilne tipke +Name[sr]=КДЕ‑ово подразумевано за четири модификаторска тастера +Name[sr@ijekavian]=КДЕ‑ово подразумијевано за четири модификаторска тастера +Name[sr@ijekavianlatin]=KDE‑ovo podrazumijevano za četiri modifikatorska tastera +Name[sr@latin]=KDE‑ovo podrazumevano za četiri modifikatorska tastera +Name[sv]=KDE-förinställt med 4 väljartangenter +Name[ta]=KDE Default for 4 Modifier Keys +Name[te]=4 సవరింపు కీలకొరకు KDE అప్రమేయం +Name[tg]=KDE с четырьмя клавишами-модификаторами +Name[th]=ค่าปริยายของ KDE สำหรับปุ่มเปลี่ยนหน้าที่ 4 ปุ่ม +Name[tr]=4 Değiştirici Anahtar için KDE Varsayılan +Name[uk]=Типова для KDE з 4 клавішами-модифікаторами +Name[vi]=Mặc định KDE cho 4 Phím Bổ trợ +Name[wa]=Prémetou KDE (4 tapes modifieueses) +Name[xh]=KDE Engagqibekanga ye 4 Izitsixo Mofifier +Name[x-test]=xxKDE Default for 4 Modifier Keysxx +Name[zh_CN]=四个修饰键的 KDE 默认值 +Name[zh_TW]=KDE 預設 4 個變更鍵 +Uses Win Modifier=true + +[klipper][Global Shortcuts] +clipboard_action=Meta+Ctrl+X +repeat_action=Meta+Ctrl+R +show_klipper_popup=Meta+Ctrl+V + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=Meta+ScrollLock +Log Out Without Confirmation=none +Log Out=Meta+Esc +Reboot Without Confirmation=none +Run Command=Meta+Return; Alt+F2 +Show System Activity=Meta+Ctrl+Pause + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Meta+Print +Kill Window=Meta+Ctrl+Del +Mouse Emulation=none +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Meta+F10 +Switch to Desktop 11=none +Switch to Desktop 12=none +Switch to Desktop 13=none +Switch to Desktop 14=none +Switch to Desktop 15=none +Switch to Desktop 16=none +Switch to Desktop 1=Meta+F1 +Switch to Desktop 2=Meta+F2 +Switch to Desktop 3=Meta+F3 +Switch to Desktop 4=Meta+F4 +Switch to Desktop 5=Meta+F5 +Switch to Desktop 6=Meta+F6 +Switch to Desktop 7=Meta+F7 +Switch to Desktop 8=Meta+F8 +Switch to Desktop 9=Meta+F9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=none +Walk Through Desktop List=none +Walk Through Desktops (Reverse)=Meta+Shift+Tab +Walk Through Desktops=Meta+Tab +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+Tab +Window Close=Alt+Esc; Alt+F4 +Window Lower=none +Window Maximize Horizontal=Meta+= +Window Maximize Vertical=Meta+| +Window Maximize=Meta++ +Window Minimize=Meta+- +Window Move=none +Window Operations Menu=Alt+Menu +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Alt+Print +Window Shade=Meta+_ +Window to Desktop 10=Meta+Alt+F10 +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=Meta+Alt+F1 +Window to Desktop 2=Meta+Alt+F2 +Window to Desktop 3=Meta+Alt+F3 +Window to Desktop 4=Meta+Alt+F4 +Window to Desktop 5=Meta+Alt+F5 +Window to Desktop 6=Meta+Alt+F6 +Window to Desktop 7=Meta+Alt+F7 +Window to Desktop 8=Meta+Alt+F8 +Window to Desktop 9=Meta+Alt+F9 +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Meta+Ctrl+K diff --git a/kcms/keys/schemes/mac4.kksrc b/kcms/keys/schemes/mac4.kksrc new file mode 100644 index 0000000000..0ecde0079e --- /dev/null +++ b/kcms/keys/schemes/mac4.kksrc @@ -0,0 +1,171 @@ +[Settings] +Name=Mac Scheme +Name[af]=Mac Tema +Name[ar]=مخطط الماك +Name[ast]=Tema Mac +Name[be]=Схема Mac +Name[be@latin]=Schiema „Mac” +Name[bg]=Mac +Name[bn]=ম্যাক্ স্কীম +Name[bn_IN]=Mac স্কিম +Name[br]=Steuñv Mac +Name[ca]=Esquema Mac +Name[ca@valencia]=Esquema Mac +Name[cs]=Mac schéma +Name[csb]=Mòtiw Mac-a +Name[cy]=Cynllun Mac +Name[da]=Mac-system +Name[de]=Mac-Design +Name[el]=Σχήμα Mac +Name[en_GB]=Mac Scheme +Name[eo]=Makintoŝa aranĝo +Name[es]=Tema Mac +Name[et]=Maci skeem +Name[eu]=Mac eskema +Name[fa]=طرحواره مکینتاش +Name[fi]=Mac-skeema +Name[fr]=Agencement Mac +Name[fy]=Mac-skema +Name[ga]=Scéim Mac +Name[gl]=Esquema de Mac +Name[gu]=મેક પધ્ધતિ +Name[he]=ערכת Mac +Name[hi]=मॅक प्रसंग +Name[hne]=मॅक प्रसंग +Name[hr]=Mac shema +Name[hsb]=Mac-šema +Name[hu]=Mac +Name[ia]=Schema Mac +Name[id]=Skema Mac +Name[is]=Mac þema +Name[it]=Schema Mac +Name[ja]=Macintosh スキーマ +Name[ka]=Mac სქემა +Name[kk]=Mac сұлбасы +Name[km]=គ្រោងការណ៍ Mac +Name[kn]=ಮ್ಯಾಕ್ ಪದ್ಧತಿ (ಸ್ಕೀಮ್) +Name[ko]=Mac 배열 +Name[ku]=Dirbê Mac +Name[lt]=Mac schema +Name[lv]=Mac shēma +Name[mai]=मैक प्रसंग +Name[mk]=Mac-шема +Name[ml]=മാക് പദ്ധതി +Name[mr]=मॅक सुत्रयोजना +Name[nb]=Mac-oppsett +Name[nds]=Mac-Vörbild +Name[ne]=म्याक योजना +Name[nl]=Mac-schema +Name[nn]=Mac-oppsett +Name[or]=Mac ଯୋଜନା +Name[pa]=Mac ਸਕੀਮ +Name[pl]=Motyw Mac-a +Name[pt]=Esquema do Mac +Name[pt_BR]=Esquema do Mac +Name[ro]=Schemă Mac +Name[ru]=Mac +Name[se]=Mac-coakkáldat +Name[si]=Mac ක්‍රමය +Name[sk]=Mac schéma +Name[sl]=Shema Mac +Name[sr]=Мекинтошева шема +Name[sr@ijekavian]=Мекинтошева шема +Name[sr@ijekavianlatin]=Macintosheva šema +Name[sr@latin]=Macintosheva šema +Name[sv]=Mac schema +Name[ta]=Mac முறைமை +Name[te]=Mac స్కీమ్ +Name[tg]=Нақшаи Mac +Name[th]=ใช้แบบของ MAC +Name[tr]=Mac Şeması +Name[uk]=Схема Mac +Name[uz]=Mac mavzusi +Name[uz@cyrillic]=Mac мавзуси +Name[vi]=Phối hợp của Mac +Name[wa]=Djeu d' rascourtis Mac +Name[xh]=Udweliso lwe Mac +Name[x-test]=xxMac Schemexx +Name[zh_CN]=Mac 方案 +Name[zh_TW]=Mac 機制 +Uses Win Modifier=true + +[klipper][Global Shortcuts] +clipboard_action=Ctrl+Alt+X +repeat_action=Ctrl+Alt+R +show_klipper_popup=Ctrl+Alt+V + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=Ctrl+Alt+L +Log Out Without Confirmation=none +Log Out=Ctrl+Alt+Del +Reboot Without Confirmation=none +Run Command=Alt+F2 +Show System Activity=Ctrl+Esc + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Ctrl+Shift+3 +Kill Window=Ctrl+Alt+Esc +Mouse Emulation=none +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Ctrl+F10 +Switch to Desktop 11=Ctrl+F11 +Switch to Desktop 12=Ctrl+F12 +Switch to Desktop 13=Ctrl+Shift+F1 +Switch to Desktop 14=Ctrl+Shift+F2 +Switch to Desktop 15=Ctrl+Shift+F3 +Switch to Desktop 16=Ctrl+Shift+F4 +Switch to Desktop 1=Ctrl+F1 +Switch to Desktop 2=Ctrl+F2 +Switch to Desktop 3=Ctrl+F3 +Switch to Desktop 4=Ctrl+F4 +Switch to Desktop 5=Ctrl+F5 +Switch to Desktop 6=Ctrl+F6 +Switch to Desktop 7=Ctrl+F7 +Switch to Desktop 8=Ctrl+F8 +Switch to Desktop 9=Ctrl+F9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=Ctrl+E +Walk Through Desktop List (Reverse)=none +Walk Through Desktop List=none +Walk Through Desktops (Reverse)=Alt+Shift+Tab +Walk Through Desktops=Alt+Tab +Walk Through Windows (Reverse)=Ctrl+Shift+Tab +Walk Through Windows=Ctrl+Tab +Window Close=Alt+F4 +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=none +Window Minimize=none +Window Move=none +Window Operations Menu=Alt+F3 +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Ctrl+Shift+4 +Window Shade=none +Window to Desktop 10=none +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=none +Window to Desktop 2=none +Window to Desktop 3=none +Window to Desktop 4=none +Window to Desktop 5=none +Window to Desktop 6=none +Window to Desktop 7=none +Window to Desktop 8=none +Window to Desktop 9=none +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/schemes/old_kde3.kksrc b/kcms/keys/schemes/old_kde3.kksrc new file mode 100644 index 0000000000..561ff99f70 --- /dev/null +++ b/kcms/keys/schemes/old_kde3.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Alt+F1 +Reboot Computer=none +Show Window List=Alt+F5 +Toggle Showing Desktop=Ctrl+Alt+D + +[Shortcuts] +AddBookmark=Ctrl+B +Back=Alt+Left +Close=Ctrl+W +Copy=Ctrl+C; Ctrl+Insert +Cut=Ctrl+X; Shift+Del +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V; Shift+Insert +PopupMenuContext=Menu +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit=Ctrl+Q +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_kde4.kksrc b/kcms/keys/schemes/old_kde4.kksrc new file mode 100644 index 0000000000..64dfd8da32 --- /dev/null +++ b/kcms/keys/schemes/old_kde4.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Meta+Menu +Reboot Computer=none +Show Window List=Meta+0 +Toggle Showing Desktop=Meta+Ctrl+D + +[Shortcuts] +AddBookmark=Ctrl+B +Back=Alt+Left +Close=Ctrl+Esc; Ctrl+W +Copy=Ctrl+C; Ctrl+Insert +Cut=Ctrl+X; Shift+Del +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V; Shift+Insert +PopupMenuContext=Menu +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit=Ctrl+Q +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_mac4.kksrc b/kcms/keys/schemes/old_mac4.kksrc new file mode 100644 index 0000000000..f0da90deab --- /dev/null +++ b/kcms/keys/schemes/old_mac4.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Alt+F1 +Reboot Computer=none +Show Window List=Alt+F5 +Toggle Showing Desktop=Ctrl+Alt+D + +[Shortcuts] +AddBookmark=Ctrl+B +Back=Alt+Left +Close=Ctrl+W +Copy=Ctrl+C +Cut=Ctrl+X +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=Ctrl+G +FindPrev=Ctrl+Shift+G +Forward=Alt+Right +GotoLine=none +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V +PopupMenuContext=Menu +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit=Ctrl+Q +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_unix3.kksrc b/kcms/keys/schemes/old_unix3.kksrc new file mode 100644 index 0000000000..6e724007a7 --- /dev/null +++ b/kcms/keys/schemes/old_unix3.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Alt+F1 +Reboot Computer=none +Show Window List=none +Toggle Showing Desktop=Ctrl+Alt+D + +[Shortcuts] +AddBookmark=Ctrl+B +Back=Alt+Left +Close=Ctrl+W +Copy=Ctrl+C +Cut=Ctrl+X +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+S +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V +PopupMenuContext=Menu +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit=Ctrl+Q +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll= +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_win3.kksrc b/kcms/keys/schemes/old_win3.kksrc new file mode 100644 index 0000000000..410d68779d --- /dev/null +++ b/kcms/keys/schemes/old_win3.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Ctrl+Esc +Reboot Computer=none +Show Window List=none +Toggle Showing Desktop=Ctrl+Alt+D + +[Shortcuts] +AddBookmark=Ctrl+D +Back=Alt+Left +Close=Ctrl+F4; Ctrl+W +Copy=Ctrl+C; Ctrl+Insert +Cut=Ctrl+X; Shift+Del +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V; Shift+Insert +PopupMenuContext=F10 +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit= +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_win4.kksrc b/kcms/keys/schemes/old_win4.kksrc new file mode 100644 index 0000000000..3859eb68d3 --- /dev/null +++ b/kcms/keys/schemes/old_win4.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Ctrl+Esc +Reboot Computer=none +Show Window List=none +Toggle Showing Desktop=Meta+D; Win+M + +[Shortcuts] +AddBookmark=Ctrl+D +Back=Alt+Left +Close=Ctrl+F4; Ctrl+W +Copy=Ctrl+C; Ctrl+Insert +Cut=Ctrl+X; Shift+Del +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V; Shift+Insert +PopupMenuContext=F10 +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit= +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/old_wm3.kksrc b/kcms/keys/schemes/old_wm3.kksrc new file mode 100644 index 0000000000..561ff99f70 --- /dev/null +++ b/kcms/keys/schemes/old_wm3.kksrc @@ -0,0 +1,51 @@ + +[Global Shortcuts] +Halt Computer=none +Popup Launch Menu=Alt+F1 +Reboot Computer=none +Show Window List=Alt+F5 +Toggle Showing Desktop=Ctrl+Alt+D + +[Shortcuts] +AddBookmark=Ctrl+B +Back=Alt+Left +Close=Ctrl+W +Copy=Ctrl+C; Ctrl+Insert +Cut=Ctrl+X; Shift+Del +DeleteWordBack=Ctrl+Backspace +DeleteWordForward=Ctrl+Del +Deselect=Ctrl+Shift+A +End=Ctrl+End +Find=Ctrl+F +FindNext=F3 +FindPrev=Shift+F3 +Forward=Alt+Right +GotoLine=Ctrl+G +Help=F1 +Home=Ctrl+Home +Insert=Ctrl+Insert +New=Ctrl+N +Next=Next +NextCompletion=Ctrl+Down +Open=Ctrl+O +Paste=Ctrl+V; Shift+Insert +PopupMenuContext=Menu +PrevCompletion=Ctrl+Up +Print=Ctrl+P +Prior=Prior +Quit=Ctrl+Q +Redo=Ctrl+Shift+Z +Reload=F5 +Replace=Ctrl+R +RotateDown=Down +RotateUp=Up +Save=Ctrl+S +SelectAll=Ctrl+A +ShowMenubar=Ctrl+M +SubstringCompletion=Ctrl+T +TextCompletion=Ctrl+E +Undo=Ctrl+Z +Up=Alt+Up +WhatsThis=Shift+F1 +ZoomIn=Ctrl++ +ZoomOut=Ctrl+- diff --git a/kcms/keys/schemes/split_kksrc b/kcms/keys/schemes/split_kksrc new file mode 100755 index 0000000000..9fab61e42f --- /dev/null +++ b/kcms/keys/schemes/split_kksrc @@ -0,0 +1,111 @@ +#!/usr/bin/perl + +use strict; +use warnings; + + +foreach my $file( @ARGV ) + { + print STDERR "Working on $file\n"; + + open(my $FILEHD, $file) or do + { + print STDERR "Failed to open $file : $!"; + next; + }; + + open(my $NEWHD, ">new_$file") or do + { + print STDERR "Failed to open new_$file : $!"; + next; + }; + + open(my $OLDHD, ">old_$file") or do + { + print STDERR "Failed to open old_$file : $!"; + next; + }; + + my $process = 0; + my @kwin; + my @krunner; + my @klipper; + my @kxkb; + for( <$FILEHD> ) + { + /\[Settings\]/ and do { print $NEWHD $_; next; }; + /Name(\[[a-zA-Z_@]*\])?/ and do { print $NEWHD $_; next; }; + /Name\[x-test\]/ and do { print $NEWHD $_; next; }; + /Uses Win Modifier/ and do { print $NEWHD $_; next; }; + + /\[Global Shortcuts\]/ and do { print $OLDHD $_; $process = 1; next; }; + /\[Shortcuts\]/ and do { print $OLDHD $_; $process = 0; next; }; + + s/Alt\+Ctrl/Ctrl+Alt/; + s/Delete/Del/; + s/Escape/Esc/; + s/Win/Meta/; + + if ($process) + { + /Desktop Screenshot=/ and do { s/Desktop Screenshot=/Desktop Screenshot to Clipboard=/; push @kwin, $_; next; }; + /Kill Window=/ and do { push @kwin, $_; next; }; + /Mouse Emulation/ and do { push @kwin, $_; next; }; + /Switch to Desktop [0-9]+/ and do { push @kwin, $_; next; }; + /Switch to (Next|Previous) Desktop=/ and do { push @kwin, $_; next; }; + /Switch One Desktop / and do { push @kwin, $_; next; }; + /Toggle Window Raise\/Lower=/ and do { push @kwin, $_; next; }; + /Walk Through Desktop/ and do { push @kwin, $_; next; }; + /Walk Through Windows/ and do { push @kwin, $_; next; }; + /Window to (Next |Previous )?Desktop/ and do { push @kwin, $_; next; }; + /Window (Close|Minimize|Lower|Move|Raise|Resize|Shade)/ and do { push @kwin, $_; next; }; + /Window Maximize/ and do { push @kwin, $_; next; }; + /Window Operations Menu=/ and do { s/Window Screenshot=/Window Screenshot to Clipboard=/; push @kwin, $_; next; }; + /Window Screenshot=/ and do { s/Window Screenshot=/Window Screenshot to Clipboard=/; push @kwin, $_; next; }; + + /Switch to Next Keyboard Layout/ and do { push @kxkb, $_; next; }; + + /clipboard_action/ and do { push @klipper, $_; next; }; + /repeat_action/ and do { push @klipper, $_; next; }; + /show_klipper_popup/ and do { push @klipper, $_; next; }; + + /Halt without Confirmation/ and do { s/Halt without Confirmation/Halt Without Confirmation/; push @krunner, $_; next; }; + /Lock Session/ and do { push @krunner, $_; next; }; + /Log Out/ and do { push @krunner, $_; next; }; + /Log Out Without Confirmation/ and do { push @krunner, $_; next; }; + /Lock Session/ and do { push @krunner, $_; next; }; + /Reboot without Confirmation/ and do { s/Reboot without Confirmation/Reboot Without Confirmation/; push @krunner, $_; next; }; + /Run Command/ and do { push @krunner, $_; next; }; + /Show Taskmanager/ and do { s/Show Taskmanager/Show System Activity/; push @krunner, $_; next; }; + } + + print $OLDHD $_; + } + + print $NEWHD "\n[klipper][Global Shortcuts]\n"; + print $NEWHD sort @klipper; + print $NEWHD "\n[krunner][Global Shortcuts]\n"; + print $NEWHD sort @krunner; + print $NEWHD "\n[kwin][Global Shortcuts]\n"; + print $NEWHD sort @kwin; + print $NEWHD "\n[kxkb][Global Shortcuts]\n"; + print $NEWHD sort @kxkb; + + close($FILEHD) or do + { + print STDERR "Failed to close $file : $!"; + next; + }; + + close($NEWHD) or do + { + print STDERR "Failed to close new_$file : $!"; + next; + }; + + close($OLDHD) or do + { + print STDERR "Failed to close old_$file : $!"; + next; + }; + } diff --git a/kcms/keys/schemes/unix3.kksrc b/kcms/keys/schemes/unix3.kksrc new file mode 100644 index 0000000000..1845cde7c5 --- /dev/null +++ b/kcms/keys/schemes/unix3.kksrc @@ -0,0 +1,171 @@ +[Settings] +Name=UNIX Scheme +Name[af]=UNIX Tema +Name[ar]=مخطط يونِكس +Name[ast]=Esquema UNIX +Name[be]=Схема UNIX +Name[be@latin]=Schiema „UNIX” +Name[bg]=UNIX +Name[bn]=ইউনিক্স স্কীম +Name[bn_IN]=UNIX স্কিম +Name[br]=Steuñv UNIX +Name[ca]=Esquema UNIX +Name[ca@valencia]=Esquema UNIX +Name[cs]=Unixové schéma +Name[csb]=Mòtiw Uniksa +Name[cy]=Cynllun UNIX +Name[da]=UNIX-system +Name[de]=UNIX-Design +Name[el]=Σχήμα UNIX +Name[en_GB]=UNIX Scheme +Name[eo]=Uniksa aranĝo +Name[es]=Esquema UNIX +Name[et]=UNIX-i skeem +Name[eu]=UNIX eskema +Name[fa]=طرحواره یونیکس +Name[fi]=UNIX-skeema +Name[fr]=Agencement UNIX +Name[fy]=UNIX-skema +Name[ga]=Scéim UNIX +Name[gl]=Esquema de UNIX +Name[gu]=UNIX પધ્ધતિ +Name[he]=ערכת UNIX +Name[hi]=यूनिक्स प्रसंग +Name[hne]=यूनिक्स प्रसंग +Name[hr]=UNIX shema +Name[hsb]=UNIX-šema +Name[hu]=UNIX +Name[ia]=Schema UNIX +Name[id]=Skema UNIX +Name[is]=UNIX þema +Name[it]=Schema UNIX +Name[ja]=UNIX スキーマ +Name[ka]=UNIX სქემა +Name[kk]=UNIX сұлбасы +Name[km]=គ្រោងការណ៍​យូនីក +Name[kn]=ಯುನೀಕ್ಸ್ ಪದ್ಧತಿ (ಸ್ಕೀಮ್) +Name[ko]=UNIX 배열 +Name[ku]=Şemaya UNIX'ê +Name[lt]=UNIX schema +Name[lv]=UNIX shēma +Name[mai]=यूनिक्स प्रसंग +Name[mk]=UNIX-шема +Name[ml]=യൂണിക്സ് പദ്ധതി +Name[mr]=UNIX सुत्रयोजना +Name[nb]=UNIX-oppsett +Name[nds]=UNIX-Vörbild +Name[ne]=यूनिक्स योजना +Name[nl]=UNIX-schema +Name[nn]=UNIX-oppsett +Name[or]=UNIX ଯୋଜନା +Name[pa]=UNIX ਸਕੀਮ +Name[pl]=Motyw Uniksa +Name[pt]=Esquema do UNIX +Name[pt_BR]=Esquema do UNIX +Name[ro]=Schemă UNIX +Name[ru]=UNIX +Name[se]=UNIX-čoahkkáldat +Name[si]=UNIX ක්‍රමය +Name[sk]=UNIX schéma +Name[sl]=Shema UNIX +Name[sr]=Уникс шема +Name[sr@ijekavian]=Уникс шема +Name[sr@ijekavianlatin]=Unix šema +Name[sr@latin]=Unix šema +Name[sv]=Unix schema +Name[ta]=UNIX முறைமை +Name[te]=UNIX స్కీమ్ +Name[tg]=Нақшаи UNIX +Name[th]=ใช้แบบของ UNIX +Name[tr]=UNIX Şeması +Name[uk]=Схема UNIX +Name[uz]=UNIX mavzusi +Name[uz@cyrillic]=UNIX мавзуси +Name[vi]=Phối hợp của Unix +Name[wa]=Djeu d' rascourtis Unix +Name[xh]=Udweliso lwe UNIX +Name[x-test]=xxUNIX Schemexx +Name[zh_CN]=Unix 方案 +Name[zh_TW]=UNIX 機制 +Uses Win Modifier=false + +[klipper][Global Shortcuts] +clipboard_action=none +repeat_action=Ctrl+Alt+R +show_klipper_popup=none + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=Ctrl+Alt+L +Log Out Without Confirmation=none +Log Out=Ctrl+Alt+Del +Reboot Without Confirmation=none +Run Command=Alt+F2 +Show System Activity=Ctrl+Esc + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=none +Kill Window=Alt+F9 +Mouse Emulation=Alt+F12 +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Ctrl+F10 +Switch to Desktop 11=Ctrl+F11 +Switch to Desktop 12=Ctrl+F12 +Switch to Desktop 13=Ctrl+Shift+F1 +Switch to Desktop 14=Ctrl+Shift+F2 +Switch to Desktop 15=Ctrl+Shift+F3 +Switch to Desktop 16=Ctrl+Shift+F4 +Switch to Desktop 1=Ctrl+F1 +Switch to Desktop 2=Ctrl+F2 +Switch to Desktop 3=Ctrl+F3 +Switch to Desktop 4=Ctrl+F4 +Switch to Desktop 5=Ctrl+F5 +Switch to Desktop 6=Ctrl+F6 +Switch to Desktop 7=Ctrl+F7 +Switch to Desktop 8=Ctrl+F8 +Switch to Desktop 9=Ctrl+F9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=Ctrl+Shift+Tab +Walk Through Desktop List=none +Walk Through Desktops (Reverse)=none +Walk Through Desktops=Ctrl+Tab +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+F11 +Window Close=none +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=Alt+Shift+F3 +Window Minimize=Alt+F4 +Window Move=Alt+F5 +Window Operations Menu=none +Window Raise=none +Window Resize=Alt+F6 +Window Screenshot to Clipboard=none +Window Shade=none +Window to Desktop 10=none +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=none +Window to Desktop 2=none +Window to Desktop 3=none +Window to Desktop 4=none +Window to Desktop 5=none +Window to Desktop 6=none +Window to Desktop 7=none +Window to Desktop 8=none +Window to Desktop 9=none +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/schemes/win3.kksrc b/kcms/keys/schemes/win3.kksrc new file mode 100644 index 0000000000..e1929fb735 --- /dev/null +++ b/kcms/keys/schemes/win3.kksrc @@ -0,0 +1,170 @@ +[Settings] +Name=Windows Scheme (Without Win Key) +Name[af]=Venster Skema (Sonder Win Sleutel) +Name[ar]=مخطط ويندوز (بدون مفتاح Win) +Name[ast]=Esquema Windows (Ensin tecla Win) +Name[be]=Схема Windows (без клавішы "Win") +Name[be@latin]=Schiema „Windows” (biaz klavišy „Win”) +Name[bg]=Windows (без клавиша Win) +Name[bn]=উইণ্ডোস্ স্কীম (উইন্ কী ব্যতীত) +Name[br]=Steuñv Windows (hep stokell Win) +Name[ca]=Esquema Windows (sense la tecla Win) +Name[ca@valencia]=Esquema Windows (sense la tecla Win) +Name[cs]=Schéma Windows (bez klávesy Win) +Name[csb]=Mòtiw Windows (bez klawiszë Win) +Name[cy]=Cynllun Windows (Heb Allwedd Win) +Name[da]=Windows-system (uden Win-tast) +Name[de]=Windows-Design (ohne Windows-Taste) +Name[el]=Σχήμα Windows (Χωρίς πλήκτρο Win) +Name[en_GB]=Windows Scheme (Without Win Key) +Name[eo]=Vindoza aranĝo (sen Vindozoklavo) +Name[es]=Esquema Windows (Sin tecla Win) +Name[et]=Windowsi skeem (ilma Win-klahvita) +Name[eu]=Windows eskema (Win teklarik gabe) +Name[fa]=طرحواره ویندوز( بدون کلید پنجره) +Name[fi]=Windows-skeema (ilman Win-näppäintä) +Name[fr]=Agencement Windows (sans la touche « Win ») +Name[fy]=Windows-skema (sûnder Win-toets) +Name[ga]=Scéim Windows (Gan Eochair Win) +Name[gl]=Esquema de Windows (sen tecla de Windows) +Name[gu]=વિન્ડોઝ પધ્ધતિ (વિન કળ વગર) +Name[he]=ערכת חלונות (בלי מקש Win) +Name[hi]=विंडोज़ प्रसंग (विन कुंजी के बगैर) +Name[hne]=विंडोज प्रसंग (विन कुंजी के बिना) +Name[hr]=Windows shema (bez Windows tipki) +Name[hsb]=Windows-šema (bjez Win-tasty) +Name[hu]=Windows (Win billentyű nélkül) +Name[ia]=Schema Windows (Sin clave Win) +Name[id]=Skema Windows (Tanpa Tombol Win) +Name[is]=Windowsþema (án Win lykils) +Name[it]=Schema Windows (senza tasto Win) +Name[ja]=Windows スキーマ (Win キーなし) +Name[kk]=Windows сұлбасы (Win пернесіз) +Name[km]=គ្រោងការណ៍ វីនដូ (គ្មាន​គ្រាប់ចុច Win) +Name[kn]=ವಿಂಡೋಸ್ ಪದ್ಧತಿ (ವಿನ್ ಕೀಲಿ ರಹಿತ) +Name[ko]=윈도 배열 (윈도 키 사용 안함) +Name[ku]=Şemaya Windowsê (Bêyê Bişkojka Winê) +Name[lt]=Windows schema (be Win klavišo) +Name[lv]=Windows shēma (bez Win taustiņa) +Name[mai]=विंडोज प्रसंग (विन कुँजी केर बगैर) +Name[mk]=Windows-шема (без копчето Win) +Name[ml]=വിന്‍ഡോസ് പദ്ധതി (വിന്‍ കീ ഇല്ലാതെ) +Name[mr]=Windows सुत्रयोजना (विना Win किल्ली) +Name[ms]=Skim Tetingkap (Tanpa Kekunci Win) +Name[nb]=Windows-oppsett (uten Windows-tast) +Name[nds]=Windows-Schema (ahn Windows-Tast) +Name[ne]=विण्डोज योजना (विन कुञ्जी बाहेक) +Name[nl]=Windows-schema (zonder Win-toets) +Name[nn]=Windows-oppsett (utan Windows-tast) +Name[or]=ୱିଣ୍ଡୋ ଯୋଜନା (ୱିନ କି ବିନା) +Name[pa]=Windows ਸਕੀਮ (ਬਿਨਾਂ Win ਸਵਿੱਚ) +Name[pl]=Motyw Windows (bez klawisza Win) +Name[pt]=Esquema do Windows (Sem a Tecla Win) +Name[pt_BR]=Esquema do Windows (sem a tecla Win) +Name[ro]=Schemă Windows (fără tasta Win) +Name[ru]=Windows (без клавиши Win) +Name[se]=Windows-čoakkáldat (Windows-boalu haga) +Name[si]=Windows ක්‍රමය (Win යතුර නොමැතිව) +Name[sk]=Windows schéma (bez klávesu Win) +Name[sl]=Shema Windows (brez tipke Win) +Name[sr]=Виндоузова шема (без тастера Win) +Name[sr@ijekavian]=Виндоузова шема (без тастера Win) +Name[sr@ijekavianlatin]=Windowsova šema (bez tastera Win) +Name[sr@latin]=Windowsova šema (bez tastera Win) +Name[sv]=Windows schema (utan Win-tangent) +Name[ta]=சாளர முறைமை (விண் விசை இல்லாமல்) +Name[te]=Windows స్కీమ్ (విన్ కీ లేకుండా) +Name[tg]=Нақшаи Windows (Бе тугмаи Win) +Name[th]=ใช้แบบของวินโดวส์ (โดยไม่ใช้ปุ่ม Win) +Name[tr]=Windows Şeması (Win Key'siz) +Name[uk]=Схема Windows (без клавіші Win) +Name[uz]=Windows qolipi (Win tugmasi bilan) +Name[uz@cyrillic]=Windows қолипи (Win тугмаси билан) +Name[vi]=Phối hợp của Windows (không có phím Cửa sổ) +Name[wa]=Djeu Windows (sins tape Win) +Name[xh]=Window Yodweliso (Ngaphandle kwe Win key) +Name[x-test]=xxWindows Scheme (Without Win Key)xx +Name[zh_CN]=Windows 方案(无 Win 键) +Name[zh_TW]=Windows 機制(沒有 Win 鍵) +Uses Win Modifier=false + +[klipper][Global Shortcuts] +clipboard_action=none +repeat_action=none +show_klipper_popup=none + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=none +Log Out Without Confirmation=none +Log Out=none +Reboot Without Confirmation=none +Run Command=none +Show System Activity=Ctrl+Alt+Del + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Ctrl+Print +Kill Window=none +Mouse Emulation=none +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=none +Switch to Desktop 11=none +Switch to Desktop 12=none +Switch to Desktop 13=none +Switch to Desktop 14=none +Switch to Desktop 15=none +Switch to Desktop 16=none +Switch to Desktop 1=none +Switch to Desktop 2=none +Switch to Desktop 3=none +Switch to Desktop 4=none +Switch to Desktop 5=none +Switch to Desktop 6=none +Switch to Desktop 7=none +Switch to Desktop 8=none +Switch to Desktop 9=none +Switch to Next Desktop=Alt+Shift+Tab +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=none +Walk Through Desktop List=none +Walk Through Desktops (Reverse)=Ctrl+Alt+Shift+Tab +Walk Through Desktops=Ctrl+Alt+Tab +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+Tab +Window Close=Alt+F4 +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=none +Window Minimize=none +Window Move=none +Window Operations Menu=Alt+Space +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Alt+Print +Window Shade=none +Window to Desktop 10=none +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=none +Window to Desktop 2=none +Window to Desktop 3=none +Window to Desktop 4=none +Window to Desktop 5=none +Window to Desktop 6=none +Window to Desktop 7=none +Window to Desktop 8=none +Window to Desktop 9=none +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/schemes/win4.kksrc b/kcms/keys/schemes/win4.kksrc new file mode 100644 index 0000000000..b28e849938 --- /dev/null +++ b/kcms/keys/schemes/win4.kksrc @@ -0,0 +1,170 @@ +[Settings] +Name=Windows Scheme (With Win Key) +Name[af]=Windows Skema (Met Win Sleutel) +Name[ar]=مخطط ويندوز (مع مفتاح Win) +Name[ast]=Esquema Windows (Con tecla Win) +Name[be]=Схема Windows (з клавішай "Win") +Name[be@latin]=Schiema „Windows” (z klavišaj „Win”) +Name[bg]=Windows (с клавиша Win) +Name[bn]=উইণ্ডোস্ স্কীম (উইন্ কী সমেত) +Name[br]=Steuñv Windows (gant stokell Win) +Name[ca]=Esquema Windows (amb la tecla Win) +Name[ca@valencia]=Esquema Windows (amb la tecla Win) +Name[cs]=Schéma Windows (s klávesou Win) +Name[csb]=Mòtiw Windows (z klawiszą Win) +Name[cy]=Cynllun Windows (Gyda'r Allwedd Win) +Name[da]=Windows-system (med Win-tast) +Name[de]=Windows-Design (mit Windows-Taste) +Name[el]=Σχήμα Windows (με πλήκτρο Win) +Name[en_GB]=Windows Scheme (With Win Key) +Name[eo]=Vindoza aranĝo (kun Vindozoklavo) +Name[es]=Esquema Windows (Con tecla Win) +Name[et]=Windowsi skeem (Win-klahviga) +Name[eu]=Windows eskema (Win teklarekin) +Name[fa]=طرحواره پنجره( با کلید پنجره) +Name[fi]=Windows-skeema (Win-näppäimellä) +Name[fr]=Agencement Windows (avec la touche « Win ») +Name[fy]=Windows-skema (mei Win-toets) +Name[ga]=Scéim Windows (Le hEochair Win) +Name[gl]=Esquema de Windows (con tecla de Windows) +Name[gu]=વિન્ડોઝ પધ્ધતિ (વિન કળ સાથે) +Name[he]=ערכת חלונות (עם מקש Win) +Name[hi]=विंडोज प्रसंग (विन कुंजी समेत) +Name[hne]=विंडोज प्रसंग (विन कुंजी समेत) +Name[hr]=Windows shema (s Windows tipkama) +Name[hsb]=Windows-šema (z Win-tastu) +Name[hu]=Windows (Win billentyűvel) +Name[ia]=Schema Windows (con clave Win) +Name[id]=Skema Windows (Dengan Tombol Win) +Name[is]=Windowsþema (með Win lykli) +Name[it]=Schema Windows (con tasto Win) +Name[ja]=Windows スキーマ (Win キーあり) +Name[ka]=Windows სქემა (Win კლავიშით) +Name[kk]=Windows сұлбасы (Win пернемен) +Name[km]=គ្រោងការណ៍ វីនដូ (មាន​គ្រាប់ចុច Win) +Name[kn]=ವಿಂಡೋಸ್ ಪದ್ಧತಿ (ವಿನ್ ಕೀಲಿ ಸಹಿತ) +Name[ko]=윈도 배열 (윈도 키 사용) +Name[ku]=Şemaya Windowsê (Digel Bişkojka Winê) +Name[lt]=Windows schema (su Win klavišu) +Name[lv]=Windows shēma (ar Win taustiņu) +Name[mai]=विंडोज प्रसंग (विन कुँजी समेत) +Name[mk]=Windows-шема (со копчето Win) +Name[ml]=വിന്‍ഡോസ് പദ്ധതി (വിന്‍ കീയോടൊപ്പം) +Name[mr]=Windows सुत्रयोजना (विन किल्ली सह) +Name[nb]=Windows-oppsett (med Windows-tast) +Name[nds]=Windows-Schema (mit Windows-Tast) +Name[ne]=विण्डोज योजना (विन कुञ्जीसँग) +Name[nl]=Windows-schema (met Win-toets) +Name[nn]=Windows-oppsett (med Windows-tast) +Name[or]=ୱିଣ୍ଡୋ ଯୋଜନା (ୱିନ କି ବିନା) +Name[pa]=Windows ਸਕੀਮ (Win ਸਵਿੱਚ ਨਾਲ) +Name[pl]=Motyw Windows (z klawiszem Win) +Name[pt]=Esquema do Windows (Com a Tecla Win) +Name[pt_BR]=Esquema do Windows (com a tecla Win) +Name[ro]=Schemă Windows (cu tasta Win) +Name[ru]=Windows (поддержка клавиши Win) +Name[se]=Windows-čoakkáldat (Windows-boaluin) +Name[si]=Windows ක්‍රමය (Win යතුර සමඟ) +Name[sk]=Windows schéma (s klávesom Win) +Name[sl]=Shema Windows (s tipko Win) +Name[sr]=Виндоузова шема (са тастером Win) +Name[sr@ijekavian]=Виндоузова шема (са тастером Win) +Name[sr@ijekavianlatin]=Windowsova šema (sa tasterom Win) +Name[sr@latin]=Windowsova šema (sa tasterom Win) +Name[sv]=Windows schema (med Win-tangent) +Name[ta]=சாளர முறைமை (விண் விசையுடன்) +Name[te]=విండోస్ పధకం (విన్ కీ తో) +Name[tg]=Нақшаи Windows (Бо тугмаи Win) +Name[th]=ใช้แบบของวินโดวส์ (ใช้ปุ่ม Win) +Name[tr]=Windows Şeması (Win Key İle) +Name[uk]=Схема Windows (з клавішею Win) +Name[uz]=Windows qolipi (Win tugmasiz) +Name[uz@cyrillic]=Windows қолипи (Win тугмасиз) +Name[vi]=Phối hợp của Windows (có phím Cửa sổ) +Name[wa]=Djeu Windows (avou tape Win) +Name[xh]=Windows Ezingagqibekanga (Nge Win-key) +Name[x-test]=xxWindows Scheme (With Win Key)xx +Name[zh_CN]=Windows 方案(有 Win 键) +Name[zh_TW]=Windows 機制(有 Win 鍵) +Uses Win Modifier=true + +[klipper][Global Shortcuts] +clipboard_action=none +repeat_action=none +show_klipper_popup=none + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=none +Log Out Without Confirmation=none +Log Out=none +Reboot Without Confirmation=none +Run Command=Meta+R +Show System Activity=Ctrl+Alt+Del + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Print +Kill Window=none +Mouse Emulation=none +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Meta+F10 +Switch to Desktop 11=none +Switch to Desktop 12=none +Switch to Desktop 13=none +Switch to Desktop 14=none +Switch to Desktop 15=none +Switch to Desktop 16=none +Switch to Desktop 1=Meta+F1 +Switch to Desktop 2=Meta+F2 +Switch to Desktop 3=Meta+F3 +Switch to Desktop 4=Meta+F4 +Switch to Desktop 5=Meta+F5 +Switch to Desktop 6=Meta+F6 +Switch to Desktop 7=Meta+F7 +Switch to Desktop 8=Meta+F8 +Switch to Desktop 9=Meta+F9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=none +Walk Through Desktop List=none +Walk Through Desktops (Reverse)=Meta+Shift+Tab +Walk Through Desktops=Meta+Tab +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+Tab +Window Close=Alt+F4 +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=none +Window Minimize=none +Window Move=none +Window Operations Menu=Alt+Space +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Alt+Print +Window Shade=none +Window to Desktop 10=none +Window to Desktop 11=none +Window to Desktop 12=none +Window to Desktop 13=none +Window to Desktop 14=none +Window to Desktop 15=none +Window to Desktop 16=none +Window to Desktop 1=none +Window to Desktop 2=none +Window to Desktop 3=none +Window to Desktop 4=none +Window to Desktop 5=none +Window to Desktop 6=none +Window to Desktop 7=none +Window to Desktop 8=none +Window to Desktop 9=none +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/schemes/wm3.kksrc b/kcms/keys/schemes/wm3.kksrc new file mode 100644 index 0000000000..e57093fd13 --- /dev/null +++ b/kcms/keys/schemes/wm3.kksrc @@ -0,0 +1,165 @@ +[Settings] +Name=WindowMaker (3 Modifier Keys) +Name[af]=WindowMaker (3 verandering sleutels) +Name[ar]=صانع النوافذ (3 مفاتيح مغيرة) +Name[ast]=WindowMaker (3 tecles de modificación) +Name[be]=WindowMaker (тры клавішы мадыфікацыі) +Name[be@latin]=Schiema „WindowMaker” (z 3 madyfikacyjnymi klavišami) +Name[bg]=WindowMaker (с 3 модификационни клавиша) +Name[bn]=উইণ্ডো-মেকার (৩-টি মডিফায়ার কী) +Name[ca]=WindowMaker (3 tecles modificadores) +Name[ca@valencia]=WindowMaker (3 tecles modificadores) +Name[cs]=WindowMaker (3 modifikátory) +Name[csb]=WindowMaker (3 klawisze zjinaczi) +Name[cy]=WindowMaker (3 Bysell Addasu) +Name[da]=Windowmaker (3 modifikatortaster) +Name[de]=WindowMaker (3 Sondertasten) +Name[el]=WindowMaker (3 τροποποιητές) +Name[en_GB]=WindowMaker (3 Modifier Keys) +Name[eo]=WindowMaker (3 modifoklavoj) +Name[es]=WindowMaker (3 teclas de modificación) +Name[et]=WindowMaker (3 muuteklahviga) +Name[eu]=WindowMaker (3 tekla modifikatzailekin) +Name[fa]=WindowMaker (۳کلید تغییردهنده) +Name[fi]=WindowMaker (3 muokkausnäppäintä) +Name[fr]=WindowMaker (3 touches de modifications) +Name[fy]=WindowMaker (3 modifikaasjetoetsen) +Name[ga]=WindowMaker (3 Eochair Mhionathraithe) +Name[gl]=WindowMaker (3 teclas modificadoras) +Name[gu]=વિન્ડોમેકર (૩ બદલનાર કળો) +Name[he]=‏WindowMaker (עם 3 מקשים משני מצב) +Name[hi]=विंडोमेकर (3 मॉडीफ़ॉयर कुंजियाँ) +Name[hne]=विंडोमेकर (3 माडीफायर कुंजियाँ) +Name[hr]=WindowMaker (3 modifikatorske tipke) +Name[hsb]=WindowMaker (3 modifikowace tasty) +Name[hu]=WindowMaker (3 módosító billentyű) +Name[ia]=Windowmaker (3 Claves Modificator) +Name[id]=WindowMaker (3 Tombol Pengubah) +Name[is]=WindowMaker (3 breytilyklar) +Name[it]=WindowMaker (3 tasti modificatori) +Name[ja]=WindowMaker (修飾キー 3 個) +Name[ka]=WindowMaker (3 ცვლადი კლავიში) +Name[kk]=WindowMaker (3 түрлендіру перне) +Name[km]=WindowMaker (សោ​​កែប្រែ ៣) +Name[kn]=ವಿಂಡೋಮೇಕರ್ (೩ ಪರಿವರ್ತಕ ಕೀಲಿಗಳು) +Name[ko]=WindowMaker (수정자 키 세 개) +Name[ku]=WindowMaker(3 Bişkojkên Guherker) +Name[lt]=WindowMaker (3 lygio klavišai – modifikatoriai) +Name[lv]=WindowMaker (3 modifikācijas taustiņi) +Name[mai]=विंडोमेकर (3 माडीफायर कुंजीसभ) +Name[mk]=WindowMaker (3 копчиња-модификатори) +Name[ml]=വിന്‍ഡോമേക്കര്‍ (3 മോഡിഫയര്‍ കീകള്‍) +Name[mr]=चौकटमेकर (3 संपादन की) +Name[nb]=WindowMaker (tre valgtaster) +Name[nds]=WindowMaker (3 Sünnertasten) +Name[ne]=विण्डोज निर्माता (३ परिमार्जक कुञ्जी) +Name[nl]=WindowMaker (3 modificatietoetsen) +Name[nn]=WindowMaker (tre valtastar) +Name[or]=WindowMaker (3ଟି ପରିବର୍ତ୍ତକ କି) +Name[pa]=WindowMaker (3 ਸੋਧ ਸਵਿੱਚਾਂ) +Name[pl]=WindowMaker (3 klawisze modyfikatorów) +Name[pt]=WindowMaker (3 Teclas Modificadoras) +Name[pt_BR]=WindowMaker (3 teclas modificadoras) +Name[ro]=WindowMaker (3 modificatori) +Name[ru]=WindowMaker (3 клавиши-модификатора) +Name[se]=WindowMaker (golbma válljenboaluiguin) +Name[si]=කවුළු සැකසීම (3 වෙනස්කම් යතුරු) +Name[sk]=WindowMaker (3 modifikačné klávesy) +Name[sl]=WindowMaker (3 spremenilne tipke) +Name[sr]=Виндоумејкер (три модификаторска тастера) +Name[sr@ijekavian]=Виндоумејкер (три модификаторска тастера) +Name[sr@ijekavianlatin]=WindowMaker (tri modifikatorska tastera) +Name[sr@latin]=WindowMaker (tri modifikatorska tastera) +Name[sv]=Windowmaker (3 väljartangenter) +Name[ta]=WindowMaker (3 Modifier Keys) +Name[te]=విండోమేకర్ (3 సవరణి కీలు) +Name[tg]=WindowMaker (3 тугмаи якҷояшуда) +Name[th]=วินโดว์เมกเกอร์ (ใช้ปุ่มเปลี่ยนหน้าที่ 3ปุ่ม) +Name[tr]=WindowMaker (3 Değiştirici Anahtar) +Name[uk]=WindowMaker (3 клавіші-модифікатори) +Name[vi]=WindowMaker (3 Phím Bổ trợ) +Name[wa]=Djeu WindowMaker (3 tapes modifieuses) +Name[x-test]=xxWindowMaker (3 Modifier Keys)xx +Name[zh_CN]=WindowMaker (三个修饰键) +Name[zh_TW]=WindowMaker(3 個變更鍵) + +[klipper][Global Shortcuts] +clipboard_action=Ctrl+Alt+X +repeat_action=Ctrl+Alt+R +show_klipper_popup=Ctrl+Alt+V + +[krunner][Global Shortcuts] +Halt Without Confirmation=none +Lock Session=Ctrl+Alt+L +Log Out Without Confirmation=none +Log Out=Ctrl+Alt+Del +Reboot Without Confirmation=none +Run Command=Alt+F2 +Show System Activity=Ctrl+Esc + +[kwin][Global Shortcuts] +Desktop Screenshot to Clipboard=Ctrl+Print +Kill Window=Ctrl+Alt+Esc +Mouse Emulation=Alt+F12 +Switch One Desktop Down=none +Switch One Desktop Up=none +Switch One Desktop to the Left=none +Switch One Desktop to the Right=none +Switch to Desktop 10=Alt+Shift+1 +Switch to Desktop 11=Alt+Shift+2 +Switch to Desktop 12=Alt+Shift+3 +Switch to Desktop 13=Alt+Shift+4 +Switch to Desktop 14=Alt+Shift+5 +Switch to Desktop 15=Alt+Shift+6 +Switch to Desktop 16=Alt+Shift+7 +Switch to Desktop 1=Alt+1 +Switch to Desktop 2=Alt+2 +Switch to Desktop 3=Alt+3 +Switch to Desktop 4=Alt+4 +Switch to Desktop 5=Alt+5 +Switch to Desktop 6=Alt+6 +Switch to Desktop 7=Alt+7 +Switch to Desktop 8=Alt+8 +Switch to Desktop 9=Alt+9 +Switch to Next Desktop=none +Switch to Previous Desktop=none +Toggle Window Raise/Lower=none +Walk Through Desktop List (Reverse)=Ctrl+Shift+Tab +Walk Through Desktop List=Ctrl+Tab +Walk Through Desktops (Reverse)=none +Walk Through Desktops=none +Walk Through Windows (Reverse)=Alt+Shift+Tab +Walk Through Windows=Alt+Tab +Window Close=Alt+Esc +Window Lower=none +Window Maximize Horizontal=none +Window Maximize Vertical=none +Window Maximize=none +Window Minimize=none +Window Move=none +Window Operations Menu=Alt+F3 +Window Raise=none +Window Resize=none +Window Screenshot to Clipboard=Alt+Print +Window Shade=none +Window to Desktop 10=Ctrl+Alt+Shift+1 +Window to Desktop 11=Ctrl+Alt+Shift+2 +Window to Desktop 12=Ctrl+Alt+Shift+3 +Window to Desktop 13=Ctrl+Alt+Shift+4 +Window to Desktop 14=Ctrl+Alt+Shift+5 +Window to Desktop 15=Ctrl+Alt+Shift+6 +Window to Desktop 16=Ctrl+Alt+Shift+7 +Window to Desktop 1=Ctrl+Alt+1 +Window to Desktop 2=Ctrl+Alt+2 +Window to Desktop 3=Ctrl+Alt+3 +Window to Desktop 4=Ctrl+Alt+4 +Window to Desktop 5=Ctrl+Alt+5 +Window to Desktop 6=Ctrl+Alt+6 +Window to Desktop 7=Ctrl+Alt+7 +Window to Desktop 8=Ctrl+Alt+8 +Window to Desktop 9=Ctrl+Alt+9 +Window to Next Desktop=none +Window to Previous Desktop=none + +[kxkb][Global Shortcuts] +Switch to Next Keyboard Layout=Ctrl+Alt+K diff --git a/kcms/keys/select_scheme_dialog.cpp b/kcms/keys/select_scheme_dialog.cpp new file mode 100644 index 0000000000..aefb919f2a --- /dev/null +++ b/kcms/keys/select_scheme_dialog.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "select_scheme_dialog.h" +#include "ui_select_scheme_dialog.h" + + +#include "KDialog" +#include "KStandardDirs" +#include +#include +#include + +SelectSchemeDialog::SelectSchemeDialog(QWidget *parent) + : KDialog(parent), + ui(new Ui::SelectSchemeDialog) +{ + m_schemes = KGlobal::dirs()->findAllResources("data", "kcmkeys/*.kksrc"); + + ui->setupUi(this); + setMainWidget(ui->layoutWidget); + + foreach (const QString &res, m_schemes) { + KConfig config(res, KConfig::SimpleConfig); + KConfigGroup group(&config, "Settings"); + QString name = group.readEntry("Name"); + + if (name.isEmpty()) { + name = res; + } + ui->m_schemes->addItem(name); + } + + ui->m_schemes->setCurrentIndex(-1); + + ui->m_url->setMode(KFile::LocalOnly | KFile::ExistingOnly); + + connect(ui->m_schemes, SIGNAL(activated(int)), + this, SLOT(schemeActivated(int))); + connect(ui->m_url->lineEdit(), SIGNAL(textChanged(QString)), + this, SLOT(slotUrlChanged(QString))); + enableButtonOk(false); +} + + +SelectSchemeDialog::~SelectSchemeDialog() +{ + delete ui; +} + +void SelectSchemeDialog::schemeActivated(int index) +{ + ui->m_url->setUrl(QUrl(m_schemes[index])); +} + + +KUrl SelectSchemeDialog::selectedScheme() const +{ + return ui->m_url->url(); +} + +void SelectSchemeDialog::slotUrlChanged(const QString &_text) +{ + enableButtonOk(!_text.isEmpty()); +} + +#include "moc_select_scheme_dialog.cpp" diff --git a/kcms/keys/select_scheme_dialog.h b/kcms/keys/select_scheme_dialog.h new file mode 100644 index 0000000000..215fb30fa5 --- /dev/null +++ b/kcms/keys/select_scheme_dialog.h @@ -0,0 +1,49 @@ +/* + * Copyright 2008 Michael Jansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SELECT_SCHEME_DIALOG_H +#define SELECT_SCHEME_DIALOG_H + +#include "KDialog" +#include + +namespace Ui +{ +class SelectSchemeDialog; +} + +class SelectSchemeDialog : public KDialog +{ + Q_OBJECT +public: + SelectSchemeDialog(QWidget *parent = 0); + ~SelectSchemeDialog(); + + KUrl selectedScheme() const; + +private Q_SLOTS: + void schemeActivated(int index); + void slotUrlChanged(const QString &); + +private: + Ui::SelectSchemeDialog *ui; + QStringList m_schemes; +}; // SelectSchemeDialog + + + +#endif diff --git a/kcms/keys/select_scheme_dialog.ui b/kcms/keys/select_scheme_dialog.ui new file mode 100644 index 0000000000..0803ad5db7 --- /dev/null +++ b/kcms/keys/select_scheme_dialog.ui @@ -0,0 +1,150 @@ + + + Michael Jansen + SelectSchemeDialog + + + + 0 + 0 + 717 + 224 + + + + Select Shortcut Scheme + + + true + + + + + 32 + 12 + 671 + 71 + + + + + + + + 0 + 50 + + + + Select one of the standard KDE shortcut schemes + + + &Standard scheme: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_schemes + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + true + + + + + + + + 150 + 0 + + + + + 1 + 0 + + + + + 0 + 50 + + + + Select a shortcut scheme file + + + &Path: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_url + + + + + + + + 3 + 0 + + + + + 0 + 50 + + + + *.kksrc + + + Select Shortcut Scheme + + + + + + + + + KComboBox + QComboBox +
    kcombobox.h
    +
    + + KDialog + QDialog +
    kdialog.h
    + 1 +
    + + KUrlRequester + QFrame +
    kurlrequester.h
    +
    +
    + + +
    diff --git a/kcms/kfontinst/AUTHORS b/kcms/kfontinst/AUTHORS new file mode 100644 index 0000000000..afabc4a809 --- /dev/null +++ b/kcms/kfontinst/AUTHORS @@ -0,0 +1 @@ +Craig Drummond diff --git a/kcms/kfontinst/CMakeLists.txt b/kcms/kfontinst/CMakeLists.txt new file mode 100644 index 0000000000..40d394ff89 --- /dev/null +++ b/kcms/kfontinst/CMakeLists.txt @@ -0,0 +1,49 @@ +if (KFI_BUILD_STANDALONE) + cmake_minimum_required(VERSION 2.6.4) + find_package(KDE4 "4.6.0" REQUIRED) + find_package(Qt4 REQUIRED) + find_package(Freetype REQUIRED) + find_package(Fontconfig REQUIRED) + find_package(Strigi REQUIRED) + find_package(X11 REQUIRED) + macro_log_feature(X11_Xft_FOUND "libxft" "X FreeType interface library" "http://www.x.org" FALSE "" "Font installer and font previews") + include(KDE4Defaults) +endif () + +if (X11_Xft_FOUND) + check_include_files(locale.h HAVE_LOCALE_H) + configure_file(config-fontinst.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-fontinst.h) + include_directories( + ${FREETYPE_INCLUDE_DIRS} + ${FONTCONFIG_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/lib + ${CMAKE_CURRENT_BINARY_DIR}/lib + ${CMAKE_CURRENT_SOURCE_DIR}/dbus + ${CMAKE_CURRENT_BINARY_DIR}/dbus + ${CMAKE_CURRENT_SOURCE_DIR}/viewpart + ${CMAKE_CURRENT_SOURCE_DIR}/kcmfontinst + ${CMAKE_CURRENT_BINARY_DIR}/kcmfontinst) + add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) + + set(libkfontinstdbusiface_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/dbus/FontinstIface.cpp) + set(libkfontinstview_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/viewpart/FontPreview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/viewpart/PreviewSelectAction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/viewpart/CharTip.cpp ) + set(libkfontinstactionlabel_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/kcmfontinst/ActionLabel.cpp) + set(libkfontinstjobrunner_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/kcmfontinst/JobRunner.cpp + ${libkfontinstactionlabel_SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/kcmfontinst/FontsPackage.cpp) + + add_subdirectory( lib ) +# add_subdirectory( dbus ) + add_subdirectory( kcmfontinst ) + add_subdirectory( apps ) + add_subdirectory( kio ) + add_subdirectory( thumbnail ) + add_subdirectory( viewpart ) + + ecm_install_icons( ${ICON_INSTALL_DIR} ) + +endif () diff --git a/kcms/kfontinst/ChangeLog b/kcms/kfontinst/ChangeLog new file mode 100644 index 0000000000..fe87b58aea --- /dev/null +++ b/kcms/kfontinst/ChangeLog @@ -0,0 +1,562 @@ +KDE4.4 +====== +1. Create a dbus daemon for handling font installtion, and have the kcm and ioslave + use this. +2. Use policykit to control installation of system-wide fonts. +3. Merged progress, skip, cancel, and error dialogs used when installing, etc., into + 1 dialog. +4. Simplify layout. +5. Remove simple mode - always use font management mode. +6. When multiple fonts are selected, use a list style preview of all selected fonts. +7. Because of the above, remove the in-line previews from the font list. + +KDE4.3 +====== +1. Dont cache previews to disk. + +KDE4.2 +====== +1. Added zoom controls to font preview. +2. Made viewer application a KUniqueApplication + +KDE3.5 -> KDE4.0 +================ + 1. Enabling/disabling of fonts. + 2. Creation of "Font Groups" + 3. Fonts are now grouped via family name, i.e. shown as: + + Courier [4] + - Times [2] + Regular + Italic + ...where the number in brackets indicates the number of styles. Clicking + on the expand icon (+) will then show the list of styles. + 4. Add ability in font view part to show unicode tables. + 5. Remove all references to Speedo fonts - haven't been supported since + KDE3.3! + 6. Only one view - list view. However, each item now has a small preview. + 7. Xft is now required. + 8. Remove Fontmap creation. + 9. Creation of fonts.dir & fonts.scale will be done via mkfontscale and + mkfontdir. +10. Legacy X is only configured for a folder if it already contains a fonts.dir + file. +11. Font installtion is *much* faster - as config files are now updated after + all fonts are installed, as opposed to every 50 fonts. +12. Folders are no longer added to X's config files - because of 10 above. + The only system config file that will be altered is either + /etc/fonts/local.conf or /etc/fonts/conf.d/00kde.conf (dependant upon + your fontconfig version) +13. When installing font files, install into a sub folder named after the 1st + character. e.g. + wibble.ttf -> ~/.fonts/w/wibble.ttf +14. Better unique names when creating font packages. +15. Use zip and not tar.gz for font packages. +16. Allow import of fonts/package. +17. Allow fonts/package to be installed via konqueror service menu. +18. Better TTC handling. +19. When installing to fonts:/, as non-root, automatically install to fonts:/Personal. + To install to fonts:/System need to explicity copy to fonts:/System. +20. Hide kfontview from KMenu - its only required by kcm, and when cliking on a font. +21. Renamed kcmfontinst to just fontinst. +22. When printing, use external kfontprint app - that way newly installed fonts can + also be printed. +23. When run as non-root, the kcontrol module will have a combo box allowing the user + to select Personal or System fonts. +24. Better bitmap font previews - list of sizes obtained via fontconfig. +25. Regular fonts listed as fonts:/, Regular - e.g. fonts:/Times, Regular +26. Use a kio_font_helper app when accesing fonts:/System - much faster, as kdesu is + not required to be called for each action. +27. Add a "Find Duplicates" tool, that looks for multiple installtions of the same font. + i.e. different locations, or the same location but different filename case + (e.g. times.ttf / times.TTF) + +KDE3.4 -> KDE3.5 +================ +1. When copying a file out of fonts sub-system, copy as filename, or .fonts.tar.gz + in the case of multiple font files mapped to the same font name. + + e.g. Times New Roman -> times.ttf + Helvetica, Bold Oblique-> Hevetica, Bold Oblique.fonts.tar.gz + Which contains: + 75dpi_helvBO10.pcf.gz + 75dpi_helvBO12.pcf.gz + 100dpi_helvBO10.pcf.gz + 100dpi_helvBO12.pcf.gz + ...etc + +2. New mimetype: fonts/package - to cater for the above. +3. Add settings to enable/disable configuring fonts for legacy X, and Ghostscript + (X defaults to true, and Ghostscript defaults to false) +4. Show mime-type in detailed view. +5. Simple font sample printing - but only of installed fonts! +6. Add toggle button to control display of bitmap fonts. + +KDE3.3 -> KDE3.4 +================ +1. Font listing comes from fontconfig. This means that fonts will be grouped, i.e. + previously each size of a bitmap font was shown seperately, now only 1 font + will be displayed which represents all sizes. +2. Only fonts, and not folders (except System and Personal), are now shown. +3. Creation of afms from pfa/pfb and a pfm file. +4. Previews are drawn via Xft - previously FreeType was called directly. +5. New font preview look. +6. No longer dependant upon file extension. +7. Check for FPE of "fontconfig" -> if set, then no need to configure X core fonts. + +KDE3.2 -> KDE3.3 +================ +1. List fonts as "Full Name" -> i.e. "Times New Roman". +2. Preview of bitmap fonts. +3. No fontname-title in thumbnails - as fonts:/ lists the fontnames! +4. When copying to fonts:/ (as non-root) only ask for destination if more than 5 seconds + since previously asked. +5. Add a konqueror service menu "Install" +6. Use FreeType2 for reading Type1 - instead of parsing the pfa/pfb header. +7. FamilyName is now the fonts *real* family name - no adding of style information. +8. Add extra style information (which was previously added to FamilyName) into the XLFD. +9. Consider regular weight to be medium (same as mkfontscale). +10. Default to width=normal, weight=medium if not set. +11. Allow change of preview string. +12. Allow zooming in/out of preview. +13. Waterfall font preview. +14. Use font preview part in the KControl module - less code duplication. +15. Include simple fontviwer app - basically just an application wrapper for the viewpart. + +KDE3.1 -> KDE3.2 +================ +1. Re-designed (yet again...) to be a kio slave. As a user, starting fonts:/ will display + + Personal Lists contents of $HOME/.fonts and $KDEHOME/share/fonts (where previous installer installed to) + Fonts are installed to $HOME/.fonts + + System Lists contents of /usr/local/share/fonts, /usr/share/fonts, and /usr/X11R6/lib/X11/fonts + Fonts are installed to /usr/local/share/fonts (as per FHS) + + To install fonts system wide, just drop onto "System" and root's password will be asked + for. + + As root, fonts:/ will show the same as fonts:/System (but without the System part...) + +2. New kcontrol module that uses fonts:/ +3. Removed: + AFM creation -- only really required (TTF wise) for SO <6.0 + StarOffice configuration (S0 6.0 / OO.o is *much* better anyway) +4. X font server (xfs) - if used - is refreshed by sending a SIGUSR1 instead of + relying on a /etc/init.d/xfs script. This is much more portable. +5. Simple FontView part for konqueror - this is basically a big re-sizable preview of the font. +6. Speed up creation of fonts.dir and fonts.scale - by reading in any existing files, and using the + entries from these instead of loading and testing the font (if listed). +7. Better font preview and thumbnails. +8. Only add a dir to fontpath if fonts.dir has greater than 0 entries! +9. Use /etc/fonts/local.conf as root fontconfig file. +10. Only add dirs to fontconfig if *not* a sub-dir of an existing dir. +11. Ensure that top-level fonts dir is always in fontpath. +12. A Fontmap file is created in each sub dir, which is then combined into 1 top level Fontmap file. + ~/.fonts/Fontmap for normal users, and /etc/fonts/Fontmap for root. +13. Modify /Fontmap to contain: + (/etc/fonts/Fontmap) .runlibfile + ...as this is the system-wide Fontmap file created. As for the per-user, one, hmmm... +14. When a folder is configured, ensure fonts.dir/fonts.scale/Fontmap/.fonts-config-timestamp (SuSE + specific) all have the same timestamp (if they exist). Helps to discover if a folder has been + modified - in which case it needs to be reconfigured (and should happen automatically). +15. Add support for TrueType Collections (.ttc), and OpenType (.otf) fonts. Currently TTC's are only + configured for X - need to also configure GS to see other faces. +16. CID fonts are *not* handled - therefore don't list the X11 CID directory, and don't let users + try to create this. +17. Don't list "encodings" in fonts:/System - and don't allow users to create this. +18. Use XFree86's libfontenc (if found) to read font-encodings. +19. Handle 1bpp glyphs in thumbnail code. +20. Ensure X fontpaths *never* end in "/" - i.e. when write XF86Config, xfs/config, + or fontpaths remove any trailing "/" +21. When adding/removing an unscaled dir from X font path, ensure ":unscaled" is + part of the path! +22. Call fc-cache on top-level dir, not on each dir. +23. Use "~" in Xft config and user X config files -> e.g. /home/user/.fonts -> ~/.fonts +24. Remove top-level dir spec from top-level fontmap, e.g. + + TimesNewRomanPSMT (/home/user/.fonts/wibble/times.ttf); + + ...becomes... + + TimesNewRomanPSMT (wibble/times.ttf); + +25. Add meta-data for AFM files to KFile plugin. + +0.11-> KDE3.1 +============= +1. Re-design of UI - removed "Install From" view. +2. Created a KIO/thumbnail font preview class. +3. Fonts are installed/uninstalled on "Apply". +4. Settings are saved on "Apply". +5. Settings tab simplified - some uneccesary settings removed. +6. Removal of Anti-Alias tab - relevant settings moved to kcmfonts. +7. Add kfile-plugin to display Meta data for fonts. +8. Remove settings wizard. +9. Drop use of internal version numbering - not tagging CVS anyway, so whats the point? +10. DCOP interface. +11. Remember size of main window when run via kcmshell. + +0.10->0.11 (KDE3.0) +=================== +1. Port to KDE3/Qt3. +2. Add support for CUPS's Fontmap. +3. Create backups of system files. +4. When install symbol encoding fonts, set encoding to "glyphs-fontspecific" in XftConfig. +5. When install monospaced fonts, set spacing to mono in XftConfig. +6. When first run (as root) - checks XFree86 config file to see if a font server is being used, if + so then fs/config is used as the config file, and "/etc/rc.d/init.d/xfs restart" is selected as + as the X refresh command. +7. Only install fonts that are useable. +8. Add checkbox to enable overwriting of existing AFMs. +9. Remember open directories in advanced mode. +10. Add support for .Z compressed Bitmap fonts. +11. Read Type1 encodings from .afm files if listed as "array" in pfa/pfb. + +0.10b11->0.10 +============= +1. Version added to KDE CVS. +2. Modified some keyboard shortcuts to remove conflicts. +3. Disable "Touch" and "Delete" folder if top-level X fonts dir is selected. + +0.10b10->0.10b11 (Test version...) +================ +1. Removed "root"/"Modify" and "Help" buttons - this gives more space to font lists, plus when using "root"/"Modify" root's + config files are not being saved. +2. "IsFixedPitch" flag in AFMs produced incorrectly - was outputing "false" for monospaced fonts! +3. Added rounding to AFM metric scaling. +4. When creating AFMs, check that each characters' BBox is within the main BBox - this is a quick fix for wingdings.afm, + which seems to be giving incorrect results. +5. StarOffice 6 / OpenOffice only need AFM files for Type1 fonts - plus no config files need to be altered. Therefore, added the ability to + select which font types AFMs should be created for. +6. Output *all* characters from a font into the AFM file. +7. Fixed a bug with Full/Family name in Speedo fonts. +8. For TrueType, Type1, and Speedo fonts - family name is obtained by using the fonts' FullName, remove FamilyName (read from file), remove + any weight, width, or italic designation, and re-add FamilyName. (This is because some fonts are named + , and was previously being lost). +9. When adding encodings to lists, check that they aren't alredy inserted. + +0.10b9->0.10b10 (Test version...) +=============== +1. Fixed a problem with non-enabled install button in basic mode - again, thanks to Hardy Griech for spotting this. + +0.10b8->0.10b9 (Test version...) +============== + +*** NOTE +*** Please remove any Kfontinst generated StarOffice psstd.fonts and Ghostscript Fontmap output before using this version + +1. StarOffice psstd.fonts generated output is no longer marked line-by line, instead it is marked as a section, e.g. + + # kfontinst /usr/X11R6/lib/X11/fonts/TrueType + + # kfontinst /usr/X11R6/lib/X11/fonts/TrueType + + ...Likewise for Ghostscript's Fontmap + +2. Limited generated StarOffice psstd.fonts lines to 126 characters, and lines longher than this will not be output. It appears + as if this is the max line len StarOffice will accept - thanks to Hardy Griech for discovering this. +3. Fixed a bug where a static pointer was not reset to NULL when module was unloaded. +4. When chekcing ps-fonts, I was looking for the string "%!PS-Adbobe", however the hershey fonts just has "%!FontType" - therefore + I've change the code to just look for "%!" +5. For pcf fonts, look for FAMILY as well as FAMILY_NAME +6. Construct name from xlfd for bitmap fonts where can't get seperate components + +0.10b7->0.10b8 (Test version...) +============== + +*** NOTE +*** Please remove any Kfontinst generated Ghostscript and/or StarOffice output before using this version + +1. Forgot to extract foundry from bitmap fonts - however, changed bitmap Xlfd creation, see below. +2. Extract Xlfd from Bitmap fonts directly - not all fonts have each seperate component available. Thanks to Claudio Bandaloukas + for helping me discover the various bugs with Bitmap output. +3. When displaying bitmap details, if individual entries (family, point size, etc) can't be read, then the + Xlfd will be displayed. +4. Ghostscript & StarOffice include guards changed from "kfontinst" to "kfi" -- this will *require" removing of any previous output! +5. Shortened generated TrueType foundry fields - to help with StarOffice + +0.10b6->0.10b7 (Test version...) +============== +1. Fixed a bug with string-to-width conversion for Type1 and bitmap fonts +2. Fixed some compile bugs if no Xft.h found +3. Remove any fonts.alias when deleting a dir +4. When try to open Type1 or Speedo fonts, check magic numbers - for Speedo check char[0]=='D' or 'd', char[1]==num, char[2]=='.', and char[3]==num + +0.10b5->0.10b6 (Test version...) +============== +1. Fonts with "Normal" weight now installed as "Medium" +2. Added support for "Oblique" in xlfd +3. Check is performed to see if destination is writeable before enabling "Install" button, likewise for the "Remove" button. + +0.10b4->0.10b5 (Test version...) +============== +1. Forgot to reset the made-changes state of XftConfig when saved! +2. Add a validator to math & edit line-edits to disallow usage of double-quotes & tabs +3. Select correct default entries for field-name combos when adding an XftRule. +4. Removed check for number of items in match list, as these are not always required (such as for the sub-pixel hinting + rule.) +5. When setting rgba - use symbolic name - previously always setting to 0! +6. Modified help a little +7. Reduced Advanced mode list-view treeStepSize to 10 pixels (from default of 20), this makes it easier for browsing + /usr/X11R6/lib/X11/fonts/etc... +8. Fixed bug where could not remove Xft exclude range! +9. Fixed a bug where uninstalled items could cause duplicates in "Install from" list. +10. Renamed the "Configure System" button to "Apply" - this should make the neccessity of the option more obvious. +11. Fixed display of uninstall folder. +12. Added "include" and "includeif" directives from XftConfig to editor. + +0.10b3->0.10b4 (Test version...) +============== + +*** NOTE +*** Please delete your existing ~/.kde/share/config/kfontinstrc -or- ~/.kde2/share/config/kfontinstrc file +*** before using this version + +1. Added support for X font server config files. +2. Show "unscaled" directories in italic. +3. Abilty to set directories as scaled/unscaled. +4. Modified GUI slightly so that it works beter with Liquid style. +5. Added chack to make sure XftConfig file exists before trying to parse. +6. Advanced editing of XftConfig. +7. Added help on XftConfig - from Danny Tholen (obiwan@mailmij.org) +8. Removed some memory leaks +10. Default folders changed for non-root users. KFontinst will now (upon initial start-up) select the following: + + X fonts dir: $KDEHOME/share/fonts + XConfig file: $KDEHOME/share/fonts/fontpaths + XftConfig file: $HOME/.xftconfig + Fontmap file: $KDEHOME/share/fonts/Fontmap + + ...This should make it possible for users to install fonts without being root. However, problems may arrise when + configuring StarOffice - as this requires some other files to be modified, which won't be possible if SO has been installed + by root. + + ...Also to accomplish this, some changes are needed to 'startkde' - see file README.startkde + +11. Because of the above, when started as non-root, KFontinst will create Type1 and TrueType sub-folders in + $KDEHOME/share/fonts - if they do not already exist. +12. Moved XftConfig stuff from a sub-page of settings tab into its own tab. +13. Added question dialog if module is unloaded before system has been configured. +14. Added ability to "touch" a X font folder - marking it as being modified, so that a re-configure of the that folder + can be done in order to create AFMs, modify encoding, etc. + +0.10b2->0.10b3 (Test version...) +============== +1. When locating Ghostscript's Fontmap file, sub-directories (up to a level of 4) are searched - this allows + for the possiblity of users using a different version of Ghostscript. +2. Create a fonts.scale as well as fonts.dir - just incase another program runs mkfontdir. +3. Fixed an error when creating AFMs for some symbol-encoded fonts. +4. Re-worded SettingsWizard "Folders/Files" tab. +5. Added a checkbox to Ghostscript configuration. +6. Added support for XftConfig. +7. Added/fixed support of Type1 fonts with no FullName or FamilyName fields - such as the hershey fonts. +8. Fixed some bugs when configuring with --enable-final. +9. Basic html help added. + +0.10b1->0.10b2 (Test version...) +============== +1. Minor compile error (struct declared as private, but used elsewhere!) + +0.9.2->0.10b1 (Test version...) +============= +1. Almost a complete re-write, +2. Handles Speedo, and Bitmap fonts +3. fonts.dir & encodings.dir are now created internally - no need for ttmkfdir +4. Re-design of GUI. +5. Advanced mode where X11 folder structure is displayed and all font types may be installed - and a Basic + mode where the X11 folder structure is hidden, and only TrueType and Type1 fonts may be installed. +6. Settings wizard. +7. Application is now a kcontrol module. +8. Complete X11 fonts directory structure is now managed - no need for seperate 'Managed' directory. +9. X11.PS is no longer created, instead the StarOffice printer file (*.PS) is now modified. +10. Ported to FreeType2. +11. Support more encodings - encodings combos now list standard encodings as well as those read from + .enc(.gz) files. +12. Internal AMF creator for Type1 and TrueType fonts - ttf2pt1 and pf2afm.ps are no longer used/supplied. +13. Removed the 'Process AFMs' & 'Delete AFMs' options - all AFMs are created be KFontinst, therefore they + should be OK for StarOffice and AbiWord. +14. If a writable XF86Config file is found - then complete folders may be installed, and folders in the X11 + directory may be uninstalled or disabled (i.e. the folder is not deleted, but it's entry is removed from + the XF86Config file). +15. No longer supply .enc files with KFontinst - they should be provided by the distro. + +0.9.1->0.9.2 +============ +1. Removed a bug where the "Configure System" menu entry was always disabled! +2. Spelling error in Settings dialog. +3. t1lib has problems with some of the fonts supplied with Adobe acrobat - therefore, if t1lib fails to load the + font, then KFontinst itself will try to read the header information (although no preview will be available, + everything else should still work). +4. Fixed multiple installing of programs in other/ directory. For instance KFontinst's version of ttmkfdir + was being installed into $(PREFIX) (usually /usr/bin) as well as $(KDE_DATADIR)/kfontinst - this was incorrect + as KFontinst will only use the version in $(KDE_DATADIR)/kfontinst, and it was possible that a previous version + of ttmkfdir (such as that supplied with XFree86) would have been overwritten. +5. Fixed bug where the user was allowed to select (and subsequently install) fonts which could not be loaded correctly. +6. Added the ability to enter a custom preview string. + +0.9->0.9.1 +========== +1. Fixed a few compile errors. +2. Fix to html formatting error. +3. Fixed a ./configure error if t1lib was not found (the string NO was being used as the + librarary name, instead of an empty string!) +4. Modified the reading of Type1 header information. + +0.8.3->0.9 +========== +1. Converted to KDE2. +2. Rearranged this file! +3. Removed command line interface - KDE2's command line stuff is way different! +4. Re-created dialogs with Qt designer. +5. Removed ProgressDialog, and replaced with a progress bar on a new statusbar. +6. Modified configure script to check for FreeType & t1lib. +7. If an encoding (not unicode) is selected, then the .enc file is copied to the X11 fonts directory. +8. As with the .enc files, the StarOffice .xpp files are also copied, and no longer just sym linked. +9. Removed enabling/disabing of Configure System button. +10. Fontmap.X11 is no longer created, instead the real Fontmap file itself is modified. + +0.8.2->0.8.3 +============ +1. Modified 'kfontinst.kdelnk' so that kdesu is now used - so that a user will automatically be prompted + for the root password. +2. Modified dialogs to use the KDE caption ("Font Installer") instead of the app name ("kfontinst") +3. Corrected size of Configure dialog. +4. Fixed a minor bug where if all fonts were uninstalled, the 'Configure System' button was disabled - therefore not + allowing you to activate the changes! +5. Added a command line interface. (type 'kfontinst --help' for details) +6. Added option to automatically fix TTF postscript names upon install. + +0.8.1->0.8.2 +============ +1. Fixed a bug which always had SO configuration disabled! +2. Fixed a bug when selecting Unicode encoding. +3. Changed "Fonts/Uninstalled" menu entry to "Fonts/Disk" +4. Added keyboard short-cuts to dialogs + +0.8->0.8.1 +========== +1. Fixed a bug where X configuration would fail if no TT fonts present. +2. If no fonts are installed, then the system configuration button/menu-entry is now disabled. +3. Changed menu structure to add 'Fonts' menu. + +0.7.4->0.8 +========== +1. Changed location of StarOffice stuf from /xp3 to just + -- As StarOffice 5.2 has 'xp3' within a 'share' sub-dir. +2. Changed structure of config file to be more modular. +3. Modified internal code structure to allow easier additon of extra apps to be configured. (NOTE: If any + apps need to be configured, then I'll also [later on] modify the Settings & Configure dialogs to + accomodate these.) +4. Because of 3, added a 'StarOffice' check to the settings dialog. If this is not seleted, then no check + is performed to make sure the SO dir is OK - and the option to config SO is diabled on the config dialog. +5. Added check when installing font to make sure that it's not already installed. + +0.7.3->0.7.4 +============ +1. Changed location of Fontmap.X11 -- from /lib/Fontmap.X11 to + /Fontmap.X11. As it seems some ghostscript installations don't + have the 'lib' sub directory. +2. Improved the documentation a little - added a FAQ section + +0.7.2->0.7.3 +============ +1. Very minor bug fix. + +0.7.1->0.7.2 +============ +1. Added more detailed error messages when system configuration fails. + +0.7->0.7.1 +========== +1. Removed lots of debug info from ttf2pt1, and afm.pl -- this should drastically speed up afm creation. +2. Modified ttf2pt1 to accept a parameter to just create .afm files +3. Added option to modify a .afm file when installing. +4. Added "Unicode" to list of encodings that can be used. +5. Removed kfontinst-cp1252.enc, kfontinst-cp1252.xpp -- these were hacks anyway, and seing as Qt2 is going to + support cp1252 by a hard-coded codec, there's no real point... +6. Rearranged the Configure System dialog - so that Force AFM regeneration is grouped next to the Generate AFMs option. +7. Encoding files now stored in /share/apps/kfontinst/Encodings + +0.6.1->0.7 +========== +1. Modified ttmkfdir & ttf2pt1 to allow usage of X11 style font re-encoding files. +2. Because .enc files are now used by kfontinst, removed the possibility of using gzipped encodings. +3. Added the ability to delete an installed font's .afm file. +4. Fixed a bug in the TtfPsNameFixer class - this would cause ttf2pt1 to creash when accessing a modified font! +5. Font encodings are now stored in /share/x11encodings +6. Removed the reencode shell script, as the encodng is all done by ttmkfdir. +7. Removed xfinst shell script - handled internally. +8. Supplied a kfontinst-cp1252 encoding - with the ugly single-quotes normaly found in .ttf files remapped to + the nice looking ones. +9. Added functionality, when configuring StarOffice, to select an appropriate xprinter.prolog for the selected + encoding (if one exists)... + +0.6->0.6.1 +========== +1. Fixed a display bug in the 'Un/Exclude from StarOffice" options. + +0.5->0.6 +======== +1. Added the ability to 'fix' the postscript names in a ttf file. +2. Fixed some missing changes to help files. + +0.4->0.5 +======== +1. Discovered a patch that modifies StarOffice's xprinter.prolog so that font's don't need to be modified + to use the microsoft cp1252 enocding scheme. (Previosuly the PS output from StarOffice would not print + OK with ghostscript - when using extra characters - unless the .ttf file was modified.) +2. Because of 1, removed the abilty to modify a TrueType font's internal charactermap - this was a hack anyway. +3. xfinst now uses mkfontdir to create encodings.dir - instead of kfontinst's install procedure copying a standard + one in (this didn't actually work...) +4. As kfontinst no longer reads the .enc files themselves, added the ability to use .enc.gz files as well + when selecting an encoding for X. +5. Re-wrote xfinst & reencode to be plain 'sh' scripts, as opposed to 'tcsh' scripts. + +0.3->0.4 +======== +1. All X fonts will now be placed with in a directory - "Managed" - this makes things easier for + AbiWord, and maybe others. +2. encodings.dir & Encodings/ will now be placed within this new "Managed" dir. +3. Only 1 StarOffice .PS file will be created - X11.PS +4. Only 1 Fontmap will be created - Fontmap.X11 - and this will be placed within + /lib +5. Because of 4, an option has been added to the Settings dialog to specify the location of + Ghostscript. +6. Because of 1, removed the font option from the Configure dialog. +7. Fixed an error with getting PS name from TT font - PS names are not allowed to have spaces, but in + fences.ttf it does. FontEngine.cpp will now check for, and fix, this - using the same 'algorithm' as that + of ttf2pt1 (which means the names will tie up with those in the .afm files). +8. Added some improvements to control of dialogs. + +0.2.1->0.3 +========== +1. Reverted back to naming .afm files .afm - and renaming any conflicting fonts. +2. Speeded up copying of files - by copying preview bitmap as opposed to regenerating it! +3. Removed need for FontMetrics directory - .afm files now placed within TrueType or Type1 dir, + and sym links are produced for StarOffice. + + 1. & 3. should now make things easier for AbiWord. + +4. Fixed output of Fontmap so that "URW Gothic" will be aliased as "UrwGothic-Roman" (etc.) as + this is what Qt will output. +5. Added more processing of .afm files - this makes them OK for AbiWord. + +0.2->0.2.1 +========== +1. Modified start-up progress dailog, and added progress dialogs to main window when scanning + fonts. These will only appear if numTTfonts>X || numT1fonts>Y + +0.1->0.2 +======== +1. Combined views of installed TrueType and Type1 fonts into 1 list. +2. When uninstalling a font, can now move the font to another directory - or delete. +3. Used t1lib so that Type1 fonts can also be previewed. +4. Changed Fontmap creator to dynamically allocate memory for each font-category. +5. .afm files are now named as ..afm - this removes the need + for renaming the .afm file if there exists Type1 and TrueType fonts with the same + fontname. +6. Removed the re-scanning of the install directories whenever a font is added. +7. Added support for extra Type1 font weights. +8. Added a start-up screen to inform the user that the installed/disk fonts are being scanned. +9. When exiting, confirmation is now only asked if the system has been changed and not + reconfigured. diff --git a/kcms/kfontinst/Messages.sh b/kcms/kfontinst/Messages.sh new file mode 100644 index 0000000000..0634d49b0a --- /dev/null +++ b/kcms/kfontinst/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc` >> rc.cpp +$XGETTEXT rc.cpp */*.cpp */*.h -o $podir/kfontinst.pot +rm -f rc.cpp diff --git a/kcms/kfontinst/apps/CMakeLists.txt b/kcms/kfontinst/apps/CMakeLists.txt new file mode 100644 index 0000000000..5519e87442 --- /dev/null +++ b/kcms/kfontinst/apps/CMakeLists.txt @@ -0,0 +1,51 @@ +include_directories( + ${kcms_SOURCE_DIR}/kfontinst/kcmfontinst/ + ${kcms_SOURCE_DIR}/kfontinst/viewpart/ + ${CMAKE_CURRENT_BINARY_DIR} + ) + +set(kfontinst_bin_SRCS ${libkfontinstjobrunner_SRCS} ${libkfontinstdbusiface_SRCS} Installer.cpp ) +# qt5_add_dbus_interface(kfontinst_bin_SRCS ../dbus/org.kde.fontinst.xml FontInstInterfaceBase) + +set(kfontprint_bin_SRCS ${libkfontinstactionlabel_SRCS} Printer.cpp ) +set(kfontview_bin_SRCS Viewer.cpp ) + +add_executable(kfontinst_bin ${kfontinst_bin_SRCS}) +add_executable(kfontprint_bin ${kfontprint_bin_SRCS}) +add_executable(kfontview_bin ${kfontview_bin_SRCS}) + +set_target_properties(kfontinst_bin PROPERTIES OUTPUT_NAME kfontinst) +set_target_properties(kfontprint_bin PROPERTIES OUTPUT_NAME kfontprint) +set_target_properties(kfontview_bin PROPERTIES OUTPUT_NAME kfontview) + +target_link_libraries(kfontinst_bin + Qt5::X11Extras + KF5::Archive + KF5::IconThemes + KF5::KIOCore + KF5::KIOWidgets + KF5::XmlGui + KF5::KDE4Support + ${X11_X11_LIB} + kfontinst +) +target_link_libraries(kfontprint_bin + Qt5::PrintSupport + KF5::IconThemes + KF5::KDE4Support + ${X11_X11_LIB} + ${FREETYPE_LIBRARIES} + kfontinstui + kfontinst +) +target_link_libraries(kfontview_bin KF5::Parts KF5::XmlGui kfontinstui kfontinst ) + +install(TARGETS kfontinst_bin ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(TARGETS kfontprint_bin DESTINATION ${LIBEXEC_INSTALL_DIR} ) +install(TARGETS kfontview_bin ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(FILES kfontviewui.rc DESTINATION ${DATA_INSTALL_DIR}/kfontview ) +install(PROGRAMS kfontview.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install(FILES installfont.desktop DESTINATION +${SERVICES_INSTALL_DIR}/ServiceMenus ) + +ecm_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/kcms/kfontinst/apps/CreateParent.h b/kcms/kfontinst/apps/CreateParent.h new file mode 100644 index 0000000000..f42158ee9e --- /dev/null +++ b/kcms/kfontinst/apps/CreateParent.h @@ -0,0 +1,73 @@ +#ifndef __CREATE_PARENT_H__ +#define __CREATE_PARENT_H__ + +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +// +// *Very* hacky way to get some KDE dialogs to appear to be transient +// for 'xid' +// +// Create's a QWidget with size 0/0 and no border, makes this transient +// for xid, and all other widgets can use this as their parent... +static QWidget * createParent(int xid) +{ + if(!xid) + return NULL; + + QWidget *parent=new QWidget(NULL, Qt::FramelessWindowHint); + + parent->resize(1, 1); + parent->show(); + + XWindowAttributes attr; + int rx, ry; + Window junkwin; + + XSetTransientForHint(QX11Info::display(), parent->winId(), xid); + if(XGetWindowAttributes(QX11Info::display(), xid, &attr)) + { + XTranslateCoordinates(QX11Info::display(), xid, attr.root, + -attr.border_width, -16, + &rx, &ry, &junkwin); + + rx=(rx+(attr.width/2)); + if(rx<0) + rx=0; + ry=(ry+(attr.height/2)); + if(ry<0) + ry=0; + parent->move(rx, ry); + } + parent->setWindowOpacity(0); + parent->setWindowTitle(QLatin1String("KFI")); + + return parent; +} + +#endif diff --git a/kcms/kfontinst/apps/Installer.cpp b/kcms/kfontinst/apps/Installer.cpp new file mode 100644 index 0000000000..a10a6814b7 --- /dev/null +++ b/kcms/kfontinst/apps/Installer.cpp @@ -0,0 +1,153 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "Installer.h" +#include "Misc.h" +#include "FontsPackage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "JobRunner.h" +#include "CreateParent.h" + +namespace KFI +{ + +int CInstaller::install(const QSet &urls) +{ + QSet::ConstIterator it(urls.begin()), + end(urls.end()); + bool sysInstall(false); + CJobRunner *jobRunner=new CJobRunner(itsParent); + + CJobRunner::startDbusService(); + + if(!Misc::root()) + { + switch(KMessageBox::questionYesNoCancel(itsParent, + i18n("Do you wish to install the font(s) for personal use " + "(only available to you), or " + "system-wide (available to all users)?"), + i18n("Where to Install"), KGuiItem(i18n(KFI_KIO_FONTS_USER)), + KGuiItem(i18n(KFI_KIO_FONTS_SYS)))) + { + case KMessageBox::No: + sysInstall=true; + break; + case KMessageBox::Cancel: + return -1; + default: + break; + } + } + + QSet instUrls; + + for(; it!=end; ++it) + { + KUrl local(KIO::NetAccess::mostLocalUrl(*it, NULL)); + bool package(false); + + if(local.isLocalFile()) + { + QString localFile(local.toLocalFile()); + + if(Misc::isPackage(localFile)) + { + instUrls+=FontsPackage::extract(localFile, &itsTempDir); + package=true; + } + } + if(!package) + { + KUrl::List associatedUrls; + + CJobRunner::getAssociatedUrls(*it, associatedUrls, false, itsParent); + instUrls.insert(*it); + + KUrl::List::Iterator aIt(associatedUrls.begin()), + aEnd(associatedUrls.end()); + + for(; aIt!=aEnd; ++aIt) + instUrls.insert(*aIt); + } + } + + if(instUrls.count()) + { + CJobRunner::ItemList list; + QSet::ConstIterator it(instUrls.begin()), + end(instUrls.end()); + + for(; it!=end; ++it) + list.append(*it); + + return jobRunner->exec(CJobRunner::CMD_INSTALL, list, Misc::root() || sysInstall); + } + else + return -1; +} + +CInstaller::~CInstaller() +{ + delete itsTempDir; +} + +} + +static K4AboutData aboutData("kfontinst", KFI_CATALOGUE, ki18n("Font Installer"), "1.0", ki18n("Simple font installer"), + K4AboutData::License_GPL, ki18n("(C) Craig Drummond, 2007")); + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("embed ", ki18n("Makes the dialog transient for an X app specified by winid")); + options.add("+[URL]", ki18n("URL to install")); + KCmdLineArgs::addCmdLineOptions(options); + + QSet urls; + KCmdLineArgs *args(KCmdLineArgs::parsedArgs()); + + for(int i=0; i < args->count(); i++) + urls.insert(args->url(i)); + + if(urls.count()) + { + + KApplication app; + QString opt(args->getOption("embed")); + KFI::CInstaller inst(createParent(opt.size() ? opt.toInt(0, 16) : 0)); + + return inst.install(urls); + } + + return -1; +} diff --git a/kcms/kfontinst/apps/Installer.h b/kcms/kfontinst/apps/Installer.h new file mode 100644 index 0000000000..f259491d69 --- /dev/null +++ b/kcms/kfontinst/apps/Installer.h @@ -0,0 +1,53 @@ +#ifndef __INSTALLER_H__ +#define __INSTALLER_H__ + +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +class QWidget; +class KTempDir; + +namespace KFI +{ +class CInstaller +{ + public: + + CInstaller(QWidget *p) + : itsParent(p), itsTempDir(NULL) { } + ~CInstaller(); + + int install(const QSet &urls); + + private: + + QWidget *itsParent; + KTempDir *itsTempDir; +}; + +} + +#endif diff --git a/kcms/kfontinst/apps/Printer.cpp b/kcms/kfontinst/apps/Printer.cpp new file mode 100644 index 0000000000..1ec6f11e6e --- /dev/null +++ b/kcms/kfontinst/apps/Printer.cpp @@ -0,0 +1,495 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config-fontinst.h" +#include "Printer.h" +#include "FcEngine.h" +#include "ActionLabel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#include +#include +#include FT_FREETYPE_H +#endif + +#ifdef HAVE_LOCALE_H +#include +#endif +#include "CreateParent.h" + +// Enable the following to allow printing of non-installed fonts. Does not seem to work :-( +//#define KFI_PRINT_APP_FONTS + +using namespace KFI; + +static const int constMarginLineBefore=1; +static const int constMarginLineAfter=2; +static const int constMarginFont=4; + +inline bool sufficientSpace(int y, int pageHeight, const QFontMetrics &fm) +{ + return (y+constMarginFont+fm.height())fontMetrics().height(), + required=titleFontHeight+constMarginLineBefore+constMarginLineAfter; + + for(unsigned int s=0; sizes[s]; ++s) + { + font.setPointSize(sizes[s]); + required+=QFontMetrics(font, painter->device()).height(); + if(sizes[s+1]) + required+=constMarginFont; + } + + if(0==size) + { + font.setPointSize(CFcEngine::constDefaultAlphaSize); + int fontHeight=QFontMetrics(font, painter->device()).height(); + + required+=(3*(constMarginFont+fontHeight))+ + constMarginLineBefore+constMarginLineAfter; + } + return (y+required)num_charmaps; ++cmap) + if(face->charmaps[cmap] && FT_ENCODING_ADOBE_CUSTOM==face->charmaps[cmap]->encoding) + { + FT_Select_Charmap(face, FT_ENCODING_ADOBE_CUSTOM); + break; + } + + for(unsigned int i=1; i<65535; ++i) + if(FT_Get_Char_Index(face, i)) + { + newStr+=QChar(i); + if(newStr.length()>255) + break; + } + + return newStr; +} + +static QString usableStr(FT_Face face, const QString &str) +{ + unsigned int slen=str.length(), + ch; + QString newStr; + + for(ch=0; ch &items, int size, QObject *parent) + : QThread(parent) + , itsPrinter(printer) + , itsItems(items) + , itsSize(size) + , itsCancelled(false) +{ +} + +CPrintThread::~CPrintThread() +{ +} + +void CPrintThread::cancel() +{ + itsCancelled=true; +} + +void CPrintThread::run() +{ + QPainter painter; + QFont sans("sans", 12, QFont::Bold); + bool changedFontEmbeddingSetting(false); + QString str(CFcEngine(false).getPreviewString()); + + if(!itsPrinter->fontEmbeddingEnabled()) + { + itsPrinter->setFontEmbeddingEnabled(true); + changedFontEmbeddingSetting=true; + } + + itsPrinter->setResolution(72); + painter.begin(itsPrinter); + + int pageWidth=painter.device()->width(), + pageHeight=painter.device()->height(), + y=0, + oneSize[2]={itsSize, 0}; + const int *sizes=oneSize; + bool firstFont(true); + + if(0==itsSize) + sizes=CFcEngine::constScalableSizes; + + painter.setClipping(true); + painter.setClipRect(0, 0, pageWidth, pageHeight); + + QList::ConstIterator it(itsItems.constBegin()), + end(itsItems.constEnd()); + + for(int i=0; it!=end && !itsCancelled; ++it, ++i) + { + QString name(FC::createName((*it).family, (*it).styleInfo)); + emit progress(i, name); + + unsigned int s=0; + QFont font; + +#ifdef KFI_PRINT_APP_FONTS + QString family; + + if(-1!=appFont[(*it).family]) + { + family=QFontDatabase::applicationFontFamilies(appFont[(*it).family]).first(); + font=QFont(family); + } +#else + font=CFcEngine::getQFont((*it).family, (*it).styleInfo, CFcEngine::constDefaultAlphaSize); +#endif + painter.setFont(sans); + + if(!firstFont && !sufficientSpace(y, &painter, font, sizes, pageHeight, itsSize)) + { + itsPrinter->newPage(); + y=0; + } + painter.setFont(sans); + y+=painter.fontMetrics().height(); + painter.drawText(0, y, name); + + y+=constMarginLineBefore; + painter.drawLine(0, y, pageWidth, y); + y+=constMarginLineAfter; + + bool onlyDrawChars=false; + Qt::TextElideMode em=Qt::LeftToRight==QApplication::layoutDirection() ? Qt::ElideRight : Qt::ElideLeft; + + if(0==itsSize) + { + font.setPointSize(CFcEngine::constDefaultAlphaSize); + painter.setFont(font); + + QFontMetrics fm(font, painter.device()); + bool lc=hasStr(font, CFcEngine::getLowercaseLetters()), + uc=hasStr(font, CFcEngine::getUppercaseLetters()); + + onlyDrawChars=!lc && !uc; + + if(lc || uc) + y+=CFcEngine::constDefaultAlphaSize; + + if(lc) + { + painter.drawText(0, y, fm.elidedText(CFcEngine::getLowercaseLetters(), em, pageWidth)); + y+=constMarginFont+CFcEngine::constDefaultAlphaSize; + } + + if(uc) + { + painter.drawText(0, y, fm.elidedText(CFcEngine::getUppercaseLetters(), em, pageWidth)); + y+=constMarginFont+CFcEngine::constDefaultAlphaSize; + } + + if(lc || uc) + { + QString validPunc(usableStr(font, CFcEngine::getPunctuation())); + if(validPunc.length()>=(CFcEngine::getPunctuation().length()/2)) + { + painter.drawText(0, y, fm.elidedText(CFcEngine::getPunctuation(), em, pageWidth)); + y+=constMarginFont+constMarginLineBefore; + } + painter.drawLine(0, y, pageWidth, y); + y+=constMarginLineAfter; + } + } + + for(; sizes[s]; ++s) + { + y+=sizes[s]; + font.setPointSize(sizes[s]); + painter.setFont(font); + + QFontMetrics fm(font, painter.device()); + + if(sufficientSpace(y, pageHeight, fm)) + { + painter.drawText(0, y, fm.elidedText(previewString(font, str, onlyDrawChars), em, pageWidth)); + if(sizes[s+1]) + y+=constMarginFont; + } + else + break; + } + y+=(s<1 || sizes[s-1]<25 ? 14 : 28); + firstFont=false; + } + emit progress(itsItems.count(), QString()); + painter.end(); + + // + // Did we change the users font settings? If so, reset to their previous values... + if(changedFontEmbeddingSetting) + itsPrinter->setFontEmbeddingEnabled(false); +} + +CPrinter::CPrinter(QWidget *parent) + : KDialog(parent) +{ + setCaption(i18n("Print")); + setButtons(Cancel); + + QFrame *page = new QFrame(this); + QGridLayout *layout=new QGridLayout(page); + layout->setMargin(KDialog::marginHint()); + layout->setSpacing(KDialog::spacingHint()); + itsStatusLabel=new QLabel(page); + itsProgress=new QProgressBar(page); + layout->addWidget(itsActionLabel = new CActionLabel(this), 0, 0, 2, 1); + layout->addWidget(itsStatusLabel, 0, 1); + layout->addWidget(itsProgress, 1, 1); + itsProgress->setRange(0, 100); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 2, 0); + setMainWidget(page); + setMinimumSize(420, 80); +} + +CPrinter::~CPrinter() +{ +} + +void CPrinter::print(const QList &items, int size) +{ + #ifdef HAVE_LOCALE_H + char *oldLocale=setlocale(LC_NUMERIC, "C"); + #endif + + QPrinter printer; + QPrintDialog *dialog = new QPrintDialog(&printer, parentWidget()); + + if(dialog->exec()) + { + CPrintThread *thread = new CPrintThread(&printer, items, size, this); + + itsProgress->setRange(0, items.count()); + itsProgress->setValue(0); + progress(0, QString()); + connect(thread, SIGNAL(progress(int,QString)), SLOT(progress(int,QString))); + connect(thread, SIGNAL(finished()), SLOT(accept())); + connect(this, SIGNAL(cancelled()), thread, SLOT(cancel())); + itsActionLabel->startAnimation(); + thread->start(); + exec(); + delete thread; + } + + delete dialog; + + #ifdef HAVE_LOCALE_H + if(oldLocale) + setlocale(LC_NUMERIC, oldLocale); + #endif +} + +void CPrinter::progress(int p, const QString &label) +{ + if(!label.isEmpty()) + itsStatusLabel->setText(label); + itsProgress->setValue(p); +} + +void CPrinter::slotButtonClicked(int button) +{ + Q_UNUSED(button) + itsStatusLabel->setText(i18n("Canceling...")); + emit cancelled(); +} + +void CPrinter::closeEvent(QCloseEvent *e) +{ + Q_UNUSED(e) + e->ignore(); + slotButtonClicked(0); +} + +static K4AboutData aboutData("kfontprint", KFI_CATALOGUE, ki18n("Font Printer"), "1.1", ki18n("Simple font printer"), + K4AboutData::License_GPL, ki18n("(C) Craig Drummond, 2007")); + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("embed ", ki18n("Makes the dialog transient for an X app specified by winid")); + options.add("size ", ki18n("Size index to print fonts")); + options.add("pfont ", ki18n("Font to print, specified as \"Family,Style\" where Style is a 24-bit decimal number composed as: ")); //krazy:exclude=i18ncheckarg + options.add("listfile ", ki18n("File containing list of fonts to print")); + options.add("deletefile", ki18n("Remove file containing list of fonts to print")); + KCmdLineArgs::addCmdLineOptions(options); + + KApplication app; + KCmdLineArgs *args(KCmdLineArgs::parsedArgs()); + QList fonts; + int size(args->getOption("size").toInt()); + + if(size>-1 && size<256) + { + QString listFile(args->getOption("listfile")); + + if(listFile.size()) + { + QFile f(listFile); + + if(f.open(QIODevice::ReadOnly)) + { + QTextStream str(&f); + + while (!str.atEnd()) + { + QString family(str.readLine()), + style(str.readLine()); + + if(!family.isEmpty() && !style.isEmpty()) + fonts.append(Misc::TFont(family, style.toUInt())); + else + break; + } + f.close(); + } + + if(args->isSet("deletefile")) + ::unlink(listFile.toLocal8Bit().constData()); + } + else + { + QStringList fl(args->getOptionList("pfont")); + QStringList::ConstIterator it(fl.begin()), + end(fl.end()); + + for(; it!=end; ++it) + { + QString f(*it); + + int commaPos=f.lastIndexOf(','); + + if(-1!=commaPos) + fonts.append(Misc::TFont(f.left(commaPos), f.mid(commaPos+1).toUInt())); + } + } + + if(fonts.count()) + { + CPrinter(createParent(args->getOption("embed").toInt(0, 16))).print(fonts, size); + + return 0; + } + } + + return -1; +} + +#include "Printer.moc" diff --git a/kcms/kfontinst/apps/Printer.h b/kcms/kfontinst/apps/Printer.h new file mode 100644 index 0000000000..146bc3e3e1 --- /dev/null +++ b/kcms/kfontinst/apps/Printer.h @@ -0,0 +1,103 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2011 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PRINTER_H__ +#define __PRINTER_H__ + +#include +#include +#include +#include +#include "Misc.h" + +class QLabel; +class QProgressBar; +class QStackedWidget; +class QPrinter; + +namespace KFI +{ + +class CActionLabel; + +class CPrintThread : public QThread +{ + Q_OBJECT + + public: + + CPrintThread(QPrinter *printer, const QList &items, int size, QObject *parent); + ~CPrintThread(); + + void run(); + + Q_SIGNALS: + + void progress(int p, const QString &f); + + public Q_SLOTS: + + void cancel(); + + private: + + QPrinter *itsPrinter; + QList itsItems; + int itsSize; + bool itsCancelled; +}; + +class CPrinter : public KDialog +{ + Q_OBJECT + + public: + + explicit CPrinter(QWidget *parent); + ~CPrinter(); + + void print(const QList &items, int size); + + Q_SIGNALS: + + void cancelled(); + + public Q_SLOTS: + + void progress(int p, const QString &label); + void slotButtonClicked(int button); + + private: + + void closeEvent(QCloseEvent *e); + + private: + + QLabel *itsStatusLabel; + QProgressBar *itsProgress; + CActionLabel *itsActionLabel; +}; + +} + +#endif diff --git a/kcms/kfontinst/apps/Viewer.cpp b/kcms/kfontinst/apps/Viewer.cpp new file mode 100644 index 0000000000..438bede3ae --- /dev/null +++ b/kcms/kfontinst/apps/Viewer.cpp @@ -0,0 +1,168 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "Viewer.h" +#include "KfiConstants.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KFI +{ + +CViewer::CViewer() +{ + KPluginFactory *factory=KPluginLoader("kfontviewpart").factory(); + + if(factory) + { + itsPreview=factory->create(this); + + actionCollection()->addAction(KStandardAction::Open, this, SLOT(fileOpen())); + actionCollection()->addAction(KStandardAction::Quit, this, SLOT(close())); + actionCollection()->addAction(KStandardAction::KeyBindings, this, SLOT(configureKeys())); + itsPrintAct=actionCollection()->addAction(KStandardAction::Print, itsPreview, SLOT(print())); + + itsPrintAct->setEnabled(false); + + if(itsPreview->browserExtension()) + connect(itsPreview->browserExtension(), SIGNAL(enableAction(const char*,bool)), + this, SLOT(enableAction(const char*,bool))); + + setCentralWidget(itsPreview->widget()); + createGUI(itsPreview); + + setAutoSaveSettings(); + applyMainWindowSettings(KGlobal::config()->group("MainWindow")); + } + else + exit(0); +} + +void CViewer::fileOpen() +{ + KUrl url(KFileDialog::getOpenUrl(KUrl(), "application/x-font-ttf application/x-font-otf " + "application/x-font-type1 " + "application/x-font-bdf application/x-font-pcf ", + this, i18n("Select Font to View"))); + showUrl(url); +} + +void CViewer::showUrl(const KUrl &url) +{ + if(url.isValid()) + itsPreview->openUrl(url); +} + +void CViewer::configureKeys() +{ + KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); + + dlg.addCollection(actionCollection()); + dlg.configure(); +} + +void CViewer::enableAction(const char *name, bool enable) +{ + if(0==qstrcmp("print", name)) + itsPrintAct->setEnabled(enable); +} + + + + +class ViewerApplication : public KUniqueApplication +{ + public: + +#ifdef Q_WS_X11 + ViewerApplication(Display *display, Qt::HANDLE visual, Qt::HANDLE colormap) + : KUniqueApplication(display,visual,colormap) + { + } +#endif + + ViewerApplication() : KUniqueApplication() + { + } + + int newInstance() + { + KCmdLineArgs *args(KCmdLineArgs::parsedArgs()); + KFI::CViewer *viewer=new KFI::CViewer; + + viewer->show(); + if(args->count() > 0) + { + for (int i = 0; i < args->count(); ++i) + { + KUrl url(args->url(i)); + + if (i != 0) + { + viewer=new KFI::CViewer; + viewer->show(); + } + viewer->showUrl(url); + } + } + + return 0; + } +}; + +} + +static K4AboutData aboutData("kfontview", KFI_CATALOGUE, ki18n("Font Viewer"), "1.1", ki18n("Simple font viewer"), + K4AboutData::License_GPL, ki18n("(C) Craig Drummond, 2004-2007")); + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addTempFileOption(); + + KCmdLineOptions options; + options.add("+[URL]", ki18n("URL to open")); + KCmdLineArgs::addCmdLineOptions(options); + + if (!KUniqueApplication::start()) + exit(0); + + KFI::ViewerApplication app; + + return app.exec(); +} + +#include "Viewer.moc" diff --git a/kcms/kfontinst/apps/Viewer.h b/kcms/kfontinst/apps/Viewer.h new file mode 100644 index 0000000000..4207431d54 --- /dev/null +++ b/kcms/kfontinst/apps/Viewer.h @@ -0,0 +1,60 @@ +#ifndef __VIEWER_H__ +#define __VIEWER_H__ + +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2007 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +class QAction; +class KUrl; + +namespace KFI +{ + +class CViewer : public KParts::MainWindow +{ + Q_OBJECT + + public: + + CViewer(); + virtual ~CViewer() { } + void showUrl(const KUrl &url); + + public Q_SLOTS: + + void fileOpen(); + void configureKeys(); + void enableAction(const char *name, bool enable); + + private: + + KParts::ReadOnlyPart *itsPreview; + QAction *itsPrintAct; +}; + +} + +#endif diff --git a/kcms/kfontinst/apps/installfont.desktop b/kcms/kfontinst/apps/installfont.desktop new file mode 100644 index 0000000000..bf18749098 --- /dev/null +++ b/kcms/kfontinst/apps/installfont.desktop @@ -0,0 +1,96 @@ +[Desktop Entry] +X-KDE-ServiceTypes=KonqPopupMenu/Plugin,application/x-font-ttf,application/x-font-type1,application/x-font-bdf,application/x-font-pcf,application/x-font-otf,application/x-font-afm,fonts/package +Actions=installFont; +Type=Service + +[Desktop Action installFont] +Name=Install... +Name[af]=Installeer... +Name[ar]=ثبّت... +Name[as]=সংস্থাপন কৰক... +Name[ast]=Instalar... +Name[be]=Устанавіць... +Name[be@latin]=Zainstaluj... +Name[bg]=Инсталиране... +Name[bn]=ইনস্টল... +Name[bn_IN]=ইনস্টল করুন... +Name[bs]=Instaliraj... +Name[ca]=Instal·la... +Name[ca@valencia]=Instal·la... +Name[cs]=Instalovat... +Name[csb]=Instalëjë... +Name[da]=Installér... +Name[de]=Installieren ... +Name[el]=Εγκατάσταση... +Name[en_GB]=Install... +Name[eo]=Instali... +Name[es]=Instalar... +Name[et]=Paigalda... +Name[eu]=Instalatu... +Name[fa]=نصب... +Name[fi]=Asenna… +Name[fr]=Installer... +Name[fy]=Ynstallearje... +Name[ga]=Suiteáil... +Name[gl]=Instalar… +Name[gu]=સ્થાપન... +Name[he]=התקנה... +Name[hi]=संस्थापित करें... +Name[hne]=इंस्टाल करव... +Name[hr]=Instaliraj… +Name[hsb]=instalować... +Name[hu]=Telepítés... +Name[ia]=Installa ... +Name[id]=Instal... +Name[is]=Setja upp... +Name[it]=Installa... +Name[ja]=インストール... +Name[kk]=Орнату... +Name[km]=ដំឡើង... +Name[kn]=ಅನುಸ್ಢಾಪಿಸು... +Name[ko]=설치... +Name[ku]=Sazkirin... +Name[lt]=Įdiegti... +Name[lv]=Instalēt... +Name[mai]=संस्थापित करू... +Name[mk]=Инсталирање... +Name[ml]=ഇന്‍‌സ്റ്റോള്‍ ചെയ്യുക... +Name[mr]=प्रतिष्ठापन... +Name[nb]=Installer … +Name[nds]=Installeren... +Name[ne]=स्थापना गर्नुहोस् +Name[nl]=Installeren... +Name[nn]=Installer … +Name[oc]=Installar... +Name[or]=ସ୍ଥାପନ କରନ୍ତୁ... +Name[pa]=ਇੰਸਟਾਲ... +Name[pl]=Instaluj... +Name[pt]=Instalar... +Name[pt_BR]=Instalar... +Name[ro]=Instalare... +Name[ru]=Установить... +Name[se]=Sajáiduhte … +Name[si]=ස්ථාපනය... +Name[sk]=Inštalovať... +Name[sl]=Namesti ... +Name[sr]=Инсталирај... +Name[sr@ijekavian]=Инсталирај... +Name[sr@ijekavianlatin]=Instaliraj... +Name[sr@latin]=Instaliraj... +Name[sv]=Installera... +Name[ta]=நிறுவுக... +Name[te]=సంస్థాపించు... +Name[tg]=Коргузорӣ... +Name[th]=ติดตั้ง... +Name[tr]=Yükle... +Name[ug]=ئورنات… +Name[uk]=Встановити... +Name[uz]=Oʻrnatish... +Name[uz@cyrillic]=Ўрнатиш... +Name[vi]=Cài đặt... +Name[wa]=Astaler... +Name[x-test]=xxInstall...xx +Name[zh_CN]=安装... +Name[zh_TW]=安裝... +Icon=preferences-desktop-font-installer +Exec=kfontinst --icon preferences-desktop-font-installer %U diff --git a/kcms/kfontinst/apps/kfontview.desktop b/kcms/kfontinst/apps/kfontview.desktop new file mode 100755 index 0000000000..141d247081 --- /dev/null +++ b/kcms/kfontinst/apps/kfontview.desktop @@ -0,0 +1,192 @@ +[Desktop Entry] +Name=KFontView +Name[af]=KFontView +Name[ar]=عارض خطوط كيدي +Name[ast]=KFontView +Name[be]=Прагляд шрыфтоў +Name[be@latin]=KFontView +Name[bg]=KFontView +Name[bn]=কে-ফন্ট-ভিউ +Name[bn_IN]=KFontView +Name[br]=KFontView +Name[bs]=K‑prikazivač-fontova +Name[ca]=KFontView +Name[ca@valencia]=KFontView +Name[cs]=Prohlížeč písem +Name[csb]=KFontView +Name[cy]=KFontView +Name[da]=KFontView +Name[de]=KFontView +Name[el]=KFontView +Name[en_GB]=KFontView +Name[eo]=KFontView +Name[es]=KFontView +Name[et]=KFontView +Name[eu]=KFontWiew +Name[fi]=KFontView +Name[fr]=KFontView +Name[fy]=KFontView +Name[ga]=KFontView +Name[gl]=KFontView +Name[gu]=KFontView +Name[he]=KFontView +Name[hi]=के-फ़ॉन्ट-व्यू +Name[hne]=के-फोंट-व्यू +Name[hr]=KFontView +Name[hsb]=KFontView +Name[hu]=KFontView +Name[ia]=KFontView +Name[id]=KFontView +Name[is]=KFontView +Name[it]=KFontView +Name[ja]=KFontView +Name[ka]=KFontView +Name[kk]=KFontView +Name[km]=KFontView +Name[kn]=ಕೆಫಾಂಟ್ ವ್ಯೂ +Name[ko]=KFontView +Name[ku]=KFontView +Name[lt]=KFontView +Name[lv]=KFontView +Name[mai]=के-फान्ट-व्यू +Name[mk]=КФонтПреглед +Name[ml]=കെഫോണ്ട്‌വ്യൂ +Name[mr]=केफॉन्टव्यू +Name[ms]=KFontView +Name[nb]=KFontView +Name[nds]=KFontView +Name[ne]=केडीई फन्ट दृश्य +Name[nl]=KFontView +Name[nn]=Skriftvisar +Name[oc]=KFontView +Name[or]=KFontView +Name[pa]=KFontView +Name[pl]=KFontView +Name[pt]=KFontView +Name[pt_BR]=KFontView +Name[ro]=KFontView +Name[ru]=KFontView +Name[se]=Fontačájeheaddji +Name[si]=KFontView +Name[sk]=KFontView +Name[sl]=KFontView +Name[sr]=К‑приказивач-фонтова +Name[sr@ijekavian]=К‑приказивач-фонтова +Name[sr@ijekavianlatin]=K‑prikazivač-fontova +Name[sr@latin]=K‑prikazivač-fontova +Name[sv]=Kfontview +Name[ta]=KFontView +Name[te]=KFontView +Name[tg]=Намоиши ҳарф +Name[th]=ดูแบบอักษร-K +Name[tr]=KFontView +Name[ug]=KFontView +Name[uk]=KFontView +Name[uz]=KFontView +Name[uz@cyrillic]=KFontView +Name[vi]=KFonView +Name[wa]=Håyneu d' fontes +Name[x-test]=xxKFontViewxx +Name[zh_CN]=KFontView +Name[zh_TW]=KFontView +Exec=kfontview %U +Icon=kfontview +X-KDE-StartupNotify=true +Type=Application +MimeType=application/x-font-ttf;application/x-font-type1;application/x-font-otf;application/x-font-pcf;application/x-font-bdf;application/vnd.kde.fontspackage; +GenericName=Font Viewer +GenericName[af]=Skriftipe Besigter +GenericName[ar]=عارض الخطوط +GenericName[ast]=Visor del triba de lletra +GenericName[be]=Прагляд шрыфта +GenericName[be@latin]=Prahladnik šryftoŭ +GenericName[bg]=Преглед на шрифтове +GenericName[bn]=ফন্ট প্রদর্শক +GenericName[bn_IN]=ফন্ট প্রদর্শক +GenericName[br]=Gweler Nodrezhoù +GenericName[bs]=Prikazivač fontova +GenericName[ca]=Visor de tipus de lletra +GenericName[ca@valencia]=Visor de tipus de lletra +GenericName[cs]=Prohlížeč písem +GenericName[csb]=Przezérnik fòntów +GenericName[cy]=Gwelydd Wynebfathau +GenericName[da]=Skrifttype-fremviser +GenericName[de]=Schriftartenbetrachter +GenericName[el]=Προβολέας γραμματοσειρών +GenericName[en_GB]=Font Viewer +GenericName[eo]=Tipara rigardilo +GenericName[es]=Visor del tipo de letra +GenericName[et]=Fontide näitaja +GenericName[eu]=Letra-tipoen ikustailea +GenericName[fa]=مشاهده‌گر قلم +GenericName[fi]=Kirjasinkatselin +GenericName[fr]=Afficheur de polices de caractères +GenericName[fy]=Lettertypewerjefte +GenericName[ga]=Amharcán Clónna +GenericName[gl]=Visor de tipos de letra +GenericName[gu]=ફોન્ટ દર્શક +GenericName[he]=מציג גופנים +GenericName[hi]=फ़ॉन्ट प्रदर्शक +GenericName[hne]=फोंट प्रदर्सक +GenericName[hr]=Preglednik fontova +GenericName[hsb]=Přehladowar za pisma +GenericName[hu]=Betűtípusböngésző +GenericName[ia]=Visor de fronte +GenericName[id]=Penampil Fonta +GenericName[is]=Leturskoðari +GenericName[it]=Visore di caratteri +GenericName[ja]=フォントビューア +GenericName[ka]=პროგრამა ფონტების სანახავად +GenericName[kk]=Қаріпті қарау құралы +GenericName[km]=កម្មវិធី​មើល​ពុម្ពអក្សរ +GenericName[kn]=ಅಕ್ಷರಶೈಲಿ ವೀಕ್ಷಕ +GenericName[ko]=글꼴 뷰어 +GenericName[ku]=Nîşandêra Curetîpan +GenericName[lt]=Šriftų žiūryklė +GenericName[lv]=Fontu skatītājs +GenericName[mai]=फान्ट प्रदर्शक +GenericName[mk]=Прегледувач на фонтови +GenericName[ml]=അക്ഷരസഞ്ചയദര്‍ശിനി +GenericName[mr]=फॉन्ट प्रदर्शक +GenericName[ms]=Pemapar Fon +GenericName[nb]=Skrifttypeviser +GenericName[nds]=Schriftoortkieker +GenericName[ne]=फन्ट दर्शक +GenericName[nl]=Lettertypeweergave +GenericName[nn]=Skriftvisar +GenericName[or]=ଅକ୍ଷରରୂପ ପ୍ରଦର୍ଶକ +GenericName[pa]=ਫੋਂਟ ਦਰਸ਼ਕ +GenericName[pl]=Przeglądarka czcionek +GenericName[pt]=Visualizador do Tipo de Letra +GenericName[pt_BR]=Visualizador de fontes +GenericName[ro]=Vizualizator de fonturi +GenericName[ru]=Просмотр шрифтов +GenericName[se]=Fontačájeheaddji +GenericName[si]=අකුරු දසුන +GenericName[sk]=Prehliadač písiem +GenericName[sl]=Pregledovalnik pisav +GenericName[sr]=Приказивач фонтова +GenericName[sr@ijekavian]=Приказивач фонтова +GenericName[sr@ijekavianlatin]=Prikazivač fontova +GenericName[sr@latin]=Prikazivač fontova +GenericName[sv]=Teckensnittsvisning +GenericName[ta]=Font Viewer +GenericName[te]=ఫాంట్ వీక్షణి +GenericName[tg]=Намоишгари ҳарфҳо +GenericName[th]=ดูตัวอย่างแบบอักษร +GenericName[tr]=Yazı Tipi Görüntüleyici +GenericName[ug]=خەت نۇسخا كۆرگۈ +GenericName[uk]=Переглядач шрифтів +GenericName[uz]=Shrift koʻruvchi +GenericName[uz@cyrillic]=Шрифт кўрувчи +GenericName[vi]=Trình xem phông chữ +GenericName[wa]=Håyneu d' fontes +GenericName[x-test]=xxFont Viewerxx +GenericName[zh_CN]=字体查看器 +GenericName[zh_TW]=字型檢視器 +Terminal=false +InitialPreference=1 +NoDisplay=true +Categories=Qt;KDE;Utility; +X-DBUS-StartupType=Unique +X-KDE-HasTempFileOption=true diff --git a/kcms/kfontinst/apps/kfontviewui.rc b/kcms/kfontinst/apps/kfontviewui.rc new file mode 100644 index 0000000000..f2e4a86fa8 --- /dev/null +++ b/kcms/kfontinst/apps/kfontviewui.rc @@ -0,0 +1,4 @@ + + + + diff --git a/kcms/kfontinst/apps/ox16-app-kfontview.png b/kcms/kfontinst/apps/ox16-app-kfontview.png new file mode 100644 index 0000000000..3691f9068b Binary files /dev/null and b/kcms/kfontinst/apps/ox16-app-kfontview.png differ diff --git a/kcms/kfontinst/apps/ox22-app-kfontview.png b/kcms/kfontinst/apps/ox22-app-kfontview.png new file mode 100644 index 0000000000..5a24803e30 Binary files /dev/null and b/kcms/kfontinst/apps/ox22-app-kfontview.png differ diff --git a/kcms/kfontinst/apps/ox32-app-kfontview.png b/kcms/kfontinst/apps/ox32-app-kfontview.png new file mode 100644 index 0000000000..c3063f15b9 Binary files /dev/null and b/kcms/kfontinst/apps/ox32-app-kfontview.png differ diff --git a/kcms/kfontinst/apps/ox48-app-kfontview.png b/kcms/kfontinst/apps/ox48-app-kfontview.png new file mode 100644 index 0000000000..0a34070750 Binary files /dev/null and b/kcms/kfontinst/apps/ox48-app-kfontview.png differ diff --git a/kcms/kfontinst/apps/ox64-app-kfontview.png b/kcms/kfontinst/apps/ox64-app-kfontview.png new file mode 100644 index 0000000000..3890299010 Binary files /dev/null and b/kcms/kfontinst/apps/ox64-app-kfontview.png differ diff --git a/kcms/kfontinst/apps/oxsc-app-kfontview.svgz b/kcms/kfontinst/apps/oxsc-app-kfontview.svgz new file mode 100644 index 0000000000..6a6a4f8d6b Binary files /dev/null and b/kcms/kfontinst/apps/oxsc-app-kfontview.svgz differ diff --git a/kcms/kfontinst/config-fontinst.h.cmake b/kcms/kfontinst/config-fontinst.h.cmake new file mode 100644 index 0000000000..98966d3bc9 --- /dev/null +++ b/kcms/kfontinst/config-fontinst.h.cmake @@ -0,0 +1,9 @@ +#ifndef __CONFIG_FONTINST_H__ +#define __CONFIG_FONTINST_H__ + +#define KFONTINST_LIB_EXEC_DIR "@LIBEXEC_INSTALL_DIR@" + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +#endif diff --git a/kcms/kfontinst/dbus/CMakeLists.txt b/kcms/kfontinst/dbus/CMakeLists.txt new file mode 100644 index 0000000000..5e203eb7b7 --- /dev/null +++ b/kcms/kfontinst/dbus/CMakeLists.txt @@ -0,0 +1,31 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR} + ${kcms_SOURCE_DIR}/fonts) +add_definitions(${QT_DEFINITIONS}) + +set(fontinst_bin_SRCS FcConfig.cpp FontInst.cpp Folder.cpp Main.cpp Utils.cpp ${libkfontinstdbusiface_SRCS} ) +set(fontinst_helper_SRCS FcConfig.cpp Helper.cpp Folder.cpp Utils.cpp ${libkfontinstdbusiface_SRCS} ) + +# qt5_generate_dbus_interface(FontInst.h org.kde.fontinst.xml) +qt5_add_dbus_adaptor(fontinst_bin_SRCS org.kde.fontinst.xml FontInst.h KFI::FontInst) +# qt5_add_dbus_interface(fontinst_bin_SRCS org.kde.fontinst.xml FontinstIface) + +add_executable(fontinst_bin ${fontinst_bin_SRCS}) +add_executable(fontinst_helper ${fontinst_helper_SRCS}) + +set_target_properties(fontinst_bin PROPERTIES OUTPUT_NAME fontinst) +target_link_libraries(fontinst_bin + Qt5::DBus Qt5::Xml ${FONTCONFIG_LIBRARIES} kfontinst) + +set_target_properties(fontinst_helper PROPERTIES OUTPUT_NAME fontinst_helper) +target_link_libraries(fontinst_helper + Qt5::DBus Qt5::Xml ${FONTCONFIG_LIBRARIES} kfontinst) + +install(TARGETS fontinst_bin DESTINATION ${LIBEXEC_INSTALL_DIR} ) +install(TARGETS fontinst_helper DESTINATION ${LIBEXEC_INSTALL_DIR} ) +install(PROGRAMS fontinst_x11 DESTINATION ${LIBEXEC_INSTALL_DIR}) + +configure_file(org.kde.fontinst.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/session/org.kde.fontinst.service) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/session/org.kde.fontinst.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR} ) + +kauth_install_helper_files(fontinst_helper org.kde.fontinst root) +kauth_install_actions(org.kde.fontinst fontinst.actions) diff --git a/kcms/kfontinst/dbus/FcConfig.cpp b/kcms/kfontinst/dbus/FcConfig.cpp new file mode 100644 index 0000000000..2213a5ef26 --- /dev/null +++ b/kcms/kfontinst/dbus/FcConfig.cpp @@ -0,0 +1,194 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2009 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "FcConfig.h" +#include "Misc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KFI_DBUG kDebug() << time(0L) + +namespace KFI +{ + +namespace FcConfig +{ + +inline QString xDirSyntax(const QString &d) { return Misc::fileSyntax(d); } + +// +// Obtain location of config file to use. +// +// For system, prefer the following: +// +// <...>/config.d/00kde.conf = preferred method from FCConfig >= 2.3 +// <...>/local.conf +// +// Non-system, prefer: +// +// $HOME/<...>/.fonts.conf +// $HOME/<...>/fonts.conf +// +QString getConfigFile(bool system) +{ +#if (FC_VERSION>=20300) + static const char constKdeRootFcFile[] = "00kde.conf"; +#endif + + FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent()); + QStringList files; + FcChar8 *file; + QString home(Misc::dirSyntax(QDir::homePath())); + + while((file=FcStrListNext(list))) + { + QString f((const char *)file); + + if(Misc::fExists(f)) + { + // For nonsystem, only consider file within $HOME + if(system || 0==Misc::fileSyntax(f).indexOf(home)) + files.append(f); + } +#if (FC_VERSION>=20300) + if(system && Misc::dExists(f) && (f.contains(QRegExp("/conf\\.d/?$")) || + f.contains(QRegExp("/conf\\.d?$"))) ) + return Misc::dirSyntax(f)+constKdeRootFcFile; // This ones good enough for me! +#endif + } + + // + // Go through list of files, looking for the preferred one... + if(files.count()) + { + QStringList::const_iterator it(files.begin()), + end(files.end()); + + for(; it!=end; ++it) + if(-1!=(*it).indexOf(QRegExp(system ? "/local\\.conf$" : "/\\.?fonts\\.conf$"))) + return *it; + return files.front(); // Just return the 1st one... + } + else // Hmmm... no known files? + return system ? "/etc/fonts/local.conf" : Misc::fileSyntax(home+"/.fonts.conf"); +} + +void addDir(const QString &dir, bool system) +{ + QDomDocument doc("fontconfig"); + QString fileName=getConfigFile(system); + QFile f(fileName); + bool hasDir(false); + + KFI_DBUG << "Using fontconfig file:" << fileName; + + // Load existing file - and check to see whether it has the dir... + if(f.open(QIODevice::ReadOnly)) + { + doc.clear(); + + if(doc.setContent(&f)) + { + QDomNode n = doc.documentElement().firstChild(); + + while(!n.isNull() && !hasDir) + { + QDomElement e = n.toElement(); + + if(!e.isNull() && "dir"==e.tagName()) + if(0==Misc::expandHome(Misc::dirSyntax(e.text())).indexOf(dir)) + hasDir=true; + n=n.nextSibling(); + } + } + f.close(); + } + + // Add dir, and save, if config does not already have this dir. + if(!hasDir) + { + if(doc.documentElement().isNull()) + doc.appendChild(doc.createElement("fontconfig")); + + QDomElement newNode = doc.createElement("dir"); + QDomText text = doc.createTextNode(Misc::contractHome(xDirSyntax(dir))); + + newNode.appendChild(text); + doc.documentElement().appendChild(newNode); + + FcAtomic *atomic=FcAtomicCreate((const unsigned char *)(QFile::encodeName(fileName).data())); + + if(atomic) + { + if(FcAtomicLock(atomic)) + { + FILE *f=fopen((char *)FcAtomicNewFile(atomic), "w"); + + if(f) + { + // + // Check document syntax... + static const char qtXmlHeader[] = ""; + static const char xmlHeader[] = ""; + static const char qtDocTypeLine[] = ""; + static const char docTypeLine[] = ""; + + QString str(doc.toString()); + int idx; + + if(0!=str.indexOf(" + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +class QString; + +namespace KFI +{ + +namespace FcConfig +{ + void addDir(const QString &dir, bool system); +} + +} + +#endif diff --git a/kcms/kfontinst/dbus/Folder.cpp b/kcms/kfontinst/dbus/Folder.cpp new file mode 100644 index 0000000000..ba9b45f9ef --- /dev/null +++ b/kcms/kfontinst/dbus/Folder.cpp @@ -0,0 +1,418 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2009 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Folder.h" +#include "FcConfig.h" +#include "Misc.h" +#include "KfiConstants.h" +#include "XmlStrings.h" +#include "Style.h" +#include "File.h" +#include "config-fontinst.h" + +#define DISABLED_FONTS "disabledfonts" +#define KFI_DBUG kDebug() << time(0L) + +namespace KFI +{ + +bool Folder::CfgFile::modified() +{ + return timestamp!=Misc::getTimeStamp(name); +} + +void Folder::CfgFile::updateTimeStamp() +{ + timestamp=Misc::getTimeStamp(name); +} + +Folder::~Folder() +{ + saveDisabled(); +} + +void Folder::init(bool system, bool systemBus) +{ + itsIsSystem=system; + if(!system) + { + FcStrList *list=FcConfigGetFontDirs(FcInitLoadConfigAndFonts()); + QStringList dirs; + FcChar8 *fcDir; + + while((fcDir=FcStrListNext(list))) + dirs.append(Misc::dirSyntax((const char *)fcDir)); + + itsLocation=Misc::getFolder(Misc::dirSyntax(QDir::homePath()+"/.fonts/"), Misc::dirSyntax(QDir::homePath()), dirs); + } + else + itsLocation=KFI_DEFAULT_SYS_FONTS_FOLDER; + + if((!system && !systemBus) || (system && systemBus)) + FcConfig::addDir(itsLocation, system); + + itsDisabledCfg.dirty=false; + if(itsDisabledCfg.name.isEmpty()) + { + QString fileName("/"DISABLED_FONTS".xml"); + + if(system) + itsDisabledCfg.name=QString::fromLatin1(KFI_ROOT_CFG_DIR)+fileName; + else + { + QString path=KGlobal::dirs()->localxdgconfdir(); + + if(!Misc::dExists(path)) + Misc::createDir(path); + itsDisabledCfg.name=path+fileName; + } + itsDisabledCfg.timestamp=0; + } +} + +bool Folder::allowToggling() const +{ + return Misc::fExists(itsDisabledCfg.name) + ? Misc::fWritable(itsDisabledCfg.name) + : Misc::dWritable(Misc::getDir(itsDisabledCfg.name)); +} + +void Folder::loadDisabled() +{ + if(itsDisabledCfg.dirty) + saveDisabled(); + + QFile f(itsDisabledCfg.name); + + KFI_DBUG << itsDisabledCfg.name; + itsDisabledCfg.dirty=false; + if(f.open(QIODevice::ReadOnly)) + { + QDomDocument doc; + + if(doc.setContent(&f)) + for(QDomNode n=doc.documentElement().firstChild(); !n.isNull(); n=n.nextSibling()) + { + QDomElement e=n.toElement(); + + if(FONT_TAG==e.tagName()) + { + Family fam(e, false); + + if(!fam.name().isEmpty()) + { + Style style(e, false); + + if(KFI_NO_STYLE_INFO!=style.value()) + { + QList files; + + if(e.hasAttribute(PATH_ATTR)) + { + File file(e, true); + + if(!file.path().isEmpty()) + files.append(file); + else + itsDisabledCfg.dirty=true; + } + else + { + for(QDomNode n=e.firstChild(); !n.isNull(); n=n.nextSibling()) + { + QDomElement ent=n.toElement(); + + if(FILE_TAG==ent.tagName()) + { + File file(ent, true); + + if(!file.path().isEmpty()) + files.append(file); + else + { + KFI_DBUG << "Set dirty from load"; + itsDisabledCfg.dirty=true; + } + } + } + } + + if(files.count()>0) + { + QList::ConstIterator it(files.begin()), + end(files.end()); + + FamilyCont::ConstIterator f(itsFonts.insert(fam)); + StyleCont::ConstIterator s((*f).add(style)); + + for(; it!=end; ++it) + (*s).add(*it); + } + } + } + } + } + + f.close(); + itsDisabledCfg.updateTimeStamp(); + } + + saveDisabled(); +} + +void Folder::saveDisabled() +{ + if(itsDisabledCfg.dirty) + { + if(!itsIsSystem || Misc::root()) + { + KFI_DBUG << itsDisabledCfg.name; + + KSaveFile file; + + file.setFileName(itsDisabledCfg.name); + + if(!file.open()) + { + KFI_DBUG << "Exit - cant open save file"; + qApp->exit(0); + } + + QTextStream str(&file); + + str << "<"DISABLED_FONTS">" << endl; + + FamilyCont::ConstIterator it(itsFonts.begin()), + end(itsFonts.end()); + + for(; it!=end; ++it) + (*it).toXml(true, str); + str << "" << endl; + str.flush(); + + if(!file.finalize()) + { + KFI_DBUG << "Exit - cant finalize save file"; + qApp->exit(0); + } + } + itsDisabledCfg.updateTimeStamp(); + itsDisabledCfg.dirty=false; + } +} + +QStringList Folder::toXml(int max) +{ + QStringList rv; + FamilyCont::ConstIterator it(itsFonts.begin()), + end(itsFonts.end()); + QString string; + QTextStream str(&string); + + for(int i=0; it!=end; ++it, ++i) + { + if(0==(i%max)) + { + if(i) + { + str << "" << endl; + rv.append(string); + string=QString(); + } + str << "<"FONTLIST_TAG" " << SYSTEM_ATTR"=\"" << (itsIsSystem ? "true" : "false") << "\">" << endl; + } + + (*it).toXml(false, str); + } + + if(!string.isEmpty()) + { + str << "" << endl; + rv.append(string); + } + return rv; +} + +Families Folder::list() +{ + Families fam(itsIsSystem); + FamilyCont::ConstIterator it(itsFonts.begin()), + end(itsFonts.end()); + + for(int i=0; it!=end; ++it, ++i) + fam.items.insert(*it); + + return fam; +} + +bool Folder::contains(const QString &family, quint32 style) +{ + FamilyCont::ConstIterator fam=itsFonts.find(Family(family)); + + if(fam==itsFonts.end()) + return false; + + StyleCont::ConstIterator st=(*fam).styles().find(Style(style)); + + return st!=(*fam).styles().end(); +} + +void Folder::add(const Family &family) +{ + FamilyCont::ConstIterator existingFamily=itsFonts.find(family); + + if(existingFamily==itsFonts.end()) + itsFonts.insert(family); + else + { + StyleCont::ConstIterator it(family.styles().begin()), + end(family.styles().end()); + + for(; it!=end; ++it) + { + StyleCont::ConstIterator existingStyle=(*existingFamily).styles().find(*it); + + if(existingStyle==(*existingFamily).styles().end()) + (*existingFamily).add(*it); + else + { + FileCont::ConstIterator fit((*it).files().begin()), + fend((*it).files().end()); + + for(; fit!=fend; ++fit) + { + FileCont::ConstIterator f=(*existingStyle).files().find(*fit); + + if(f==(*existingStyle).files().end()) + (*existingStyle).add(*fit); + } + + (*existingStyle).setWritingSystems((*existingStyle).writingSystems()|(*it).writingSystems()); + if(!(*existingStyle).scalable() && (*it).scalable()) + (*existingStyle).setScalable(true); + } + } + } +} + +void Folder::configure(bool force) +{ +KFI_DBUG << "EMPTY MODIFIED " << itsModifiedDirs.isEmpty(); + + if(force || !itsModifiedDirs.isEmpty()) + { + saveDisabled(); + + QSet::ConstIterator it(itsModifiedDirs.constBegin()), + end(itsModifiedDirs.constEnd()); + QSet dirs; + + for(; it!=end; ++it) + if(Misc::fExists((*it)+"fonts.dir")) + dirs.insert(KShell::quoteArg(*it)); + + if(!dirs.isEmpty()) + QProcess::startDetached(QLatin1String(KFONTINST_LIB_EXEC_DIR"/fontinst_x11"), dirs.toList()); + + itsModifiedDirs.clear(); + +KFI_DBUG << "RUN FC"; + Misc::doCmd("fc-cache"); +KFI_DBUG << "DONE"; + } +} + +Folder::Flat Folder::flatten() const +{ + FamilyCont::ConstIterator fam=itsFonts.begin(), + famEnd=itsFonts.end(); + Flat rv; + + for(; fam!=famEnd; ++fam) + { + StyleCont::ConstIterator style((*fam).styles().begin()), + styleEnd((*fam).styles().end()); + + for(; style!=styleEnd; ++style) + { + FileCont::ConstIterator file((*style).files().begin()), + fileEnd((*style).files().end()); + + for(; file!=fileEnd; ++file) + rv.insert(FlatFont(*fam, *style, *file)); + } + } + + return rv; +} + +Families Folder::Flat::build(bool system) const +{ + ConstIterator it(begin()), + e(end()); + Families families(system); + + for(; it!=e; ++it) + { + Family f((*it).family); + Style s((*it).styleInfo, (*it).scalable, (*it).writingSystems); + FamilyCont::ConstIterator fam=families.items.constFind(f); + + if(families.items.constEnd()==fam) + { + s.add((*it).file); + f.add(s); + families.items.insert(f); + } + else + { + StyleCont::ConstIterator st=(*fam).styles().constFind(s); + + if((*fam).styles().constEnd()==st) + { + s.add((*it).file); + (*fam).add(s); + } + else + (*st).add((*it).file); + } + } + + return families; +} + +} diff --git a/kcms/kfontinst/dbus/Folder.h b/kcms/kfontinst/dbus/Folder.h new file mode 100644 index 0000000000..5c07ed08ec --- /dev/null +++ b/kcms/kfontinst/dbus/Folder.h @@ -0,0 +1,114 @@ +#ifndef FOLDER_H +#define FOLDER_H + +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2009 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include "Family.h" +#include "Style.h" +#include "File.h" +#include "Misc.h" + +namespace KFI +{ + +class Folder +{ + struct CfgFile + { + bool modified(); + void updateTimeStamp(); + + bool dirty; + QString name; + time_t timestamp; + }; + + public: + + struct FlatFont : Misc::TFont + { + FlatFont(const Family &fam, const Style &style, const File &f) + : Misc::TFont(fam.name(), style.value()), + writingSystems(style.writingSystems()), + scalable(style.scalable()), + file(f) + { + } + bool operator==(const FlatFont &o) const { return file.path()==o.file.path(); } + + qulonglong writingSystems; + bool scalable; + File file; + }; + + struct Flat : public QSet + { + Families build(bool system) const; + }; + + Folder() { } + ~Folder(); + + void init(bool system, bool systemBus); + const QString & location() const { return itsLocation; } + bool allowToggling() const; + void loadDisabled(); + void saveDisabled(); + void setDisabledDirty() { itsDisabledCfg.dirty=true; } + bool disabledDirty() const { return itsDisabledCfg.dirty; } + QStringList toXml(int max=0); + Families list(); + bool contains(const QString &family, quint32 style); + void add(const Family &family); + void addModifiedDir(const QString &dir) { itsModifiedDirs.insert(dir); } + void addModifiedDirs(const QSet &dirs) { itsModifiedDirs+=dirs; } + bool isModified() const { return !itsModifiedDirs.isEmpty(); } + void clearModified() { itsModifiedDirs.clear(); } + void configure(bool force=false); + Flat flatten() const; + const FamilyCont & fonts() const { return itsFonts; } + FamilyCont::ConstIterator addFont(const Family &fam) { return itsFonts.insert(fam); } + void removeFont(const Family &fam) { itsFonts.remove(fam); } + void clearFonts() { itsFonts.clear(); } + + private: + + bool itsIsSystem; + FamilyCont itsFonts; + CfgFile itsDisabledCfg; + QString itsLocation; + QSet itsModifiedDirs; +}; + +inline Q_DECL_EXPORT uint qHash(const Folder::FlatFont &key) +{ + return qHash(key.file); // +qHash(key.index()); +} + +} + +#endif diff --git a/kcms/kfontinst/dbus/FontInst.cpp b/kcms/kfontinst/dbus/FontInst.cpp new file mode 100644 index 0000000000..37d5adebc8 --- /dev/null +++ b/kcms/kfontinst/dbus/FontInst.cpp @@ -0,0 +1,971 @@ +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2009 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FontInst.h" +#include "fontinstadaptor.h" +#include "Misc.h" +#include "Fc.h" +#include "WritingSystems.h" +#include "Utils.h" +#include "FontinstIface.h" + +#define KFI_DBUG kDebug() << time(0L) + +namespace KFI +{ + +static void decompose(const QString &name, QString &family, QString &style) +{ + int commaPos=name.lastIndexOf(','); + + family=-1==commaPos ? name : name.left(commaPos); + style=-1==commaPos ? KFI_WEIGHT_REGULAR : name.mid(commaPos+2); +} + +static bool isSystem=false; +static Folder theFolders[FontInst::FOLDER_COUNT]; +static const int constSystemReconfigured=-1; +static const int constConnectionsTimeout = 30 * 1000; +static const int constFontListTimeout = 10 * 1000; + +typedef void (*SignalHandler)(int); + +static void registerSignalHandler(SignalHandler handler) +{ + if (!handler) + handler = SIG_DFL; + + sigset_t mask; + sigemptyset(&mask); + +#ifdef SIGSEGV + signal(SIGSEGV, handler); + sigaddset(&mask, SIGSEGV); +#endif +#ifdef SIGFPE + signal(SIGFPE, handler); + sigaddset(&mask, SIGFPE); +#endif +#ifdef SIGILL + signal(SIGILL, handler); + sigaddset(&mask, SIGILL); +#endif +#ifdef SIGABRT + signal(SIGABRT, handler); + sigaddset(&mask, SIGABRT); +#endif + + sigprocmask(SIG_UNBLOCK, &mask, 0); +} + +void signalHander(int) +{ + static bool inHandler=false; + + if(!inHandler) + { + inHandler=true; + theFolders[isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER].saveDisabled(); + inHandler=false; + } +} + +FontInst::FontInst() +{ + isSystem=Misc::root(); + registerTypes(); + + new FontinstAdaptor(this); + QDBusConnection bus=QDBusConnection::sessionBus(); + + KFI_DBUG << "Connecting to session bus"; + if(!bus.registerService(OrgKdeFontinstInterface::staticInterfaceName())) + { + KFI_DBUG << "Failed to register service!"; + ::exit(-1); + } + if(!bus.registerObject(FONTINST_PATH, this)) + { + KFI_DBUG << "Failed to register object!"; + ::exit(-1); + } + + registerSignalHandler(signalHander); + itsConnectionsTimer=new QTimer(this); + itsFontListTimer=new QTimer(this); + connect(itsConnectionsTimer, SIGNAL(timeout()), SLOT(connectionsTimeout())); + connect(itsFontListTimer, SIGNAL(timeout()), SLOT(fontListTimeout())); + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); + + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + theFolders[i].init(FOLDER_SYS==i, isSystem); + + updateFontList(false); +} + +FontInst::~FontInst() +{ + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + theFolders[i].saveDisabled(); +} + +void FontInst::list(int folders, int pid) +{ + KFI_DBUG << folders << pid; + + itsConnections.insert(pid); + updateFontList(false); + QList fonts; + + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + if(0==folders || folders&(1<start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); + emit fontList(pid, fonts); +} + +void FontInst::stat(const QString &name, int folders, int pid) +{ + KFI_DBUG << name << folders << pid; + + bool checkSystem=0==folders || folders&SYS_MASK || isSystem, + checkUser=0==folders || (folders&USR_MASK && !isSystem); + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + + itsConnections.insert(pid); + if( (checkSystem && findFont(name, FOLDER_SYS, fam, st)) || + (checkUser && findFont(name, FOLDER_USER, fam, st, !checkSystem)) ) + { + Family rv((*fam).name()); + rv.add(*st); + KFI_DBUG << "Found font, emit details..."; + emit fontStat(pid, rv); + } + else + { + KFI_DBUG << "Font not found, emit empty details..."; + emit fontStat(pid, Family(name)); + } +} + +void FontInst::install(const QString &file, bool createAfm, bool toSystem, int pid, bool checkConfig) +{ + KFI_DBUG << file << createAfm << toSystem << pid << checkConfig; + + itsConnections.insert(pid); + + if(checkConfig) + updateFontList(); + + EFolder folder=isSystem || toSystem ? FOLDER_SYS : FOLDER_USER; + Family font; + Utils::EFileType type=Utils::check(file, font); + + int result=Utils::FILE_BITMAP==type && !FC::bitmapsEnabled() + ? STATUS_BITMAPS_DISABLED + : Utils::FILE_INVALID==type + ? STATUS_NOT_FONT_FILE + : STATUS_OK; + + if(STATUS_OK==result) + { + if(Utils::FILE_AFM!=type && Utils::FILE_PFM!=type) + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT) && STATUS_OK==result; ++i) + if(theFolders[i].contains(font.name(), (*font.styles().begin()).value())) + result=STATUS_ALREADY_INSTALLED; + + if(STATUS_OK==result) + { + QString name(Misc::modifyName(Misc::getFile(file))), + destFolder(Misc::getDestFolder(theFolders[folder].location(), name)); + + result=Utils::FILE_AFM!=type && Utils::FILE_PFM!=type && Misc::fExists(destFolder+name) ? (int)KIO::ERR_FILE_ALREADY_EXIST : (int)STATUS_OK; + if(STATUS_OK==result) + { + if(toSystem && !isSystem) + { + KFI_DBUG << "Send request to system helper" << file << destFolder << name; + QVariantMap args; + args["method"] = "install"; + args["file"] = file; + args["name"] = name; + args["destFolder"] = destFolder; + args["createAfm"] = createAfm; + args["type"] = (int)type; + result=performAction(args); + } + else + { + if(!Misc::dExists(destFolder)) + result=Misc::createDir(destFolder) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; + + if(STATUS_OK==result) + result=QFile::copy(file, destFolder+name) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; + + if(STATUS_OK==result) + { + Misc::setFilePerms(QFile::encodeName(destFolder+name)); + if((Utils::FILE_SCALABLE==type || Utils::FILE_PFM==type) && createAfm) + Utils::createAfm(destFolder+name, type); + } + } + + if(STATUS_OK==result && Utils::FILE_AFM!=type && Utils::FILE_PFM!=type) + { + StyleCont::ConstIterator st(font.styles().begin()); + FileCont::ConstIterator f((*st).files().begin()); + File df(destFolder+name, (*f).foundry(), (*f).index()); + + (*st).clearFiles(); + (*st).add(df); + theFolders[folder].add(font); + theFolders[folder].addModifiedDir(destFolder); + emit fontsAdded(Families(font, FOLDER_SYS==folder)); + } + } + } + } + + emit status(pid, result); + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); +} + +void FontInst::uninstall(const QString &family, quint32 style, bool fromSystem, int pid, bool checkConfig) +{ + KFI_DBUG << family << style << fromSystem << pid << checkConfig; + + itsConnections.insert(pid); + + if(checkConfig) + updateFontList(); + + EFolder folder=isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER; + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; + + if(STATUS_OK==result) + { + Family del((*fam).name()); + Style s((*st).value(), (*st).scalable(), (*st).writingSystems()); + FileCont files((*st).files()); + FileCont::ConstIterator it(files.begin()), + end(files.end()); + + if(fromSystem && !isSystem) + { + QStringList fileList; + bool wasDisabled(false); + + for(; it!=end; ++it) + { + fileList.append((*it).path()); + theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*it).path())); + if(!wasDisabled && Misc::isHidden(Misc::getFile((*it).path()))) + wasDisabled=true; + } + QVariantMap args; + args["method"] = "uninstall"; + args["files"] = fileList; + result=performAction(args); + + if(STATUS_OK==result) + { + FileCont empty; + s.setFiles(files); + (*st).setFiles(empty); + if(wasDisabled) + theFolders[FOLDER_SYS].setDisabledDirty(); + } + } + else + { + for(; it!=end; ++it) + if(!Misc::fExists((*it).path()) || QFile::remove((*it).path())) + { + // Also remove any AFM or PFM files... + QStringList other; + Misc::getAssociatedFiles((*it).path(), other); + QStringList::ConstIterator oit(other.constBegin()), + oend(other.constEnd()); + for(; oit!=oend; ++oit) + QFile::remove(*oit); + + theFolders[folder].addModifiedDir(Misc::getDir((*it).path())); + (*st).remove(*it); + s.add(*it); + if(!theFolders[folder].disabledDirty() && Misc::isHidden(Misc::getFile((*it).path()))) + theFolders[folder].setDisabledDirty(); + } + } + + if(STATUS_OK==result) + { + if((*st).files().isEmpty()) + { + (*fam).remove(*st); + if((*fam).styles().isEmpty()) + theFolders[folder].removeFont(*fam); + } + else + result=STATUS_PARTIAL_DELETE; + del.add(s); + } + emit fontsRemoved(Families(del, FOLDER_SYS==folder)); + + } + KFI_DBUG << "status" << result; + emit status(pid, result); + + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); +} + +void FontInst::uninstall(const QString &name, bool fromSystem, int pid, bool checkConfig) +{ + KFI_DBUG << name << fromSystem << pid << checkConfig; + + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + if(findFont(name, fromSystem || isSystem ? FOLDER_SYS : FOLDER_USER, fam, st)) + uninstall((*fam).name(), (*st).value(), fromSystem, pid, checkConfig); + else + emit status(pid, KIO::ERR_DOES_NOT_EXIST); +} + +void FontInst::move(const QString &family, quint32 style, bool toSystem, int pid, bool checkConfig) +{ + KFI_DBUG << family << style << toSystem << pid << checkConfig; + + itsConnections.insert(pid); + if(checkConfig) + updateFontList(); + + if(isSystem) + emit status(pid, KIO::ERR_UNSUPPORTED_ACTION); + else + { + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + EFolder from=toSystem ? FOLDER_USER : FOLDER_SYS, + to=toSystem ? FOLDER_SYS : FOLDER_USER; + + if(findFont(family, style, from, fam, st)) + { + FileCont::ConstIterator it((*st).files().begin()), + end((*st).files().end()); + QStringList files; + + for(; it!=end; ++it) + { + files.append((*it).path()); + theFolders[from].addModifiedDir(Misc::getDir((*it).path())); + // Actual 'to' folder does not really matter, as we only want to call fc-cache + // ...actual folders only matter for xreating fonts.dir, etc, and we wont be doing this... + theFolders[to].addModifiedDir(theFolders[to].location()); + } + + QVariantMap args; + args["method"] = "move"; + args["files"] = files; + args["toSystem"] = toSystem; + args["dest"] = theFolders[to].location(); + args["uid"] = getuid(); + args["gid"] = getgid(); + int result=performAction(args); + + if(STATUS_OK==result) + updateFontList(); + emit status(pid, result); + } + else + { + KFI_DBUG << "does not exist"; + emit status(pid, KIO::ERR_DOES_NOT_EXIST); + } + } + + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); +} + +static bool renameFontFile(const QString &from, const QString &to, int uid=-1, int gid=-1) +{ + QByteArray src(QFile::encodeName(from)), + dest(QFile::encodeName(to)); + + if(KDE_rename(src.data(), dest.data())) + return false; + + Misc::setFilePerms(dest); + if(-1!=uid && -1!=gid) + ::chown(dest.data(), uid, gid); + return true; +} + +void FontInst::enable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) +{ + KFI_DBUG << family << style << inSystem << pid << checkConfig; + toggle(true, family, style, inSystem, pid, checkConfig); +} + +void FontInst::disable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) +{ + KFI_DBUG << family << style << inSystem << pid << checkConfig; + toggle(false, family, style, inSystem, pid, checkConfig); +} + +void FontInst::removeFile(const QString &family, quint32 style, const QString &file, bool fromSystem, int pid, + bool checkConfig) +{ + KFI_DBUG << family << style << file << fromSystem << pid << checkConfig; + + itsConnections.insert(pid); + + if(checkConfig) + updateFontList(); + + // First find the family/style + EFolder folder=isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER; + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; + + if(STATUS_OK==result) + { + // Family/style found - now check that the requested file is *within* the same folder as one + // of the files linked to this font... + FileCont files((*st).files()); + FileCont::ConstIterator it(files.begin()), + end(files.end()); + QString dir(Misc::getDir(file)); + + result=KIO::ERR_DOES_NOT_EXIST; + for(; it!=end && STATUS_OK!=result; ++it) + if(Misc::getDir((*it).path())==dir) + result=STATUS_OK; + + if(STATUS_OK==result) + { + // OK, found folder - so can now proceed to delete the file... + if(fromSystem && !isSystem) + { + QVariantMap args; + args["method"] = "removeFile"; + args["file"] = file; + result=performAction(args); + } + else + { + result=Misc::fExists(file) + ? QFile::remove(file) + ? (int)STATUS_OK + : (int)KIO::ERR_WRITE_ACCESS_DENIED + : (int)KIO::ERR_DOES_NOT_EXIST; + } + + if(STATUS_OK==result) + theFolders[folder].addModifiedDir(dir); + } + } + + emit status(pid, result); +} + +void FontInst::reconfigure(int pid, bool force) +{ + KFI_DBUG << pid << force; + bool sysModified(theFolders[FOLDER_SYS].isModified()); + + saveDisabled(); + + KFI_DBUG << theFolders[FOLDER_USER].isModified() << sysModified; + if(!isSystem && (force || theFolders[FOLDER_USER].isModified())) + theFolders[FOLDER_USER].configure(force); + + if(sysModified) + { + if(isSystem) + { + theFolders[FOLDER_SYS].configure(); + } + else + { + QVariantMap args; + args["method"] = "reconfigure"; + performAction(args); + theFolders[FOLDER_SYS].clearModified(); + } + } + + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); + + updateFontList(); + emit status(pid, isSystem ? constSystemReconfigured : STATUS_OK); +} + +QString FontInst::folderName(bool sys) +{ + return theFolders[sys || isSystem ? FOLDER_SYS : FOLDER_USER].location(); +} + +void FontInst::saveDisabled() +{ + if(isSystem) + theFolders[FOLDER_SYS].saveDisabled(); + else + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + if(FOLDER_SYS==i && !isSystem) + { + if(theFolders[i].disabledDirty()) + { + QVariantMap args; + args["method"] = "saveDisabled"; + performAction(args); + theFolders[i].saveDisabled(); + } + } + else + theFolders[i].saveDisabled(); +} + +void FontInst::connectionsTimeout() +{ + bool canExit(true); + + KFI_DBUG << "exiting"; + checkConnections(); + + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + { + if(theFolders[i].disabledDirty()) + canExit=false; + theFolders[i].saveDisabled(); + } + + if(0==itsConnections.count()) + { + if(canExit) + qApp->exit(0); + else // Try again later... + itsConnectionsTimer->start(constConnectionsTimeout); + } +} + +void FontInst::fontListTimeout() +{ + updateFontList(true); + itsFontListTimer->start(constFontListTimeout); +} + +void FontInst::updateFontList(bool emitChanges) +{ + // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-( + FcBool fcModified=!FcConfigUptoDate(0); + + if(fcModified || + theFolders[FOLDER_SYS].fonts().isEmpty() || + (!isSystem && theFolders[FOLDER_USER].fonts().isEmpty()) || + theFolders[FOLDER_SYS].disabledDirty() || + (!isSystem && theFolders[FOLDER_USER].disabledDirty())) + { + KFI_DBUG << "Need to refresh font lists"; + if(fcModified) + { + KFI_DBUG << "Re-init FC"; + if(!FcInitReinitialize()) + KFI_DBUG << "Re-init failed????"; + } + + Folder::Flat old[FOLDER_COUNT]; + + if(emitChanges) + { + KFI_DBUG << "Flatten existing font lists"; + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + old[i]=theFolders[i].flatten(); + } + + saveDisabled(); + + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + theFolders[i].clearFonts(); + + KFI_DBUG << "update list of fonts"; + + FcPattern *pat = FcPatternCreate(); + FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_FAMILYLANG, + FC_WEIGHT, FC_LANG, FC_CHARSET, FC_SCALABLE, +#ifndef KFI_FC_NO_WIDTHS + FC_WIDTH, +#endif + FC_SLANT, FC_INDEX, FC_FOUNDRY, (void*)0); + + FcFontSet *list=FcFontList(0, pat, os); + + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + + theFolders[FOLDER_SYS].loadDisabled(); + if(!isSystem) + theFolders[FOLDER_USER].loadDisabled(); + + if(list) + { + QString home(Misc::dirSyntax(QDir::homePath())); + + for (int i = 0; i < list->nfont; i++) + { + QString fileName(Misc::fileSyntax(FC::getFcString(list->fonts[i], FC_FILE))); + + if(!fileName.isEmpty() && Misc::fExists(fileName)) // && 0!=fileName.indexOf(constDefomaLocation)) + { + QString family, + foundry; + quint32 styleVal; + int index; + qulonglong writingSystems(WritingSystems::instance()->get(list->fonts[i])); + FcBool scalable=FcFalse; + + if(FcResultMatch!=FcPatternGetBool(list->fonts[i], FC_SCALABLE, 0, &scalable)) + scalable=FcFalse; + + FC::getDetails(list->fonts[i], family, styleVal, index, foundry); + FamilyCont::ConstIterator fam=theFolders[isSystem || 0!=fileName.indexOf(home) + ? FOLDER_SYS : FOLDER_USER].addFont(Family(family)); + StyleCont::ConstIterator style=(*fam).add(Style(styleVal)); + FileCont::ConstIterator file=(*style).add(File(fileName, foundry, index)); + + (*style).setWritingSystems((*style).writingSystems()|writingSystems); + if(scalable) + (*style).setScalable(); + } + } + + FcFontSetDestroy(list); + } + + if(emitChanges) + { + KFI_DBUG << "Look for differences"; + for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) + { + KFI_DBUG << "Flatten, and take copies..."; + Folder::Flat newList=theFolders[i].flatten(), + onlyNew=newList; + + KFI_DBUG << "Determine differences..."; + onlyNew.subtract(old[i]); + old[i].subtract(newList); + + KFI_DBUG << "Emit changes..."; + Families families=onlyNew.build(isSystem || i==FOLDER_SYS); + + if(!families.items.isEmpty()) + emit fontsAdded(families); + + families=old[i].build(isSystem || i==FOLDER_SYS); + if(!families.items.isEmpty()) + emit fontsRemoved(families); + } + } + KFI_DBUG << "updated list of fonts"; + } +} + +void FontInst::toggle(bool enable, const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) +{ + KFI_DBUG; + itsConnections.insert(pid); + + if(checkConfig) + updateFontList(); + + EFolder folder=isSystem || inSystem ? FOLDER_SYS : FOLDER_USER; + FamilyCont::ConstIterator fam; + StyleCont::ConstIterator st; + int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; + + if(STATUS_OK==result) + { + FileCont files((*st).files()), + toggledFiles; + FileCont::ConstIterator it(files.begin()), + end(files.end()); + QHash movedFonts; + QHash movedAssoc; + QSet modifiedDirs; + + for(; it!=end && STATUS_OK==result; ++it) + { + QString to=Misc::getDir((*it).path())+ + QString(enable ? Misc::unhide(Misc::getFile((*it).path())) + : Misc::hide(Misc::getFile((*it).path()))); + if(to!=(*it).path()) + { + KFI_DBUG << "MOVE:" << (*it).path() << " to " << to; + // If the font is a system font, and we're not root, then just go through the actions here - so + // that we can build the list of changes that would happen... + if((inSystem && !isSystem) || renameFontFile((*it).path(), to)) + { + modifiedDirs.insert(Misc::getDir(enable ? to : (*it).path())); + toggledFiles.insert(File(to, (*it).foundry(), (*it).index())); + // Now try to move an associated AFM or PFM files... + QStringList assoc; + + movedFonts[*it]=to; + Misc::getAssociatedFiles((*it).path(), assoc); + + QStringList::ConstIterator ait(assoc.constBegin()), + aend(assoc.constEnd()); + + for(; ait!=aend && STATUS_OK==result; ++ait) + { + to=Misc::getDir(*ait)+ + QString(enable ? Misc::unhide(Misc::getFile(*ait)) + : Misc::hide(Misc::getFile(*ait))); + + if(to!=*ait) + { + if((inSystem && !isSystem) || renameFontFile(*ait, to)) + { + movedAssoc[*ait]=to; + } + else + { + result=KIO::ERR_WRITE_ACCESS_DENIED; + } + } + } + } + else + { + result=KIO::ERR_WRITE_ACCESS_DENIED; + } + } + } + + if(inSystem && !isSystem) + { + Family toggleFam((*fam).name()); + + toggleFam.add(*st); + QVariantMap args; + args["method"] = "toggle"; + QString xml; + QTextStream str(&xml); + toggleFam.toXml(false, str); + args["xml"] = xml; + args["enable"] = enable; + result=performAction(args); + } + + if(STATUS_OK==result) + { + Family addFam((*fam).name()), + delFam((*fam).name()); + Style addStyle((*st).value(), (*st).scalable(), (*st).writingSystems()), + delStyle((*st).value(), (*st).scalable(), (*st).writingSystems()); + + addStyle.setFiles(toggledFiles); + addFam.add(addStyle); + delStyle.setFiles(files); + delFam.add(delStyle); + (*st).setFiles(toggledFiles); + + theFolders[folder].addModifiedDirs(modifiedDirs); + emit fontsAdded(Families(addFam, FOLDER_SYS==folder)); + emit fontsRemoved(Families(delFam, FOLDER_SYS==folder)); + + theFolders[folder].setDisabledDirty(); + } + else // un-move fonts! + { + QHash::ConstIterator fit(movedFonts.constBegin()), + fend(movedFonts.constEnd()); + QHash::ConstIterator ait(movedAssoc.constBegin()), + aend(movedAssoc.constEnd()); + + for(; fit!=fend; ++fit) + renameFontFile(fit.value(), fit.key().path()); + for(; ait!=aend; ++ait) + renameFontFile(ait.value(), ait.key()); + } + } + emit status(pid, result); + + itsConnectionsTimer->start(constConnectionsTimeout); + itsFontListTimer->start(constFontListTimeout); +} + +void FontInst::addModifedSysFolders(const Family &family) +{ + StyleCont::ConstIterator style(family.styles().begin()), + styleEnd(family.styles().end()); + + for(; style!=styleEnd; ++style) + { + FileCont::ConstIterator file((*style).files().begin()), + fileEnd((*style).files().end()); + + for(; file!=fileEnd; ++file) + theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*file).path())); + } +} + +void FontInst::checkConnections() +{ + KFI_DBUG; + QSet::ConstIterator it(itsConnections.begin()), + end(itsConnections.end()); + QSet remove; + + for(; it!=end; ++it) + if(0!=kill(*it, 0)) + remove.insert(*it); + itsConnections.subtract(remove); +} + +bool FontInst::findFontReal(const QString &family, const QString &style, EFolder folder, + FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st) +{ + KFI_DBUG; + Family f(family); + fam=theFolders[folder].fonts().find(f); + if(theFolders[folder].fonts().end()==fam) + return false; + + StyleCont::ConstIterator end((*fam).styles().end()); + for(st=(*fam).styles().begin(); st!=end; ++st) + if(FC::createStyleName((*st).value())==style) + return true; + + return false; +} + +bool FontInst::findFont(const QString &font, EFolder folder, + FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, + bool updateList) +{ + KFI_DBUG; + QString family, + style; + + decompose(font, family, style); + + if(!findFontReal(family, style, folder, fam, st)) + { + if(updateList) + { + // Not found, so refresh font list and try again... + updateFontList(); + return findFontReal(family, style, folder, fam, st); + } + else + { + return false; + } + } + + return true; +} + +bool FontInst::findFontReal(const QString &family, quint32 style, EFolder folder, + FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st) +{ + KFI_DBUG; + fam=theFolders[folder].fonts().find(Family(family)); + + if(theFolders[folder].fonts().end()==fam) + return false; + else + { + st=(*fam).styles().find(style); + + return (*fam).styles().end()!=st; + } +} + +bool FontInst::findFont(const QString &family, quint32 style, EFolder folder, + FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, + bool updateList) +{ + KFI_DBUG; + if(!findFontReal(family, style, folder, fam, st)) + { + if(updateList) + { + // Not found, so refresh font list and try again... + updateFontList(); + return findFontReal(family, style, folder, fam, st); + } + else + { + return false; + } + } + return true; +} + +int FontInst::performAction(const QVariantMap &args) +{ + KAuth::Action action("org.kde.fontinst.manage"); + + action.setHelperID("org.kde.fontinst"); + action.setArguments(args); + KFI_DBUG << "Call " << args["method"].toString() << " on helper"; + itsFontListTimer->stop(); + itsConnectionsTimer->stop(); + KAuth::ActionReply reply = action.execute(); + + switch(reply.type()) + { + case KAuth::ActionReply::KAuthError: + KFI_DBUG << "KAuth failed - error code:" << reply.errorCode(); + return KIO::ERR_COULD_NOT_AUTHENTICATE; + case KAuth::ActionReply::HelperError: + KFI_DBUG << "Helper failed - error code:" << reply.errorCode(); + return (int)reply.errorCode(); + } + + KFI_DBUG << "Success!"; + return STATUS_OK; +} + +} diff --git a/kcms/kfontinst/dbus/FontInst.h b/kcms/kfontinst/dbus/FontInst.h new file mode 100644 index 0000000000..1365efda5c --- /dev/null +++ b/kcms/kfontinst/dbus/FontInst.h @@ -0,0 +1,162 @@ +#ifndef FONTINST_H +#define FONTINST_H + +/* + * KFontInst - KDE Font Installer + * + * Copyright 2003-2009 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include "Family.h" +#include "Folder.h" +#include "FontinstIface.h" +#include "kfontinst_export.h" + +#define FONTINST_PATH "/FontInst" + +class QTimer; + +namespace KFI +{ + +class KFONTINST_EXPORT FontInst : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.fontinst") + + public: + + enum EStatus + { + STATUS_OK=0, + STATUS_SERVICE_DIED = KJob::UserDefinedError+500, + STATUS_BITMAPS_DISABLED, + STATUS_ALREADY_INSTALLED, + STATUS_NOT_FONT_FILE, + STATUS_PARTIAL_DELETE, + STATUS_NO_SYS_CONNECTION + }; + + enum EFolder + { + FOLDER_SYS, + FOLDER_USER, + + FOLDER_COUNT + }; + + enum + { + SYS_MASK=0x01, + USR_MASK=0x02 + }; + + static void registerTypes() + { + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType